QIWI
Feedback
bss@qiwi.com
NAV Navbar
shell PHP SDK Node.js SDK Java SDK .Net SDK Popup

P2P Invoices

Terms of Service

To enable p2p-transfer service for individuals on your site, you need a QIWI wallet with Basic or Professional identification status. If your wallet has Anonymous status, pass identification with any means suitable for you:

We recommend to get Professional status, as it applies higher value of limits for remains on your balance, payments and transfers amount per month, and maximum amount of a single operation. See details of limits in the documentation.

Take a look at frequently asked questions on the service, and get an understanding on how to avoid blocking your wallet from the documentation.

Get access to the service

  1. Authorize on p2p.qiwi.com.
  2. Make sure you have access to invoice creation – on the invoicing form fill in Amount field and click Create invoice button. Below the link to the invoice and Copy link button would be displayed.

invoice-test

Congratulations! You can start service integration.

How to start working with the service

  1. Create public and secret keys.
  2. Implement API operations or invoicing payment form call. Use SDK or various CMS solutions.
  3. To get notifications after invoice payment, implement its processing and activate sending notifications.
  4. Start accepting payments from cards and QIWI wallets.

Invoicing Operations Flow

sequenceDiagram participant user as User participant rec as Recipient participant p2p as QIWI P2P API opt Basic scenario user->>rec:Doing order activate user activate rec alt Using API rec->>p2p:Invoice issue p2p->>rec:Link to Payment form rec->>user:Redirect to Payment form
oplata.qiwi.com end alt Using Payment form rec->>user:Invoice issue on the form
oplata.qiwi.com end deactivate rec user->>p2p:Choose payment method / Paying for invoice deactivate user p2p->>p2p:Invoice payment end opt Additional opt Notifications (callback) activate p2p activate rec p2p->>rec:Invoice status notification rec->>p2p:Server response (OK) deactivate rec deactivate p2p end opt Invoice status check activate p2p activate rec rec->>p2p:Check invoice status p2p->>rec:Invoice data deactivate rec deactivate p2p end end
  1. User submits an order on the merchant’s website.

  2. Merchant redirects the user to Payment Form link. It automatically issues an invoice for the order. Or you may issue an invoice by API and redirect the user to the created Payment Form (link to the form is placed in the API response).

  3. The user chooses a way to pay for the invoice on the Payment Form and confirm payment. By default, the optimal payment method is showed.

  4. If merchant activates notification server, the merchant's service receives notification once the invoice is successfully paid by the user. Notifications are digitally signed, so you need to verify the signature on your notification server.

If required, via the API merchant can:

SDK and libraries

CMS solutions

Authorization methods

New keys issue for authorization has been stopped. Sorry for the inconvenience.

const QiwiBillPaymentsAPI = require('@qiwi/bill-payments-node-js-sdk');

const SECRET_KEY = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************';

const qiwiApi = new QiwiBillPaymentsAPI(SECRET_KEY);
--header "Authorization: Bearer MjMyNDQxMjM6NDUzRmRnZDQ0M*******"
<?php

const SECRET_KEY = 'eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************';

$billPayments = new Qiwi\Api\BillPayments(SECRET_KEY);

?>
String secretKey = "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************";
 BillPaymentClient client = BillPaymentClientFactory.createDefault(secretKey);
var secretKey = "eyJ2ZXJzaW9uIjoicmVzdF92MyIsImRhdGEiOnsibWVyY2hhbnRfaWQiOjUyNjgxMiwiYXBpX3VzZXJfaWQiOjcxNjI2MTk3LCJzZWNyZXQiOiJmZjBiZmJiM2UxYzc0MjY3YjIyZDIzOGYzMDBkNDhlYjhiNTnONPININONPN090MTg5Z**********************";

var client = BillPaymentClientFactory.createDefault(secretKey);

To authorize API requests, you need the keys:

