Merchant API v1

Adsrek Pay API Documentation

Полное руководство по созданию счетов, HMAC-подписям, проверке статусов, callback-уведомлениям and внутренним выплатам at AR-кошельки.

HTTPSPOST JSONHMAC-SHA256UTF-8
You do not have a site yet. The documentation is available with demo values. To use real keys, first add a site.
01

Quick start

Minimal working payment acceptance flow.

1Create a site and obtain SHOP ID, API KEY and SECRET KEY.
2Build the HMAC signature on your server and create an invoice.
3Redirect the customer to payment_url.
4After payment, verify the signed callback or check status through the API.
Base URL:https://adsrekpay.com
Important: a customer redirect to the Success URL does not confirm payment by itself. Mark the order as paid only after the status paid and signature verification.
02

General requirements

Request format and supported values.

Method and format

All methods accept only POST. Recommended header: Content-Type: application/json. A regular POST array is also accepted.

Request size

Maximum request body size — 65,536 bytes. Exceeding this limit returns HTTP 413.

Currencies

RUB, USD, EUR, USDT.

Amount precision

RUB, USD and EUR use 2 decimal places. USDT uses 8. The server normalizes the amount before signature verification.

Invoice lifetime

An invoice is valid for 24 hours from creation. The response includes the Unix timestamp expires_at.

Encoding

Use UTF-8. Fields order_id and description are sanitized and length-limited.

03

Авторизация and HMAC-SHA256

Every request is signed with the selected site SECRET KEY.

Every request always includes shop_id, api_key and a 64-character hexadecimal sign. The SECRET KEY is used only to calculate the signature and is never sent to the API.

Invoice creationcreate|SHOP_ID|API_KEY|ORDER_ID|AMOUNT|CURRENCY
Check statusstatus|SHOP_ID|API_KEY|ORDER_ID
Payoutpayout|SHOP_ID|API_KEY|ORDER_ID|AMOUNT|CURRENCY|TARGET
PHP: signature calculation
function adsrekSign(string $canonical, string $secretKey): string
{
    return hash_hmac('sha256', $canonical, $secretKey);
}

$amount = number_format(10, 2, '.', '');
$canonical = 'create|1001|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|ORDER-1001|' . $amount . '|RUB';
$sign = adsrekSign($canonical, 'YOUR_SECRET_KEY');
Normalization: for an amount of 10 RUB, the following string is signed 10.00, and for 1 USDT — 1.00000000. Currency is sent in uppercase.
04

Invoice creation

POST https://adsrekpay.com/api/pay/create.php

FieldRequiredDescription
shop_idYesSite ID from your account.
api_keyYesPublic API KEY of the site.
order_idYesUnique order ID within the site, up to 100 characters.
amountYesAmount must be at least 0.01.
currencyYesRUB, USD, EUR or USDT.
descriptionNoOrder description, up to 255 characters.
callback_urlNoPublic HTTPS URL. Overrides the site URL for this invoice.
success_urlNoHTTPS return URL after successful payment.
fail_urlNoHTTPS return URL after cancellation or error.
signYesHMAC-SHA256 of the canonical string.
JSON request
{
  "shop_id": 1001,
  "api_key": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "order_id": "ORDER-1001",
  "amount": "10.00",
  "currency": "RUB",
  "description": "Order payment ORDER-1001",
  "callback_url": "https://site.ru/payment/callback",
  "success_url": "https://site.ru/payment/success",
  "fail_url": "https://site.ru/payment/fail",
  "sign": "HMAC_SHA256_HEX"
}
Successful response
{
  "ok": true,
  "invoice_id": 1501,
  "order_id": "ORDER-1001",
  "status": "new",
  "amount": "10.00",
  "currency": "RUB",
  "payment_url": "https://adsrekpay.com/merchant/invoice.php?id=TOKEN",
  "expires_at": 1785704400
}
Idempotency: a repeated request with the same order_id, amount and currency returns the existing invoice. If the amount or currency differs, the API returns ORDER_ID_CONFLICT.
05

Check invoice status

POST https://adsrekpay.com/api/pay/status.php

