Feedback
bss@qiwi.com
NAV

Pay-On-Delivery Payments

Last update: 2018-07-03

This API is an extension of the Pull Payments API to be implemented by merchant (an online store or platform / marketplace) that sells products) to support QIWI Pull Payments with postponed payments. Postponed payment allows users to buy items and make payment later on delivery via QIWI.

Pay-On-Delivery REST API

Last update: 2018-06-27

Invoicing Operation Flow

Pay-On-Delivery API Invoicing

Authorization

Pay-On-Delivery REST API requests are authorized through HTTP Basic-authorization with API ID and API password. Header is Authorization string and its value is Basic Base64(API_ID:API_PASSWORD).

user@server:~$ curl "server_URL"
  --header "Authorization: Basic MjMyNDQxMjM6NDUzRmRnZDQ0Mw=="
Parameter Description Type Required
API_ID Provider API identifier for authorization Integer +
API_PASSWORD API password for authorization String +
Shop ID Numeric identifier of the provider’s service Integer +

Issuing Invoice

Creates new invoice to the specified phone number (wallet ID in QIWI Wallet).

Request → PUT

user@server:~$ curl "https://w.qiwi.com/api/v2/prv/373712/bills/BILL_1"
  -X PUT  --header "Accept: text/json" --header "Authorization: Basic ***"
  -d "user=tel%3A%2B79161234567&amount=10.00&ccy=RUB&comment=test&lifetime=2016-09-25T15:00:00&pay_source=cod&extras%5Border_id%5D=abcde12345"
Parameter Description Type Required
user The QIWI Wallet user’s ID, to whom the invoice is issued. It is the user’s phone number with tel: prefix String(20) Y
amount The invoice amount. The number is rounded down with two decimal places Number(6.2) Y
ccy Invoice currency identifier (Alpha-3 ISO 4217 code). Depends on currencies allowed for the merchant. The following values are supported: RUB, EUR, USD, KZT String(3) Y
comment Comment to the invoice String(255) Y
lifetime Date and time up to which the invoice is available for payment. If the invoice is not paid by this date it will become void and will be assigned a final status.
Important! Invoice will be automatically expired when 45 days is passed after the invoicing date
YYYY-MM-DDThh:mm:ss URL-encoded Y
pay_source If the value is mobile the user’s MNO balance will be used as a funding source. If the value is qw, any other funding source is used available in QIWI Wallet system for the user. If parameter isn’t present, value qw is assumed cod string Y
prv_name Merchant’s name String(100) N
extras[order_id] Extra-parameter of the request - Order ID in the merchant’s information system for the invoice. ^.{1,255}$ String Y

Response ←


HTTP/1.1 200 OK
Content-Type: text/json
{
  "response": {
     "result_code": 0,
     "bill": {
        "bill_id": "BILL_1",
        "amount": "10.00",
        "ccy": "RUB",
        "status": "waiting",
        "error": 0,
        "user": "tel:+79031234567",
        "comment": "test"
     }
  }
}
HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
 "response": {
  "result_code": 150,
  "description": "Authorization failed"
  }
}

HTTP/1.1 200 OK
Content-Type: text/xml
<response>
   <result_code>0</result_code>
   <bill>
    <bill_id>BILL-1</bill_id>
    <amount>10.00</amount>
    <originAmount>10.00</originAmount>
    <ccy>RUB</ccy>
    <originCcy>RUB</originCcy>
    <status>rejected<status>
    <error>0</error>
    <user>tel:+79161234567</user>
    <comment>test</comment>
   </bill>
</response>
HTTP/1.1 401 Unauthorized
Content-Type: text/xml
<response>
   <result_code>341</result_code>
   <description>Authorization is failed</description>
</response>

Response format depends on Accept header in the request:

Parameter Type Description
result_code Integer Error code
description String Error description. Returned when result_code is non-zero.
bill Object Bill data. Returned when result_code is zero (successful operation). Parameters:
bill.bill_id String Unique invoice identifier generated by the merchant
bill.amount String The invoice amount. The number is rounded down with two decimal places.
bill.ccy String Currency identifier of the invoice (Alpha-3 ISO 4217 code)
bill.status String Current invoice status
bill.error Integer Always 0, means successful operation
bill.user String The QIWI Wallet user’s ID, to whom the invoice is issued. It is the user’s phone number with tel: prefix.
bill.comment String Comment to the invoice

Checking Invoice Status

Get payment status of the invoice.

user@server:~$ curl "https://w.qiwi.com/api/v2/prv/373712/bills/sdf23452435"
  --header "Authorization: Basic ***" --header "Accept: text/json"