To create a <SECRET_KEY> and <PUBLIC_KEY> pair of keys:

  1. Login to the personal account on https://p2p.qiwi.com/.
  2. Open API tab and click Create a key pair and configure button. When you create a key pair for the first time, click Configure button.

    p2p API Settings

  3. Enter a name for the key pair and click Create button.

  4. Save the secret key in a safe place — it won't be displayed in your personal account interface. But you can always copy public key from your personal account.

  5. Click on Next button. The key pair will be activated for use.

You can use the secret key for QIWI Wallet payment operations:

See details in the documentation.

Invoice Issue on Payment Form

It is the simplest way of integration. On opening Payment Form, client receives an invoice at the same time. The invoice data sends in URL explicitly. Client gets a Payment Form web page with multiple payment means.

When using this method, one cannot be sure that all invoices are issued by the merchant. API invoice creation mitigates this risk.

const publicKey = 'Fnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypc*******';

const params = {
    publicKey,
    amount: 42.24,
    billId: 'cc961e8d-d4d6-4f02-b737-2297e51fb48e',
    email: 'mail@example.com'
};

const link = qiwiApi.createPaymentForm(params);
curl https://oplata.qiwi.com/create?publicKey=Fnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypc*******&amount=100&email=mail@example.com&customFields[paySourcesFilter]=qw,card&lifetime=2020-12-01T0509
<?php

$publicKey = '2tbp1WQvsgQeziGY9vTLe9vDZNg7tmCymb4Lh6STQokqKrpCC6qrUUKEDZAJ7mvFnzr1yTebUiQaBLDnebLMMxL8nc6FF5zf******';
$params = [
  'publicKey' => $publicKey,
  'amount' => 200,
  'billId' => 'cc961e8d-d4d6-4f02-b737-2297e51fb48e'
];

/** @var \Qiwi\Api\BillPayments $billPayments */
$link = $billPayments->createPaymentForm($params);

echo $link;

?>
String publicKey = "2tbp1WQvsgQeziGY9vTLe9vDZNg7tmCymb4Lh6STQokqKrpCC6qrUUKEDZAJ7mvFnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypdXCbQJqHEJW5RJmKfj8nvgc";
 MoneyAmount amount = new MoneyAmount(
        BigDecimal.valueOf(499.90),
        Currency.getInstance("RUB")
);
String billId = UUID.randomUUID().toString();
String paymentUrl = client.createPaymentForm(new PaymentInfo(key, amount, billId, ""));
var publicKey = "2tbp1WQvsgQeziGY9vTLe9vDZNg7tmCymb4Lh6STQokqKrpCC6qrUUKEDZAJ7mvFnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypdXCbQJqHEJW5RJmKfj8nvgc";

var amount = new MoneyAmount
{
    ValueDecimal = 499.9m,
    CurrencyEnum = CurrencyEnum.Rub
};
var billId = Guid.NewGuid().ToString();

var paymentUrl = client.CreatePaymentForm(new PaymentInfo(key, amount, billId, ""));

GET →

const publicKey = 'Fnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypc*******';

const params = {
    publicKey,
    amount: 42.24,
    billId: 'cc961e8d-d4d6-4f02-b737-2297e51fb48e',
    email: 'mail@example.com'
};