Request
{
  "shop_id": 1001,
  "api_key": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "order_id": "ORDER-1001",
  "sign": "HMAC_SHA256_HEX"
}
Response
{
  "ok": true,
  "invoice": {
    "id": "1501",
    "order_id": "ORDER-1001",
    "amount": "10.00",
    "currency": "RUB",
    "status": "paid",
    "created_at": "1785618000",
    "paid_at": "1785618125"
  }
}

Verify together status, amount, currency and order_id. Do not mark an order as paid based only on the return URL.

06

Callback / webhook

A notification is sent after the invoice is successfully paid.

Format

POST application/x-www-form-urlencoded.

Successful response

Any HTTP status from 200 through 299 is treated as successful delivery.

Repeating

Up to 5 attempts, at least 5 minutes apart, until a 2xx response is received.

Network protection

Public HTTPS only, with SSL verification and no redirects. Connection timeout is 5 seconds; total timeout is 10 seconds.

Callback fields
invoice_id=1501
shop_id=1001order_id=ORDER-1001
amount=10.00
currency=RUB
status=paid
paid_at=1785618125
sign=HMAC_SHA256_HEX
Signature string
invoice_id|shop_id|order_id|amount|currency|status

1501|1001|ORDER-1001|10.00|RUB|paid
PHP: secure callback handler
<?php
$secretKey = getenv('ADSREK_PAY_SECRET');
$data = $_POST;

$required = ['invoice_id','shop_id','order_id','amount','currency','status','sign'];
foreach ($required as $field) {
    if (!isset($data[$field])) {
        http_response_code(400);
        exit('MISSING_FIELD');
    }
}

$canonical = implode('|', [
    (int)$data['invoice_id'],
    (int)$data['shop_id'],
    (string)$data['order_id'],
    (string)$data['amount'],
    (string)$data['currency'],
    (string)$data['status'],
]);
$expected = hash_hmac('sha256', $canonical, $secretKey);

if (!hash_equals($expected, strtolower((string)$data['sign']))) {
    http_response_code(401);
    exit('BAD_SIGNATURE');
}

// Find the order by order_id and lock the row in a database transaction.
// A repeated callback for an already paid order must return 200 without crediting it again.
if ($data['status'] === 'paid') {
    // Also compare amount and currency with your order.
    // Mark the order as paid atomically.
}

http_response_code(200);
echo 'OK';
07

Payout to an AR wallet

POST https://adsrekpay.com/api/pay/payout.php

This is not an external withdrawal. This method transfers funds only inside Adsrek Pay to the recipient’s AR wallet. The fee 0.50% is charged to the merchant in addition to the transfer amount.
Request
{
  "shop_id": 1001,
  "api_key": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "order_id": "PAYOUT-1001",
  "amount": "10.00",
  "currency": "RUB",
  "target": "AR584193027",
  "sign": "HMAC_SHA256_HEX"
}
Response
{
  "ok": true,
  "payout": {
    "transfer_id": 9021,
    "amount": "10.00",
    "currency": "RUB",
    "recipient": "AR584193027"
  }
}

order_id is required and provides idempotency. Repeating a successfully completed payout returns the saved result instead of debiting funds again.

08

Invoice statuses

Actual Merchant API v1 statuses.

newAwaiting paymentThe invoice has been created and is available to the customer.
paidPaidFinal successful status.
expiredExpired24 hours elapsed without payment.
cancelCancelledFinal cancellation status.
refundedRefundedFunds have been refunded.
09

API errors

An error response has the form {"ok":false,"error":"CODE"}.

HTTPCodeValue
400INVALID_JSONThe request body could not be parsed.
401AUTH_REQUIREDshop_id, api_key or a valid 64-character signature is missing.
401SHOP_NOT_FOUNDThe site was not found, is disabled, or the API KEY is invalid.
401BAD_SIGNATUREThe HMAC signature does not match.
401SHOP_SECRET_ERRORThe server could not decrypt the site SECRET KEY.
404INVOICE_NOT_FOUNDNo invoice was found for the specified order_id.
405METHOD_NOT_ALLOWEDA method other than POST was used.
413PAYLOAD_TOO_LARGEThe request body exceeds 65,536 bytes.
422BAD_CURRENCYThe currency is not supported.
422BAD_AMOUNTThe amount failed normalization.
422BAD_INVOICEorder_id is empty or the amount is below the minimum.
422BAD_CALLBACK_URLThe callback URL is not a public HTTPS address.
422ORDER_ID_CONFLICTA repeated order_id was sent with a different amount or currency.
422RECIPIENT_NOT_FOUNDThe internal payout recipient was not found.
422INSUFFICIENT_FUNDSThe merchant balance is insufficient for the amount plus fee.
422DUPLICATE_OR_IN_PROGRESSA payout with this order_id is already processing or previously failed.
422SELF_PAYOUT_NOT_ALLOWEDYou cannot make an API payout to yourself.
10