Request → GET

Response ←


HTTP/1.1 200 OK
Content-Type: text/json
{
  "response": {
     "result_code": 0,
     "bill": {
        "bill_id": "BILL_1",
        "amount": "10.00",
        "originAmount": "10.00",
        "ccy": "RUB",
        "originCcy": "RUB",
        "status": "waiting",
        "error": 0,
        "user": "tel:+79031234567",
        "comment": "test"
     }
  }
}
HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
 "response": {
  "result_code": 150,
  "description": "Authorization failed"
  }
}

HTTP/1.1 200 OK
Content-Type: text/xml
<response>
   <result_code>0</result_code>
   <bill>
    <bill_id>BILL_1</bill_id>
    <amount>10.00</amount>
    <originAmount>10.00</originAmount>
    <ccy>RUB</ccy>
    <originCcy>RUB</originCcy>
    <status>rejected<status>
    <error>0</error>
    <user>tel:+79161234567</user>
    <comment>test</comment>
   </bill>
</response>
HTTP/1.1 401 Unauthorized
Content-Type: text/xml
<response>
   <result_code>341</result_code>
   <description>Authorization is failed</description>
</response>

Response format depends on Accept header in the request:

Parameter Type Description
result_code Integer Error code
description String Error description. Returned when result_code is non-zero.
bill Object Bill data. Returned when result_code is zero (successful operation). Parameters:
bill.bill_id String Unique invoice identifier generated by the merchant
bill.amount String The invoice amount. The number is rounded down with two decimal places.
bill.originAmount String The amount taken from the balance when the invoice get paid (see originCcy parameter). The number is rounded down with two decimal places. Returns for invoices when the user initiates payment.
bill.ccy String Currency identifier of the invoice (Alpha-3 ISO 4217 code)
bill.originCcy String Currency identifier of the balance from which the invoice is paid (Alpha-3 ISO 4217 code). Returns for invoices when the user initiates payment.
bill.status String Current invoice status
bill.error Integer Always 0, means successful operation
bill.user String The QIWI Wallet user’s ID, to whom the invoice is issued. It is the user’s phone number with tel: prefix.
bill.comment String Comment to the invoice

Cancelling Unpaid Invoice

Сancels unpaid invoice provided that its lifetime has not expired yet.

user@server:~$ curl -X PATCH
  --header "Authorization: Basic ***"  --header "Accept: text/json"
  "https://w.qiwi.com/api/v2/prv/373712/bills/BILL_1"
  -d "status=rejected"

Request → PATCH

Response ←


HTTP/1.1 200 OK
Content-Type: text/json
{
  "response": {
     "result_code": 0,
     "bill": {
        "bill_id": "BILL_1",
        "amount": "10.00",
        "ccy": "RUB",
        "status": "rejected",
        "error": 0,
        "user": "tel:+79031234567",
        "comment": "test"
     }
  }
}
HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
 "response": {
  "result_code": 150,
  "description": "Authorization failed"
  }
}

HTTP/1.1 200 OK
Content-Type: text/xml
<response>
   <result_code>0</result_code>
   <bill>
    <bill_id>BILL_1</bill_id>
    <amount>10.00</amount>
    <ccy>RUB</ccy>
    <status>rejected<status>
    <error>0</error>
    <user>tel:+79161234567</user>
    <comment>test</comment>
   </bill>
</response>
HTTP/1.1 401 Unauthorized
Content-Type: text/xml
<response>
   <result_code>341</result_code>
   <description>Authorization is failed</description>
</response>

Response format depends on Accept header in the request:

Parameter Type Description
result_code Integer Error code
description String Error description. Returned when result_code is non-zero.
bill Object Invoice data. Returned when result_code is zero (successful operation). Parameters:
bill.bill_id String Unique invoice identifier generated by the merchant
bill.amount String The invoice amount. The number is rounded down with two decimal places.
bill.ccy String Currency identifier of the invoice (Alpha-3 ISO 4217 code)
bill.status String Rejected invoice status
bill.error Integer Always 0, means successful operation
bill.user String The QIWI Wallet user’s ID, to whom the invoice is issued. It is the user’s phone number with tel: prefix.
bill.comment String Comment to the invoice

Pay-On-Delivery Purchase Cancelling

Merchant can process a full refund of a purchase cancelled by user before delivering. Refund processes to QIWI using the PUT-request described below. This request creates a reversed transaction for the initial one.

Operation Flow

Purchase Cancelling

Request → PUT