const link = qiwiApi.createPaymentForm(params);
curl https://oplata.qiwi.com/create?publicKey=Fnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypc*******&amount=100&email=mail@example.com&customFields[paySourcesFilter]=qw,card&lifetime=2020-12-01T0509
Parameter Description Type
publicKey Required. Merchant's public key for the payment form obtained on p2p.qiwi.com String
billId Unique invoice identifier in merchant's system URL-Encoded String(200)
amount Amount of the invoice rounded down on two decimals Number(6.2)
phone Phone number of the client to which the invoice is issuing (international format) URL-Encoded String
email E-mail of the client where the invoice payment link will be sent URL-Encoded String
account Client identifier in merchant's system URL-Encoded String
comment Invoice commentary URL-Encoded String(255)
customFields[] Additional invoice data URL-encoded String(255)
customFields[paySourcesFilter] Allow only the specified payment methods for the client on Payment Form, if they are available to the merchant. Possible values:
qw - QIWI Wallet
card - card payment
URL-Encoded String(255)
customFields[themeCode] Personalization code for Payments Form String(255)
lifetime Expiration date of the pay form link (invoice payment's due date). If the invoice is not paid after that date, the invoice assigns EXPIRED final status and it becomes void.
Important! Invoice will be automatically expired when 45 days is passed after the invoicing date
URL-Encoded String
YYYY-MM-DDThhmm

P2P Invoices API. Creating an invoice

Only invoicing in ruble and tenge is supported.

API invoicing is the reliable method for integration. Parameters are sent by means of server2server requests with authorization.

Successful response contains payUrl URL to redirect client on Payment Form. See the documentation for additional URL parameters supported.

There is also more simple way to invoicing — with direct calling payment form.

Request → PUT

const billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

const fields = {
    amount: 1.00,
    currency: 'RUB',
    comment: 'Hello world',
    expirationDateTime: '2018-03-02T08:44:07'
};

qiwiApi.createBill( billId, fields ).then( data => {
    //do smth with data
});
curl --location --request PUT \
  'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e' \
  --header 'content-type: application/json' \
  --header 'accept: application/json' \
  --header 'Authorization: Bearer <SECRET_KEY>' \
  --data-raw '{
    "amount": {
     "currency": "RUB",
     "value": "1.00"
    },
    "comment": "Text comment",
    "expirationDateTime": "2025-12-10T09:02:00+03:00",
    "customer": {
     "phone": "78710009999",
     "email": "test@example.com",
     "account": "454678"
    },
    "customFields" : {
     "paySourcesFilter":"qw",
     "themeCode": "Yvan-YKaSh",
     "yourParam1": "64728940",
     "yourParam2": "order 678"
    }
  }'
<?php

$billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';
$fields = [
  'amount' => 1.00,
  'currency' => 'RUB',
  'comment' => 'test',
  'expirationDateTime' => '2018-03-02T08:44:07',
  'email' => 'mail@example.com',
  'account' => 'client4563'
];

/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->createBill($billId, $fields);

print_r($response);

?>
CreateBillInfo billInfo = new CreateBillInfo(
                UUID.randomUUID().toString(),
                new MoneyAmount(
                        BigDecimal.valueOf(199.90),
                        Currency.getInstance("RUB")
                ),
                "comment",
                ZonedDateTime.now().plusDays(45),
                new Customer(
                        "mail@example.com",
                        UUID.randomUUID().toString(),
                        "79123456789"
                ),
                ""
        );
BillResponse response = client.createBill(billInfo);
var billInfo = new CreateBillInfo
{
    BillId = Guid.NewGuid().ToString(),
    Amount = new MoneyAmount
    {
        ValueDecimal = 199.9m,
        CurrencyEnum = CurrencyEnum.Rub
    },
    Comment = "comment",
    ExpirationDateTime = DateTime.Now.AddDays(45),
    Customer = new Customer
    {
        Email = "mail@example.com",
        Account = Guid.NewGuid().ToString(),
        Phone = "79123456789"
    },
};
var response = client.createBill(billInfo);
Body parameter Description Type
billId Required. Unique invoice identifier in merchant's system String(200)
amount Required. Invoice amount information Object
amount.
currency
Required. Invoice amount currency code. RUB KZT Alpha-3 ISO 4217 code
amount.
value
Required. Amount of the invoice rounded down to two decimals Number(6.2)
expirationDateTime Required. Invoice due date. Time should be specified with time zone. If the date passed, the invoice is cancelled. YYYY-MM-DDThhmm+\-hh:mm
customer Customer data of the invoice subject Object
customer.
phone
Phone number of the client to which the invoice is issuing (international format) String
customer.
email
E-mail of the client where the invoice payment link will be sent String
customer.
account
Client identifier in merchant's system String
comment Invoice commentary String(255)
customFields Additional invoice data Object
customFields.
paySourcesFilter
Allow only these payment methods for the client on Payment Form. Possible values:
qw - QIWI Wallet
card - card payment
Comma separated string
customFields.
themeCode
Personalization for Payments Form String(255)