Ready examples

Code for server-side integration.

PHP cURL: create an invoice
<?php
$shopId = 1001;
$apiKey = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
$secretKey = getenv('ADSREK_PAY_SECRET');
$orderId = 'ORDER-1001';
$amount = number_format(10, 2, '.', '');
$currency = 'RUB';

$canonical = implode('|', ['create', $shopId, $apiKey, $orderId, $amount, $currency]);
$payload = [
    'shop_id' => $shopId,
    'api_key' => $apiKey,
    'order_id' => $orderId,
    'amount' => $amount,
    'currency' => $currency,
    'description' => 'Order payment ' . $orderId,
    'sign' => hash_hmac('sha256', $canonical, $secretKey),
];

$ch = curl_init('https://adsrekpay.com/api/pay/create.php');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 15,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($response === false) {
    throw new RuntimeException(curl_error($ch));
}
curl_close($ch);

$data = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
if ($httpCode !== 200 || empty($data['ok'])) {
    throw new RuntimeException($data['error'] ?? 'Adsrek Pay API error');
}
header('Location: ' . $data['payment_url']);
exit;
Python: create an invoice
import hashlib
import hmac
import os
import requests

shop_id = 1001api_key = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
secret_key = os.environ["ADSREK_PAY_SECRET"]
order_id = "ORDER-1001"
amount = "10.00"
currency = "RUB"

canonical = f"create|{shop_id}|{api_key}|{order_id}|{amount}|{currency}"
sign = hmac.new(secret_key.encode(), canonical.encode(), hashlib.sha256).hexdigest()

payload = {
    "shop_id": shop_id,
    "api_key": api_key,
    "order_id": order_id,
    "amount": amount,
    "currency": currency,
    "description": f"Order payment {order_id}",
    "sign": sign,
}
response = requests.post("https://adsrekpay.com/api/pay/create.php", json=payload, timeout=15)
response.raise_for_status()
data = response.json()
print(data["payment_url"])
JavaScript: redirect only
// The invoice is created only on your server, where the SECRET KEY is stored.
const response = await fetch('/your-server/create-adsrek-invoice', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({order_id: 'ORDER-1001'})
});
const data = await response.json();
if (!data.payment_url) throw new Error('Could not create invoice');
window.location.assign(data.payment_url);
11

Security

Mandatory rules for production integration.

  • SECRET KEY — server-side only. Do not put it in HTML, JavaScript, a mobile app or a public repository.
  • Always use HTTPS. HTTP callbacks and local/private IP addresses are blocked.
  • Verify signatures with hash_equals. Regular string comparison can be vulnerable to timing attacks.
  • Verify the amount, currency and order_id. Match the signed callback against the order data in your database.
  • Process repeated notifications idempotently. One invoice must never be credited twice.
  • After key rotation, the old keys stop working immediately. Update configuration atomically.
  • Keep a backup of config/paycore.local.php. Adsrek Pay needs this file to decrypt merchant SECRET KEY values.
12

Limits and current version

Фактические параметры Merchant API v1.

Version

Merchant API v1. Endpoint URLs include PHP file names and must be used unchanged.

Minimum amount

0.01 in the selected currency.

Acceptance fee

1.00% is deducted from the amount credited to the merchant.

API payout fee

0.50% is charged additionally to the merchant.

Sandbox

A separate sandbox is not available yet. For testing, use a separate site and the minimum amount.

Лимит requests

Специальный тарифный rate limit в текущей версии не задан. Не отправляйте частые циклические запросы: используйте callback and разумный polling.