user@server:~$ curl "https://w.qiwi.com/api/v2/prv/373712/bills/BILL_1/refund/REF1"
  -v -w "%{http_code}"
  -X PUT  --header "Accept: text/json"
  --header "Authorization: Basic ***"
  --header "Content-type: application/x-www-form-urlencoded; charset=utf-8"
  -d "amount=5.0"

Response ←


HTTP/1.1 200 OK
Content-Type: text/json
{
   "response": {
      "result_code": 0,
      "refund": {
         "refund_id": "REF1",
         "amount": "5.00",
         "status": "success",
         "error": 0
      }
   }
}
HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
 "response": {
  "result_code": 150,
  "description": "Authorization failed"
  }
}

HTTP/1.1 200 OK
Content-Type: text/xml
<response>
  <result_code>0</result_code>
  <refund>
   <refund_id>REF1</refund_id>
   <amount>5.0</amount>
   <status>success<status>
   <error>0</error>
  </refund>
</response>
HTTP/1.1 401 Unauthorized
Content-Type: text/xml
<response>
   <result_code>341</result_code>
   <description>Authorization is failed</description>
</response>
Parameter Type Description
result_code Integer Error code
description String Error description. Returned when result_code is non-zero.
refund Object Refund data. Returned when result_code is zero (successful operation). Parameters:
refund.refund_id String The purchase cancel operation identifier, unique number in a series of refunds processed for a particular invoice
refund.amount String The actual amount of the purchase cancel operation. The positive number that is rounded down with two decimal places.
refund.status String Current refund status
refund.error Integer Error code.
Important! When the amount of purchase cancel operation exceeds the initial invoice amount, error code 242 is returned.

Pay-On-Delivery Purchase Cancelling Status Verification

Merchant can verify status of the purchase cancel operation by this method. It returns current status of the purchase cancel operation.

Request → GET

user@server:~$ curl "https://w.qiwi.com/api/v2/prv/373712/bills/BILL_1/refund/REF1"
  -v -w "%{http_code}"
  --header "Accept: text/json" --header "Authorization: Basic ***"

Response ←


HTTP/1.1 200 OK
Content-Type: text/json
{
   "response": {
      "result_code": 0,
      "refund": {
         "refund_id": "REF1",
         "amount": "5.00",
         "status": "success",
         "error": 0
      }
   }
}
HTTP/1.1 401 Unauthorized
Content-Type: text/json
{
 "response": {
  "result_code": 150,
  "description": "Authorization failed"
  }
}

HTTP/1.1 200 OK
Content-Type: text/xml
<response>
  <result_code>0</result_code>
  <refund>
   <refund_id>REF1</refund_id>
   <amount>5.0</amount>
   <status>success<status>
   <error>0</error>
  </refund>
</response>

HTTP/1.1 401 Unauthorized
Content-Type: text/xml
<response>
   <result_code>341</result_code>
   <description>Authorization is failed</description>
</response>
Parameter Type Description
result_code Integer Error code
description String Error description. Returned when result_code is non-zero.
refund Object Refund data. Returned when result_code is zero (successful operation). Parameters:
refund.refund_id String The purchase cancel operation identifier
refund.amount String The actual amount of the purchase cancel operation. The positive number that is rounded down with two decimal places.
refund.status String Current purchase cancel operation status
refund.error Integer Error code. Important! When the amount of purchase cancel operation exceeds the initial invoice amount, error code 242 is returned.

Operation Statuses

Last update: 2018-06-27

Invoice Status

Status Description Final
waiting Invoice issued, pending payment N
paid Invoice has been paid Y
rejected Invoice has been rejected Y
unpaid Payment processing error. Invoice has not been paid Y
expired Invoice expired. Invoice has not been paid Y

Purchase Cancel Operation Status

Status Description Final
processing Payment refund is pending N
success Payment refund is successful Y
fail Payment refund is unsuccessful Y

Error Codes

Last update: 2018-06-27
Code Description Fatal
0 Success Unrelated
5 Incorrect data in the request parameters Y
13 Server is busy, try again later N
78 Operation is forbidden Y
150 Authorization error (e.g. invalid login/password) Y
152 Protocol is not enabled or protocol is disabled N
155 This merchant’s identifier (API ID) is blocked Y
210 Invoice not found Y
215 Invoice with this bill_id already exists Y
241 Invoice amount is less than allowed Y
242 Invoice amount is greater than allowed. Also returns to purchase cancel operation request when the amount of refund exceeds the initial invoice amount Y
298 User not registered Y
300 Technical error N
303 Wrong phone number Y
316 Authorization from the blocked merchant N
319 No rights for the operation N
339 IP-addresses blocked Y
341 Required parameter is incorrectly specified or absent in the request Y
700 Monthly limit on operations is exceeded Y
774 QIWI Wallet user account temporarily blocked Y
1001 Currency is not allowed for the merchant Y
1003 No convert rate for these currencies N
1019 Unable to determine wireless operator for MNO balance payment Y
1419 Invoice was already payed Y

