1C-Bitrix Integration with the Payme Payment System (Uzbekistan)
Payme is Uzbekistan's largest payment aggregator with more than 10 million active users. For an online store serving a Uzbek audience, the absence of Payme from the list of payment methods noticeably hurts conversion — mobile shoppers in Uzbekistan are accustomed to paying through this app. There is no official ready-made module for 1C-Bitrix; integration is implemented via a custom JSON-RPC handler using the Subscribe API protocol.
How the Subscribe API Works
Payme does not redirect the buyer to a third-party site — instead, the Payme server calls JSON-RPC methods on your server. The store implements a server accepting 6 methods:
| Method | Purpose |
|---|---|
CheckPerformTransaction |
Verify the order exists and the amount is correct |
CreateTransaction |
Record the start of a payment |
PerformTransaction |
Confirm successful payment |
CancelTransaction |
Handle a cancellation |
CheckTransaction |
Return the transaction status |
GetStatement |
Transaction reconciliation for a period |
Server Implementation in Bitrix
The entry point is a dedicated PHP file local/api/payme.php:
<?php
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
header('Content-Type: application/json');
// Basic Auth — the password must match the key from the Payme merchant cabinet
$auth = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
preg_match('/Basic (.+)/', $auth, $m);
[, $password] = explode(':', base64_decode($m[1] ?? ''), 2);
if (!hash_equals(PAYME_CASHIER_KEY, $password)) {
echo json_encode(['error' => ['code' => -32504, 'message' => 'Auth failed']]);
exit;
}
$body = json_decode(file_get_contents('php://input'), true);
$method = $body['method'] ?? '';
$params = $body['params'] ?? [];
$id = $body['id'] ?? null;
if ($method === 'CheckPerformTransaction') {
$orderId = (int)($params['account']['order_id'] ?? 0);
$amount = (int)($params['amount'] ?? 0); // in tiyin
$order = Bitrix\Sale\Order::load($orderId);
if (!$order) {
echo json_encode(['error' => ['code' => -31050, 'message' => ['ru' => 'Order not found']], 'id' => $id]);
exit;
}
// Compare amount (store prices in UZS multiplied by 100)
$expected = (int)round($order->getPrice() * 100);
if ($expected !== $amount) {
echo json_encode(['error' => ['code' => -31001, 'message' => ['ru' => 'Amount mismatch']], 'id' => $id]);
exit;
}
echo json_encode(['result' => ['allow' => true], 'id' => $id]);
exit;
}
if ($method === 'PerformTransaction') {
$paymeId = $params['id'];
// Find the payment by payme_id and confirm
$payment = findPaymentByPaymeId($paymeId);
if ($payment && !$payment->isPaid()) {
$payment->setPaid('Y');
$payment->save();
}
echo json_encode(['result' => [
'transaction' => $paymeId,
'perform_time' => time() * 1000,
'state' => 2,
], 'id' => $id]);
exit;
}
Transaction Storage
Payme requires idempotency: a repeated CreateTransaction with the same ID must return the existing transaction. A dedicated table is needed:
CREATE TABLE b_payme_transactions (
payme_id VARCHAR(64) PRIMARY KEY,
order_id INT NOT NULL,
amount BIGINT NOT NULL,
state TINYINT DEFAULT 1,
create_time BIGINT,
perform_time BIGINT DEFAULT 0,
cancel_time BIGINT DEFAULT 0,
reason TINYINT DEFAULT NULL
);
Transaction states: 1 — created, 2 — completed, -1/-2 — cancelled at different stages.
Currency and Conversion
Payme only accepts Uzbek soums. Pass the amount in tiyin (1 UZS = 100 tiyin). If store prices are in USD or EUR, conversion at the CBU exchange rate is required (recommended to cache for 1 hour):
$amountTiyin = (int)round($orderPriceUsd * $uzsPerUsd * 100);
Testing
Test environment: https://checkout.test.paycom.uz/. Test keys are issued separately from live keys in the developer cabinet. Test all 6 methods, especially CancelTransaction at different stages — depending on whether PerformTransaction has already been executed, different state values (-1 or -2) are returned.
Timeline
| Task | Duration |
|---|---|
| JSON-RPC server development (all 6 methods) | 2–3 days |
| Integration with the Bitrix Sale module | 1–2 days |
| Testing and submission to Payme for review | 1–2 days |
| Store registration in Payme | 3–10 business days |