Response ←

Successful response body example

{
    "siteId": "9hh4jb-00",
    "billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
    "amount": {
        "currency": "RUB",
        "value": "1.00"
    },
    "status": {
        "value": "WAITING",
        "changedDateTime": "2021-01-18T14:22:56.672+03:00"
    },
    "customer": {
        "phone": "78710009999",
        "email": "test@example.com",
        "account": "454678"
    },
    "customFields": {
        "paySourcesFilter": "qw",
        "themeCode": "Yvan-YKaSh",
        "yourParam1": "64728940",
        "yourParam2": "order 678"
    },
    "comment": "Text comment",
    "creationDateTime": "2021-01-18T14:22:56.672+03:00",
    "expirationDateTime": "2025-12-10T09:02:00+03:00",
    "payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}

Error response body

{
    "serviceName": "invoicing-api",
    "errorCode": "http.message.conversion.failed",
    "description": "Bad request",
    "userMessage": "Bad request",
    "dateTime": "2021-01-18T14:29:51.984+03:00",
    "traceId": "8fa9cfe10c7f83d1"
}

Field Type Description
billId String Unique invoice identifier in the merchant's system
siteId String Merchant's site identifier in p2p.qiwi
amount Object Information about the invoice amount
amount.
value
String The invoice amount. The number is rounded down to two decimals
amount.
currency
String Currency identifier of the invoice amount (Alpha-3 ISO 4217 code)
status Object Data of the invoice current status
status.
value
String String representation of the status. Possible statuses
status.
changedDateTime
String Status refresh date. Date format:
YYYY-MM-DDThh:mm:ss±hh
customer Object Customer data of the invoice subject
customer.
phone
String The customer’s phone (if specified in the invoice)
customer.
email
String The customer's e-mail (if specified in the invoice)
customer.
account
String The customer's identifier in the merchant's system (if specified in the invoice)
customFields Object Additional invoice data provided by the merchant
comment String Comment to the invoice
creationDateTime String System date of the invoice creation. Date format:
YYYY-MM-DDThh:mm:ss±hh
payUrl String Pay form URL
expirationDateTime String Expiration date of the payment form URL (invoice payment's due date). Redirect the customer to the URL link for invoice payment, or use Popup JavaScript library, to open the form in the popup window. Date format:
YYYY-MM-DDThh:mm:ss±hh

P2P Invoices API. Checking the Invoice Status

We recommend using the method after receiving the payment notification.

Request → GET

const billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

qiwiApi.getBillInfo(billId).then( data => {
    //do smth ith data
});
curl --location --request GET \
  'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e' \
  --header 'accept: application/json' \
  --header 'Authorization: Bearer <SECRET_KEY>'
<?php

$billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->getBillInfo($billId);

print_r($response);

?>
String billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";
 BillResponse response = client.getBillInfo(billId);
var billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";

var response = client.getBillInfo(billId);

Response ←

Successful response body example

{
    "siteId": "9hh4jb-00",
    "billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
    "amount": {
        "currency": "RUB",
        "value": "1.00"
    },
    "status": {
        "value": "WAITING",
        "changedDateTime": "2021-01-18T14:22:56.672+03:00"
    },
    "customer": {
        "email": "test@example.com",
        "phone": "78710009999",
        "account": "454678"
    },
    "customFields": {
        "paySourcesFilter": "qw",
        "themeCode": "Yvan-YKaSh",
        "yourParam1": "64728940",
        "yourParam2": "order 678"
    },
    "comment": "Text comment",
    "creationDateTime": "2021-01-18T14:22:56.672+03:00",
    "expirationDateTime": "2025-12-10T09:02:00+03:00",
    "payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}

Error response body example

{
    "serviceName": "invoicing-api",
    "errorCode": "api.invoice.not.found",
    "description": "Invoice not found",
    "userMessage": "Invoice not found",
    "dateTime": "2021-01-18T14:34:40.865+03:00",
    "traceId": "b3d41cafa0c6d088"
}
Field Type Description
billId String Unique invoice identifier in the merchant's system
siteId String Merchant's site identifier in p2p.qiwi
amount Object Information about the invoice amount
amount.
value
Number The invoice amount. The number is rounded down to two decimals
amount.
currency
String Currency identifier of the invoice amount (Alpha-3 ISO 4217 code)
status Object Invoice status data
status.
value
String Current invoice status
status.
changedDateTime
String Status refresh date
customFields Object Additional invoice data provided by the merchant
customer Object Customer data of the invoice subject
customer.
phone
String The customer’s phone (if specified in the invoice)
customer.
email
String The customer's e-mail (if specified in the invoice)
customer.
account
String The customer's identifier in the merchant's system (if specified in the invoice)
comment String Comment to the invoice
creationDateTime String System date of the invoice creation. Date format:
YYYY-MM-DDThh:mm:ss
payUrl String Payment form URL for customer redirect
expirationDateTime String Expiration date of the pay form link (invoice payment's due date). Date format:
YYYY-MM-DDThh:mm:ss

P2P Invoices API. Cancelling the Invoice

Use this method to cancel unpaid invoice.

Request → POST

const bill_id = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

qiwiApi.cancelBill(billId).then( data => {
    //do smth with data
});
curl --location --request POST \
  'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e/reject' \
  --header 'content-type: application/json' \
  --header 'accept: application/json' \
  --header 'Authorization: Bearer <SECRET_KEY>' \
  --data-raw ''
<?php

$billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->cancelBill($billId);

print_r($response);

?>
String billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";
 BillResponse response = client.cancelBill(billId);
var billId = "fcb40a23-6733-4cf3-bacf-8e425fd1fc71";

var response = client.cancelBill(billId);

Response ←

Successful response body example

{
    "siteId": "9hh4jb-00",
    "billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
    "amount": {
        "currency": "RUB",
        "value": "1.00"
    },
    "status": {
        "value": "REJECTED",
        "changedDateTime": "2021-01-18T14:36:17.65+03:00"
    },
    "customer": {
        "email": "test@example.com",
        "phone": "78710009999",
        "account": "454678"
    },
    "customFields": {
        "paySourcesFilter": "qw",
        "themeCode": "Yvan-YKaSh",
        "yourParam1": "64728940",
        "yourParam2": "order 678"
    },
    "comment": "Text comment",
    "creationDateTime": "2021-01-18T14:22:56.672+03:00",
    "expirationDateTime": "2025-12-10T09:02:00+03:00",
    "payUrl": "https://oplata.qiwi.com/form/?invoice_uid=aa0fa2bb-5452-47ca-9190-cd9c1a73718f"
}

Error response body example

{
    "serviceName": "invoicing-api",
    "errorCode": "api.invoice.not.found",
    "description": "Invoice not found",
    "userMessage": "Invoice not found",
    "dateTime": "2021-01-18T14:39:54.265+03:00",
    "traceId": "bc6bb6e7c5cf5beb"
}
Field Type Description
billId String Unique invoice identifier in the merchant's system
siteId String Merchant's site identifier in p2p.qiwi
amount Object Information about the invoice amount
amount.
value
Number The invoice amount. The number is rounded down to two decimals
amount.
currency
String Currency identifier of the invoice amount (Alpha-3 ISO 4217 code)
status Object Invoice status data
status.
value
String Current invoice status
status.
changedDateTime
String Status refresh date
customFields Object Additional invoice data provided by the merchant
customer Object Customer data of the invoice subject
customer.
phone
String The customer’s phone (if specified in the invoice)
customer.
email
String The customer's e-mail (if specified in the invoice)
customer.
account
String The customer's identifier in the merchant's system (if specified in the invoice)
comment String Comment to the invoice
creationDateTime String System date of the invoice creation. Date format:
YYYY-MM-DDThh:mm:ss
payUrl String Payment form URL
expirationDateTime String Expiration date of the pay form link (invoice payment's due date). Date format:
YYYY-MM-DDThh:mm:ss

P2P Invoices API. Invoice Payment Statuses

Status Description Final
WAITING Invoice issued awaiting for payment -
PAID Invoice paid +
REJECTED Invoice rejected by customer +
EXPIRED Invoice expired. Invoice not paid +

Invoice payment notifications

Before working with the notification service, consider the Notification API Integration Terms.

Pools of IP-addresses from which QIWI service sends notifications:

If your web service works behinds the firewall, you need to add these IP-addresses to the list of allowed addresses for incoming TCP packets.

Notification (callback) is an incoming HTTP POST-request.

Request ← POST

Notification example

POST /qiwi-notify.php HTTP/1.1
Accept: application/json
Content-type: application/json
X-Api-Signature-SHA256: J4WNfNZd***V5mv2w=
Host: example.com

{
  "bill": {
    "siteId": "9hh4jb-00",
    "billId": "cc961e8d-d4d6-4f02-b737-2297e51fb48e",
    "amount": {
      "value": "1.00",
      "currency": "RUB"
    },
    "status": {
      "value": "PAID",
      "changedDateTime": "2021-01-18T15:25:18+03"
    },
    "customer": {
      "phone": "78710009999",
      "email": "test@example.com",
      "account": "454678"
    },
    "customFields": {
      "paySourcesFilter": "qw",
      "themeCode": "Yvan-YKaSh",
      "yourParam1": "64728940",
      "yourParam2": "order 678"
    },
    "comment": "Text comment",
    "creationDateTime": "2021-01-18T15:24:53+03",
    "expirationDateTime": "2025-12-10T09:02:00+03"
  },
  "version": "1"
}

The request's body contains JSON-serialized invoice data encoded by UTF-8 codepage.

Notification server registration

New keys issue for notification has been stopped. Sorry for the inconvenience.

URL address of notification processing server is configured in the personal account profile. A new key pair is issued simultaneously. Only one key pair can be supported for the notification server — the old key pair is disabled once the new is isssued.

  1. Login to your personal account.
  2. Open API tab and click Create key pair and configure button. When you configure the server address along with creating a key pair for the first time, click Configure button.

    p2p API Settings

  3. Enter name for the new key pair.

  4. Select Use this key pair for notifications about invoice status changes field.

  5. In URL notification server specify your notification service URL. The service URL must be accessible from the Internet.
  6. Click Create button.

  7. Change QIWI P2P keys in your application settings to the new one.

How to verify notification authenticity

Upon receiving incoming notification you need to verify its digital signature. The notification signature is placed in X-Api-Signature-SHA256 HTTP header. Signature mechanism uses HMAC algorithm integrity check with SHA256-hash function.

Signature verification algorithm is as follows:

  1. Prepare a string of the following notification's parameters separated by |:

    invoice_parameters = {amount.currency}|{amount.value}|{billId}|{siteId}|{status.value}

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

  2. Apply HMAC-SHA256 function:

    hash = HMAС(SHA256, invoice_parameters, <SECRET_KEY>)

    where:

    • <SECRET_KEY>secret key used for the invoice creation;
    • invoice_parameters – string from step 1.
  3. Compare X-Api-Signature-SHA256 header's value with the result of step 2.

const validSignatureFromNotificationServer =
      '07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b';

const notificationData = {
    bill: {
        siteId: 'test',
        billId: 'test_bill',
        amount: { value: 1, currency: 'RUB' },
        status: { value: 'PAID', datetime: '2018-03-01T11:16:12+03' },
        customer: {},
        customFields: {},
        creationDateTime: '2018-03-01T11:15:39+03',
        expirationDateTime: '2018-04-15T11:15:39+03'
    },
    version: '1'
};

const merchantSecret = 'test-merchant-secret-for-signature-check';

qiwiApi.checkNotificationSignature(
    validSignatureFromNotificationServer, notificationData, merchantSecret
); // true
<?php

$validSignatureFromNotificationServer = '07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b';
$notificationData = [
  'bill' => [
    'siteId' => 'test',
    'billId' => 'test_bill',
    'amount' => ['value' => 1, 'currency' => 'RUB'],
    'status' => ['value' => 'PAID']
  ],
  'version' => '3'
];
$merchantSecret = 'test-merchant-secret-for-signature-check';

/** @var \Qiwi\Api\BillPayments $billPayments */
$billPayments->checkNotificationSignature(
  $validSignatureFromNotificationServer, $notificationData, $merchantSecret
); // true

?>
String merchantSecret = "test-merchant-secret-for-signature-check";
Notification notification = new Notification(
        new Bill(
                "test",
                "test_bill",
                new MoneyAmount(
                        BigDecimal.ONE,
                        Currency.getInstance("RUB")
                ),
                BillStatus.PAID
        ),
        "3"
);
String validSignature = "07e0ebb10916d97760c196034105d010607a6c6b7d72bfa1c3451448ac484a3b";
 BillPaymentsUtils.checkNotificationSignature(validSignature, notification, merchantSecret); //true

String and key of the signature are encoded in UTF-8.

Notification field Description Type
bill Invoice data Object
billId Invoice identifier in the merchant's system String(200)
siteId Merchant's site identifier in p2p.qiwi String
amount Information about the invoice amount Object
amount.
value
The invoice amount. The number is rounded down to two decimals Number(6.2)
amount.
currency
Currency identifier of the invoice amount (Alpha-3 ISO 4217 code) String(3)
status Invoice status data Object
status.
value
Current invoice status String
status.
changedDateTime
Status refresh date. Date format:
YYYY-MM-DDThh:mm:ssZ
String
customFields Additional invoice data provided by the merchant Object
customer Customer data of the invoice subject (if specified in the invoice) Object
customer.
phone
The customer’s phone (if specified in the invoice) String
customer.
email
The customer's e-mail (if specified in the invoice) String
customer.
account
The customer's identifier in the merchant's system (if specified in the invoice) String
comment Comment to the invoice String(255)
creationDateTime System date of the invoice creation. Date format:
YYYY-MM-DDThh:mm:ssZ
String
payUrl Payment form URL String
expirationDateTime Expiration date of the pay form link (invoice payment's due date). Date format:
YYYY-MM-DDThh:mm:ssZ
String
version Notification service version String

Response →

HTTP/1.1 200 OK
Content-Type: application/json

{
 "error":"0"
}

After receiving incoming notification request, you should verify its signature and returns the JSON-response. The processing result code should be returned in response.

Invoice payment form

You can add parameters to URL from payUrl field of the response to invoice request:

Invoice URL example

curl https://oplata.qiwi.com/form?invoiceUid=a8437e7e-dc48-44f7-9bdb-4d46ca8ef2e4&paySource=qw
Parameter Description Type
paySource Pre-selected payment method for the client on Payment Form. Possible values:
qw - QIWI Wallet
card - card payment
mobile - payment from phone balance
When specified method is inaccessible, the page automatically selects recommended method for the user.
String

Add referal links for payments from your site. This confirms the site is real and will avoid the wallet blocking problems. Payments from any page without Refer header will result in wallet block. See details in the documentation.

Referal link example

<?php
  header("Referrer-Policy: no-referrer-when-downgrade");
?>

To make sure the full referer is tranferred for the new version of browsers, set Referrer-Policy in your server response with no-referrer-when-downgrade value. It can be done for all pages of your site or only for the page with payment form link.

Personalization

Personalization allows you to create a payment form with your style, customizable logo, background and color of the buttons.

Example of a customized payment form:

Customer form

To setup payment form appearance:

  1. Login to your personal account on p2p.qiwi.com.
  2. Open Transfer acceptance form tab. Your code style for themeCode parameter (you need it for API and SDK requests) is marked bold in grey block. Value codeStyle is different for different QIWI wallets.

    alt_text

  3. Click Tune button and configure the form' parameters.
  4. Click Save button.

Invoice Issue on Payment Form

curl https://oplata.qiwi.com/create?publicKey=Fnzr1yTebUiQaBLDnebLMMxL8nc6FF5zfmGQnypc*******&amount=100&billId=cc961e8d-d4d6-4f02-b737-2297e51fb48e&customFields%5BthemeCode%5D=codeStyle

Invoice Issue by API

curl --location --request PUT \
  'https://api.qiwi.com/partner/bill/v1/bills/cc961e8d-d4d6-4f02-b737-2297e51fb48e' \
  --header 'content-type: application/json' \
  --header 'accept: application/json' \
  --header 'Authorization: Bearer <SECRET_KEY>' \
  --data-raw '{
    "amount": {
     "currency": "RUB",
     "value": 100.00
    },
    "comment": "Text comment",
    "expirationDateTime": "2025-04-13T14:30:00+03:00",
    "customer": {},
    "customFields": {"themeCode":"codeStyle"}
  }'
const billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';

const fields = {
    amount: 1.00,
    currency: 'RUB',
    comment: 'Hello world',
    customFields: {themeCode: 'codeStyle'},
    expirationDateTime: '2018-03-02T08:44:07+03:00'
};

qiwiApi.createBill( billId, fields ).then( data => {
    //do with data
});
<?php

$billId = 'cc961e8d-d4d6-4f02-b737-2297e51fb48e';
$customFields = ['themeCode' => 'codeStyle'];
$fields = [
  'amount' => 1.00,
  'currency' => 'RUB',
  'comment' => 'test',
  'expirationDateTime' => '2018-03-02T08:44:07+03:00',
  'email' => 'mail@example.org',
  'account' => 'client4563',
  'customFields' => $customFields
];

/** @var \Qiwi\Api\BillPayments $billPayments */
$response = $billPayments->createBill($billId, $fields);

print_r($response);

?>
client.CreateBill(
    info: new CreateBillInfo
    {
        BillId = Guid.NewGuid().ToString(),
        Amount = new MoneyAmount
        {
            ValueDecimal = 199.9m,
            CurrencyEnum = CurrencyEnum.Rub
        },
        Comment = "comment",
        ExpirationDateTime = DateTime.Now.AddDays(45),
        Customer = new Customer
        {
            Email = "mail@example.org",
            Account = Guid.NewGuid().ToString(),
            Phone = "79123456789"
        },
        CustomFields: new CustomFields
        {
            ThemeCode = "codeStyle"
        }
    }
);

To apply the appearance to the payment form:

The library methods open the payment form as a popup window over your site in browser.

Download QIWI Checkout Popup

Installation:

<script src='https://oplata.qiwi.com/popup/v1.js'></script>

The library has two methods:

Open an existing invoice

Open an existing invoice

params = {
    payUrl: 'https://oplata.qiwi.com/form?invoiceUid=06df838c-0f86-4be3-aced-a950c244b5b1'
}

QiwiCheckout.openInvoice(params)
    .then(data => {
        // ...
    })
    .catch(error => {
        // ...
    })

Call function QiwiCheckout.openInvoice.

Parameter Description Type
payUrl Required. Payment form URL String

Open personal payform

Call function QiwiCheckout.openPreorder.

Open your custom payform

params = {
    widgetAlias: 'https://my.qiwi.com/form/User-ABCDE1234-56'
}

QiwiCheckout.openPreorder(params)
    .then(data => {
        // ...
    })
    .catch(error => {
        // ...
    })
Parameter Description Type
widgetAlias Required. URL of your payform String