Pay-On-Delivery Checkout Page

Last update: 2018-06-27

To use Pay-On-Delivery scenario, merchant redirects QIWI user to Pay-On-Delivery checkout page. To redirect, HTTP GET-request is used to the URL https://payondelivery.qiwi.com/.

QIWI Pay-On-Delivery Checkout page
GET /?shop_id=2042&transaction=Bill_1&order_id=abcde12345& phone=79161111111&sub_id=Max198353& success_url=http%3A%2F%2Fgoogle.com&fail_url=http%3A%2F%2Fwww.bing.com& sig=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 HTTP/1.1
Host: payondelivery.qiwi.com

Request → REDIRECT

Parameter Type & RegExp Description Required
shop_id Integer, ^[0-9]{1,64}$ Merchant’s ID in Pay-On-Delivery system, provided upon merchant’s integration. Y
transaction string, ^[_0-9a-zA-Z]{1,200}$ Payment ID generated by the merchant, corresponds to bill_id parameter used to create the invoice. Y
order_id string, ^.{1,255}$ Order ID in the merchant’s information system for the invoice. Y
phone string, ^[0-9]{10,11}$ The user’s phone number (without “+” symbol). Y
sub_id string, ^.{1,255}$ Unique user ID on merchant’s side. Y
sig string, ^[0-9a-z]{64}$ Signature of the request. It is calculated as SHA256 hash function for the string of concatenated parameter: phone+shop_id+ order_id + transaction + key, where values of corresponding parameters are taken from the GET-request and key is a secret string. Y
successUrl URL-encoded string, (?i)^(http|https):\/\/.+$ The URL to which the payer will be redirected when invoice is paid Y
failUrl URL-encoded string, (?i)^(http|https):\/\/.+$ The URL to which the payer will be redirected when invoice is not paid Y

Redirection to Merchant Site

After initiation of the payment, the user is automatically returned back to the merchant’s site:

GET /ok.php?order_id=660&bill_id=test_5958&amount=746.47&ccy=RUB& checksum=6fde5fdcefe98569496c079b2fcb8373f92ad6755e2e941497f758fdeb8f7af5 HTTP/1.1
Host: merchant-shop.com
<?php
// Example of checksum generation
$hash_params = [
'amount' => '746.00',
'bill_id' => 'test_5958',
'ccy' => 'RUB',
'key' => 'secret string',
'order_id' => '660',
];
ksort($hash_params);
$checksum = hash('sha256', implode($hash_params));
?>

The following parameters are sent in the redirect URL:

Name Type Description
order_id String Order ID in the merchant’s information system for the invoice
bill_id String Invoice ID generated by the merchant, corresponds to bill_id parameter used to create the invoice.
amount A positive number rounded down to 2 decimal places after the comma The invoice amount in rubles. The merchant has to send this amount to the logistic company as a sum of Pay-on-Delivery package
ccy String Currency identifier (Alpha-3 ISO 4217) of the invoice
checksum String Signature of the request. It is calculated as SHA256 hash function for the string of concatenated parameter sorted by their names: amount+bill_id+ ccy+key+order_id where key is a secret key for the signature.

Notifications

Last update: 2017-11-14

Notification is a POST-request (callback). The request’s body contains all relevant data of the invoice serialized as HTTP-request parameters and encoded by UTF-8 plus parameter command=bill.

Request → POST

Example

user@server:~$ curl "https://service.ru/qiwi-notify.php"
  -v -w "%{http_code}"
  -X POST --header "Accept: text/xml"
  --header "Content-Type: application/x-www-form-urlencoded; charset=utf-8"
  --Authorization: "Basic MjA0Mjp0ZXN0Cg=="
  -d "bill_id=BILL-1%26status=paid%26amount=1.00%26user=tel%3A%2B79031811737%26prv_name=TEST%26ccy=RUB%26comment=test%26command=bill"
Parameter Description Type Required
status Current invoice status String Y
amount The invoice amount. The number is rounded down with two decimal places Number(6.2) Y
user The QIWI Wallet user’s ID, to whom the invoice is issued. It is the user’s phone number with “tel:” prefix String Y
prv_name Merchant’s site name specified on ishop.qiwi.com in “Settings”->”Contract/project details”->”Short name” section String Y
ccy Invoice currency identifier (Alpha-3 ISO 4217 code) String(3) Y
comment Comment to the invoice String(255) Y
command Always bill by default String Y

Response ←

HTTP/1.1 200 OK
Content-Type: text/xml

<?xml version="1.0"?>
<result>
<result_code>0</result_code>
</result>

Response must be in XML format.

XML Tag Description
result Grouping tag. Describes notification processing result.
result_code Notification result code (positive integer). We recommend that the result codes returned by the merchant be in accordance with Notification codes table.

Authorization on Merchant’s Server

Merchant’s server should use basic-authorization or authorization by signature. Merchant may also use client SSL certificate verification (self-signed cerificates may be used as well). QIWI Wallet server certificate should be verified in HTTPS requests.

Basic authorization

POST /qiwi-notify.php HTTP/1.1
Accept: text/xml
Content-type: application/x-www-form-urlencoded
Authorization: Basic ***
Host: service.ru

command=bill&bill_id=BILL-1&status=paid&error=0&amount=1.00&user=tel%3A%2B79031811737&prv_name=Retail_Store&ccy=RUB&comment=test

The login is taken from Shop ID parameter. To obtain password, click on Change password button in Protocols details - REST-protocol section of QIWI partners web site.

Authorization by signature

POST /qiwi-notify.php HTTP/1.1
Accept: text/xml
Content-type: application/x-www-form-urlencoded
X-Api-Signature: J4WNfNZd***V5mv2w=
Host: service.ru

command=bill&bill_id=LocalTest17&status=paid&error=0&amount=0.01&user=tel%3A%2B78000005122&prv_name=Test&ccy=RUB&comment=Some+Descriptor
<?php

function hexToStr($hex){
    $string='';
    for ($i=0; $i < strlen($hex)-1; $i+=2){
        $string .= chr(hexdec($hex[$i].$hex[$i+1]));
    }
    return $string;
}

//Signature generation by key and string
function checkSign($key, $req){
    $sign_hash = hash_hmac("sha1", $req, $key);
    $sign_tr = hexToStr($sign_hash);
    $sign = base64_encode($sign_tr);
    return $sign;
}

//Sort POST-request parameters and return values
function getReqParams(){
    $reqparams = "";
    ksort($_POST);
    foreach ($_POST as $param => $valuep) {
        $reqparams = "$reqparams|$valuep";
    }
    return substr($reqparams,1);
}

//Take signature from the request
function getSign(){
    $HEADERS = getallheaders();
    foreach ($HEADERS as $header => $value) {
       if ($header == 'X-Api-Signature') {
            $SIGN_REQ = $value;
       }
    }
    return $SIGN_REQ;
}

// Sort parameters
$Request = getReqParams();
// Notification password
$NOTIFY_PWD = "***";
// Get sign
$reqres = checkSign($NOTIFY_PWD, $Request);

// Get sign from the request
$SIGN_REQ = getSign();

if ($reqres == $SIGN_REQ) {
    $error = 0;
}
else $error = 151;

//Response
header('Content-Type: text/xml');
$xmlres = <<<XML
<?xml version="1.0"?>
<result>
<result_code>$error</result_code>
</result>
XML;
echo $xmlres;
?>

The HTTP header X-Api-Signature with signature is added to the POST-request. Signature is calculated as HMAC algorithm with SHA1-hash function.

Signature verification algorithm is as follows:

  1. Prepare a string of all parameters values from the notification POST-request sorted in alphabetical order and separated by |:

    {parameter1}|{parameter2}|…

    where {parameter1} is the value of the notification parameter. All values should be treated as strings.

  2. Transform obtained string and password for the notification basic-authorization into bytes encoded in UTF-8.
  3. Apply HMAC-SHA1 function:

    hash = HMAС(SHA1, Notification_password_bytes, Invoice_parameters_bytes) Where:

    • Notification_password_bytes – secret key (bytecoded notification password);
    • Invoice_parameters_bytes – bytecoded POST-request body;
    • hash – hash-function result.
  4. Transform HMAC-hash value into bytes with UTF-8 and Base64-encode it.
  5. Compare X-Api-Signature header’s value with the result of step 4.

PHP Implementation Example

The given PHP example implements notification authorization by signature verification. Open the PHP tab on the right.

Notification Codes

Code Description
0 Success
5 The format of the request parameters is incorrect
13 Database connection error
150 Incorrect password
151 Signature authorization failed
300 Server connection error