inPAY API Hujjatlari
v1.0 · Stable Yordam
Developer Documentation

To'lovlarni qabul qiling — bir necha daqiqada

inPAY REST API merchantlarga to'lov tranzaksiyalarini xavfsiz va tezkor yaratish imkoniyatini beradi. RESTful arxitektura, JSON format, Bearer Token autentifikatsiya.

Xavfsizlik
Bearer Token + IP Whitelist
Real-time
Tezkor to'lov jarayoni
Webhook
Avtomatik bildirishnomalar
Boshlash uchun: inPAY platformasida ro'yxatdan o'tib, kassa hisobingizni yarating va merchant_id hamda merchant_token oling.

Tezkor boshlash

5 daqiqada to'lov qabul qilishni boshlang

1
Hisob ma'lumotlarini oling
inPAY platformasida ro'yxatdan o'ting va kassa yarating. Tasdiqlangach merchant_id va merchant_token oling.
2
Bearer token oling
GET /authorization/ chaqirib 24 soatlik tokenni oling.
3
To'lov yarating
POST /create/ orqali tranzaksiya yarating va pay_url oling.
4
Foydalanuvchini yo'naltiring
Mijozni pay_url ga yuboring — u to'lovni amalga oshiradi.
5
Webhookni qabul qiling
callback_url ga POST so'rov keladi — bazani yangilang va HTTP 200 qaytaring.

Autentifikatsiya

24 soatlik Bearer token olish

GET/authorization/

API bilan ishlash uchun Bearer Token olish kerak. Token 24 soat amal qiladi. Har bir so'rovda Authorization: Bearer {token} headerini yuboring.

Query parametrlari
ParametrTipMajburiyTavsif
merchant_idinteger✓ HaMerchant identifikatori
merchant_tokenstring✓ HaMerchant token (32 belgili)
Headers
HTTP
Accept: application/json
cURL
curl -X GET "https://inpay.uz/api/v1/authorization/?merchant_id=1353&merchant_token=6a7bf375b302cfcda6692e6f60402cb3" \
  -H "Accept: application/json"
PHP
<?php
$curl = curl_init();
curl_setopt_array($curl, [
  CURLOPT_URL            => 'https://inpay.uz/api/v1/authorization/?merchant_id=1353&merchant_token=...',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => ['Accept: application/json'],
]);
$res = json_decode(curl_exec($curl), true);
curl_close($curl);

// Tokenni keshda saqlang (24 soat)
apcu_store('inpay_token', $res['bearer_token'], 86400);
Python
import requests

r = requests.get(
    "https://inpay.uz/api/v1/authorization/",
    params={"merchant_id": 1353, "merchant_token": "..."},
    headers={"Accept": "application/json"},
    timeout=10,
)
token = r.json()["bearer_token"]
# Save token to cache for 24 hours
Muvaffaqiyatli javob
JSON · 200 OK
{
  "success": true,
  "bearer_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Eslatma: Bearer token 24 soat amal qiladi. Har so'rovda yangi token olish o'rniga, tokenni keshda saqlang va muddati tugaganda yangilang.

To'lov yaratish

Yangi tranzaksiyani boshlash

POST/create/

Yangi to'lov tranzaksiyasini yaratish. Muvaffaqiyatli bo'lsa foydalanuvchini pay_url ga yo'naltiring. Endpoint oxirida / belgisi shart.

Headers
HTTP
Content-Type: application/json
Authorization: Bearer {your_bearer_token}
Body (JSON)
ParametrTipMajburiyTavsif
merchant_idstring✓ HaMerchant ID
tokenstring✓ HaMerchant token
amountnumber✓ HaTo'lov summasi (min: 1 000 so'm)
descriptionstring— IxtiyoriyTo'lov haqida izoh
payment_methodstring— IxtiyoriyTo'lov usuli (click, payme, plum, inPAY)
callback_urlstring— IxtiyoriyWebhook URL manzili
phonestring— IxtiyoriyTelefon raqami (998901234567)
return_urlstring— IxtiyoriyTo'lovdan keyin mijoz qaytariladigan sahifa — domen kassangizga tegishli bo'lishi shart
client_ipstring— IxtiyoriyHaqiqiy to'lovchi (mijoz) IP manzili — server orqali ulanadigan integratsiyalar uchun tavsiya etiladi
cURL
curl -X POST "https://inpay.uz/api/v1/create/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -d '{
    "merchant_id":    "1353",
    "token":          "6a7bf375b302cfcda6692e6f60402cb3",
    "amount":         15000,
    "description":    "Order #12345",
    "payment_method": "click",
    "phone":          "998901234567",
    "client_ip":      "203.0.113.10",
    "callback_url":   "https://merchant.uz/payment/callback",
    "return_url":     "https://merchant.uz/thanks"
  }'
PHP
<?php
$payload = [
  'merchant_id'    => '1353',
  'token'          => '6a7bf375b302cfcda6692e6f60402cb3',
  'amount'         => 15000,
  'description'    => 'Order #12345',
  'payment_method' => 'click',
  'phone'          => '998901234567',
  'client_ip'      => '203.0.113.10',
  'callback_url'   => 'https://merchant.uz/payment/callback',
];

$ch = curl_init('https://inpay.uz/api/v1/create/');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST           => true,
  CURLOPT_POSTFIELDS     => json_encode($payload),
  CURLOPT_HTTPHEADER     => [
    'Content-Type: application/json',
    'Authorization: Bearer ' . $bearerToken,
  ],
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);

if ($res['success']) {
  header('Location: ' . $res['pay_url']);
  exit;
}
Node.js / axios
const axios = require('axios');

const { data } = await axios.post('https://inpay.uz/api/v1/create/', {
  merchant_id:    '1353',
  token:          '6a7bf375b302cfcda6692e6f60402cb3',
  amount:         15000,
  description:    'Order #12345',
  payment_method: 'click',
  phone:          '998901234567',
  client_ip:      '203.0.113.10',
  callback_url:   'https://merchant.uz/payment/callback',
}, {
  headers: {
    'Content-Type':  'application/json',
    'Authorization': `Bearer ${bearerToken}`,
  },
});

console.log('order_id:', data.order_id);
console.log('pay_url: ', data.pay_url);
Python
import requests

payload = {
    "merchant_id":    "1353",
    "token":          "6a7bf375b302cfcda6692e6f60402cb3",
    "amount":         15000,
    "description":    "Order #12345",
    "payment_method": "click",
    "phone":          "998901234567",
    "client_ip":      "203.0.113.10",
    "callback_url":   "https://merchant.uz/payment/callback",
}
r = requests.post(
    "https://inpay.uz/api/v1/create/",
    json=payload,
    headers={"Authorization": f"Bearer {bearer_token}"},
    timeout=15,
)
data = r.json()
print(data["pay_url"])
Muvaffaqiyatli javob
JSON · 200 OK
{
  "success":      true,
  "order_id":     "1ff2f5a6d66f6e9c",
  "pay_url":      "https://inpay.uz/checkout/1ff2f5a6d66f6e9c/click",
  "pay_url_link": "https://my.click.uz/services/pay?service_id=80905&merchant_id=43478&transaction_param=1ff2f5a6d66f6e9c&amount=15000&return_url=https%3A%2F%2Finpay.uz%2Fcheckout%2F1ff2f5a6d66f6e9c",
  "pay_links": {
    "click": "https://my.click.uz/services/pay?service_id=80905&merchant_id=43478&transaction_param=1ff2f5a6d66f6e9c&amount=15000&return_url=...",
    "payme": "https://checkout.paycom.uz/bT02OGU2YzczYzJiZmRiNTMwNDA1ZTNmZWI7YWMub3JkZXJfaWQ9..."
  },
  "phone":        "998901234567",
  "message":      "invoice yaratildi",
  "security": {
    "ip_mode":  "optional",
    "ip_check": "IP verified (optional)"
  }
}
pay_links — kassada yoqilgan usullar uchun to'g'ridan-to'g'ri to'lov tizimi havolalari (click → my.click.uz, payme → checkout.paycom.uz). Telegram bot yoki saytingizda mijozga usul tanlatib, uni inPAY sahifasisiz bevosita to'lovga yuborishingiz mumkin. pay_url_link esa payment_methodda so'ralgan usulning to'g'ridan havolasi ("plum"da plum.uz to'lov sahifasiga olib boradi); usul ko'rsatilmasa pay_url bilan bir xil bo'ladi.
Keyingi qadamlar: Foydalanuvchini pay_url ga yo'naltiring → u to'lovni amalga oshiradi → webhook orqali bildirishnoma olasiz → order_id orqali holatni tekshirishingiz mumkin.

Tranzaksiya holati

order_id orqali holatni tekshirish

GET/transactions/?order_id=...
Query parametrlari
ParametrTipMajburiyTavsif
order_idstring✓ HaTo'lov yaratishda qaytarilgan buyurtma ID
cURL
curl -X GET "https://inpay.uz/api/v1/transactions/?order_id=1ff2f5a6d66f6e9c" \
  -H "Accept: application/json"
PHP
<?php
$orderId = '1ff2f5a6d66f6e9c';
$ch = curl_init();
curl_setopt_array($ch, [
  CURLOPT_URL            => "https://inpay.uz/api/v1/transactions/?order_id={$orderId}",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER     => ['Accept: application/json'],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

// $data['status'] => pending | success | failed | cancelled
Misol
JSON · 200 OK
{
  "success":        true,
  "order_id":       "1ff2f5a6d66f6e9c",
  "status":         "success",
  "amount":         15000,
  "payment_method": "click",
  "created_at":     "2025-12-10 05:14:52",
  "paid_at":        "2025-12-10 05:15:23"
}
Status holatlari
pending
To'lov kutilmoqda
success
To'lov muvaffaqiyatli
failed
To'lov muvaffaqiyatsiz
cancelled
To'lov bekor qilindi

Fiskal chek

order_id orqali soliq.uz fiskal chekini olish

GET/fiscal/?order_id=...

Authorization: Bearer <token> majburiy — order shu merchantga tegishli bo'lsagina qaytadi.

Query parametrlari
ParametrTipMajburiyTavsif
order_idstring✓ HaTo'lov order_id (Payme/Click)
merchant_idstring✓ HaMerchant ID (token egasi)
cURL
curl -X GET "https://inpay.uz/api/v1/fiscal/?order_id=1ff2f5a6d66f6e9c&merchant_id=1353" \
  -H "Authorization: Bearer YOUR_TOKEN"
Misol
JSON · 200 OK
{
  "success": true,
  "data": {
    "order_id":          "1ff2f5a6d66f6e9c",
    "status":            "success",
    "fiscalized":        true,
    "fiscal_url":        "https://ofd.soliq.uz/epi?t=EP...&r=...&s=...",
    "fiscal_receipt_id": "52308469",
    "payment_method":    "payme"
  }
}

Kassa balansi

Kassada hozir ishlatish mumkin bo'lgan summa

GET/info/merchant/{merchant_id}

Authorization: Bearer <token> majburiy. Token qaysi kassaga tegishli bo'lsa, faqat o'sha kassa balansi qaytadi.

Faqat server tomonidan chaqiring. Bu endpoint Access-Control-Allow-Origin sarlavhasini qaytarmaydi — ya'ni brauzerdagi JavaScript uni o'qiy olmaydi. Bu ataylab: bearer token frontend kodiga tushsa, uni ko'rgan har kim sizning nomingizdan to'lov ham yarata oladi.
Yo'l parametri
ParametrTipMajburiyTavsif
merchant_idstring✓ HaKassa ID — bearer token egasi bo'lgan kassa

Eski ko'rinish ham ishlaydi: /balance/?merchant_id=94055. Ikkalasi bir xil javob qaytaradi.

cURL
curl -X GET "https://inpay.uz/api/v1/info/merchant/94055" \
  -H "Authorization: Bearer YOUR_TOKEN"
Misol
JSON · 200 OK
{
  "success": true,
  "data": {
    "merchant_id":   "94055",
    "business_name": "INSPIRE",
    "balance":       1134300
  }
}
Javob maydonlari
MaydonTipTavsif
merchant_idstringKassa ID
business_namestringKassa nomi
balancenumberHozir ishlatish va yechib olish mumkin bo'lgan summa, so'mda
Javob ataylab qisqa. API qancha kam maydon bersa, token sizib chiqqanda yo'qotiladigan ma'lumot ham shuncha kam bo'ladi. Settlement ushlab turgan summa (hold) va boshqa valyutalardagi balans bu javobga kirmaydi — ular kabinetda ko'rinadi.
Cheklovlar: daqiqasiga 60 ta so'rov. Javob keshlanmaydi — har safar bazadagi joriy qiymat qaytadi. Kassangizda IP oq ro'yxati strict rejimida bo'lsa, bu endpoint ham o'sha ro'yxatni tekshiradi.
Xatolar
HTTPerror_codeTavsif
401MISSING_AUTH_TOKENAuthorization sarlavhasi yo'q
401INVALID_TOKENToken noto'g'ri yoki u boshqa kassaga tegishli
400MISSING_MERCHANT_IDmerchant_id berilmagan yoki formati noto'g'ri
403MERCHANT_NOT_APPROVEDKassa hali tasdiqlanmagan
403MERCHANT_DISABLED_BY_ADMINKassa admin tomonidan o'chirilgan
403IP_NOT_WHITELISTED_STRICTIP manzil oq ro'yxatda yo'q (strict rejim)
405METHOD_NOT_ALLOWEDFaqat GET qabul qilinadi
429RATE_LIMIT_EXCEEDEDDaqiqadagi 60 ta chegara oshdi

Webhook bildirishnomalar

To'lov holati o'zgarganda real-time xabar

Muhim: To'lov amalga oshganda, inPAY tizimi sizning callback_url manzilingizga POST so'rov yuboradi. Handler HTTP 200 bilan javob qaytarishi shart.
Jarayon
1
Foydalanuvchi to'lovni amalga oshiradi
Checkout sahifasida to'lov tugmasi bosiladi
2
inPAY webhook yuboradi
Sizning callback_url ga POST so'rov keladi
3
Serveringiz JSON qabul qiladi
Ma'lumotlarni parse qiling va bazani yangilang
4
HTTP 200 "OK" qaytaring
Aks holda inPAY qayta urinib ko'radi
Webhook ma'lumotlari (JSON)
ParametrTipTavsif
amountstringTo'lov summasi (masalan: "15000.00")
statusstringTo'lov holati: success yoki failed
order_idstringBuyurtma identifikatori
transaction_idintegerinPAY tizimidagi tranzaksiya ID
created_atstringYaratilgan vaqt (ISO format)
JSON · POST body
{
  "amount":         "15000.00",
  "status":         "success",
  "order_id":       "1ff2f5a6d66f6e9c",
  "transaction_id": 149,
  "created_at":     "2025-12-10 05:14:52"
}
callback.php
<?php
$input = file_get_contents('php://input');
$data  = json_decode($input, true);

if (!$data) {
  http_response_code(400);
  exit('Invalid JSON');
}

if (($data['status'] ?? '') === 'success') {
  // Update order in your DB
  $pdo->prepare('UPDATE orders SET status=?, paid_at=NOW() WHERE order_id=?')
      ->execute(['paid', $data['order_id']]);
}

// Always respond 200 OK
http_response_code(200);
echo 'OK';
Express.js
const express = require('express');
const app = express();
app.use(express.json());

app.post('/payment/callback', async (req, res) => {
  const { amount, status, order_id, transaction_id } = req.body;

  if (status === 'success') {
    await db.query(
      'UPDATE orders SET status = ? WHERE order_id = ?',
      ['paid', order_id]
    );
  }

  res.status(200).send('OK');
});
Flask
from flask import Flask, request

app = Flask(__name__)

@app.route("/payment/callback", methods=["POST"])
def callback():
    data = request.get_json(silent=True) or {}

    if data.get("status") == "success":
        # update DB
        mark_paid(data["order_id"])

    return "OK", 200
  • Webhook URL ni callback_url da yuboring — aks holda kassadagi default URL ishlatiladi
  • return_url — bu faqat mijozni brauzerda saytingizga qaytarish uchun. Buyurtmani faqat webhook bo'yicha bajaring: qaytish parametrlari (order_id, status, amount, signature) ishonchli manba emas.
  • Handler JSON formatini qabul qilishi va HTTP 200 qaytarishi shart
  • Webhook URL HTTPS bo'lishi tavsiya etiladi

To'lov tugmasi (Pay Button)

Server kodisiz integratsiya — saytingizga ikki qator HTML qo'yasiz

HTML<script src="https://inpay.uz/widget.js">

API kalit ham, Bearer token ham kerak emas. Summa va dizayn inPAY tomonida saqlanadi — brauzerda o'zgartirib bo'lmaydi.

Tugmani kabinetdagi «To'lov tugmasi» bo'limida yaratasiz: nomi, summasi va dizayni tanlanadi, natijada btn_ bilan boshlanuvchi token beriladi. Saytingizga faqat shu token joylashadi. Dizaynni keyin o'zgartirsangiz sayt kodiga tegish shart emas — tugma o'zi yangilanadi.

Saytingizga qo'yiladigan kod
HTML
<script src="https://inpay.uz/widget.js"></script>
<inpay-button token="btn_xxxxxxxxxxxxxxxxxxxx"></inpay-button>
Bitta widget.js sahifadagi barcha tugmalarga yetadi — faqat <inpay-button> qatorini takrorlang. Dinamik (SPA) sahifalarda keyin qo'shilgan tugmalar ham avtomatik ishga tushadi; qo'lda ishga tushirish uchun InPay.refresh() chaqiriladi.
Domen ro'yxati majburiy: tugma faqat kassaning whitelist ro'yxatidagi domenlarda ishlaydi. Ro'yxatda bo'lmagan saytda tugma o'rniga xato yozuvi chiqadi — bu tokeningizni begona saytga ko'chirishdan himoya qiladi.
<inpay-button> atributlari
ParametrTipMajburiyTavsif
tokenstring✓ HaKabinetdan olingan tugma tokeni. Format: btn_ + 20 ta hex belgi.

Boshqa atribut yo'q — summa, matn, o'lcham, rang va belgi tokenga bog'langan holda inPAY bazasidan keladi. Shuning uchun HTML kodni ko'rgan odam summani o'zgartira olmaydi.

To'lov qanday kechadi
  1. Sahifa ochilganda widget.js GET /api/widget/button ga murojaat qilib tugma ko'rinishini oladi.
  2. Mijoz tugmani bosadi → POST /api/widget/session yuboriladi.
  3. inPAY transactions jadvalida pending tranzaksiya yaratadi (source = "widget") va /checkout/<order_id> havolasini qaytaradi.
  4. Mijoz yangi oynada to'lovni amalga oshiradi (oyna bloklansa — o'sha sahifada ochiladi).
  5. To'lov tugagach kassangizning callback_url manziliga odatdagi webhook keladi — Webhook bo'limidagi format bilan bir xil.
Vidjet orqali kelgan to'lov /api/v1/create/ orqali yaratilganidan farq qilmaydi: bir xil transactions yozuvi, bir xil order_id, bir xil webhook. Yagona farq — source maydoni "widget" bo'ladi, shu orqali sotuvni ajratib hisoblashingiz mumkin.
Dizayn imkoniyatlari
ParametrQiymatlarTavsif
styleclassic · dark · outline · gradient · minimal · white6 ta tayyor dizayn
sizes · m · lTugma balandligi: 36 / 44 / 54 px
radius0 … 40Burchak radiusi, piksel
logonone · inpay · kassaBelgisiz, inPAY logotipi yoki kassangiz logotipi
full0 · 1To'liq kenglikdagi blok tugma

Bu qiymatlar so'rovda yuborilmaydi — ular kabinetda tanlanadi va tokenga saqlanadi. Jadval javobdagi maydonlarni tushunish uchun keltirilgan.

Vidjet endpointlari

widget.js ichkarida shu ikki endpointni chaqiradi

Odatda ularni qo'lda chaqirish shart emas. Agar tugmani o'z dizayningiz bilan chizmoqchi bo'lsangiz — shu ikki so'rov yetarli.

GET/api/widget/button?token=btn_...

Tugmaning ko'rinishi va summasini qaytaradi. Summa faqat ekranda ko'rsatish uchun.

JSON · 200 OK
{
  "ok":       true,
  "title":    "Premium obuna",
  "amount":   99000,
  "currency": "UZS",
  "label":    "inPAY orqali to'lash",
  "style":    "classic",
  "size":     "m",
  "radius":   10,
  "logo":     "inpay",
  "mlogo":    "",
  "full":     0
}
POST/api/widget/session

Tranzaksiya yaratadi. Tanasi — faqat token. Summa so'rovdan olinmaydi, u bazadagi tugmadan olinadi.

cURL
curl -X POST "https://inpay.uz/api/widget/session" \
  -H "Content-Type: application/json" \
  -H "Origin: https://sizning-saytingiz.uz" \
  -d '{"token":"btn_xxxxxxxxxxxxxxxxxxxx"}'
JSON · 200 OK
{
  "ok":       true,
  "order_id": "9f3c1a7b25e40d68",
  "url":      "https://inpay.uz/checkout/9f3c1a7b25e40d68"
}
Vidjet xatolari
HTTPerrorTavsif
400tokenToken yuborilmagan
404not_foundBunday tugma yo'q yoki o'chirilgan
403originDomen kassaning whitelist ro'yxatida emas
403kassaKassa tasdiqlanmagan yoki faol emas
400amountTugma summasi belgilanmagan
405method/api/widget/session faqat POST qabul qiladi

CardSystem — o'z saytingizda karta qabul qilish

Karta ma'lumoti sizning formangizda kiritiladi, SMS kod bilan tasdiqlanadi

POST/api/v1/cardsystem/create
Ikki yo'lning farqi
Hosted checkoutCardSystemCard System (avto to'lov)
Karta kim qabul qiladiinPAY sahifasiSizning formangizinPAY sahifasi (bir marta)
Har to'lovda SMS kodHaHaYo'q
PCI mas'uliyatiinPAY'daSizdainPAY'da
QachonEng oddiy, tavsiya etiladiTo'liq o'z dizayni kerak bo'lsaObuna, takroriy to'lov

Tarif: CardSystem Premium va Business tarifida avtomatik ochiladi. Standard tarifda hosted checkout (/api/v1/create) ishlatiladi yoki admindan alohida ruxsat so'raladi.

Diqqat: CardSystem'da karta raqami sizning serveringizdan o'tadi — uni hech qachon saqlamang va logga yozmang, faqat HTTPS orqali uzating. Agar shu talab sizga og'ir bo'lsa — hosted checkout (/api/v1/create) ni tanlang.

Oqim
Flow
1. POST /api/v1/create              → order_id olinadi (bearer token bilan)
2. POST /api/v1/cardsystem/create   → karta yuboriladi, mijozga SMS ketadi → hash_token
3. POST /api/v1/cardsystem/verify   → hash_token + SMS kod → to'lov yakunlanadi
4. webhook                          → serveringizga natija keladi
2-qadam · create — Body (JSON)
ParametrTipMajburiyTavsif
cardsystem_order_idstring✓ Ha1-qadamda olingan order_id
card_numberstring✓ Ha16 raqam, probelsiz
exp_monthint✓ Ha1–12
exp_yearint✓ HaIkki raqam (masalan 29)
Javob
JSON · 200 OK
{
  "success": true,
  "message": "SMS kod yuborildi",
  "data": {
    "cardsystem_order_id": "1ff2f5a6d66f6e9c",
    "hash_token":          "a91c…",
    "amount":              15000,
    "phone":               "+998 90 *** ** 12",
    "next_step":           "/api/v1/cardsystem/verify"
  }
}
3-qadam · verify
POST /api/v1/cardsystem/verify
{ "hash_token": "a91c…", "otp_code": "123456" }

// 200 OK
{ "success": true, "message": "To'lov muvaffaqiyatli",
  "data": { "cardsystem_order_id": "1ff2f5a6d66f6e9c", "amount": 15000,
            "status": "success", "paid_at": "2026-07-27 09:20:11" } }
Cheklovlar va himoya
NimaQiymatNima bo'ladi
So'rov tezligi30 / daqiqa / IPHTTP 429
OTP urinishlari5 / 5 daqiqaSessiya bloklanadi
Sessiya muddati15 daqiqaQaytadan boshlash kerak
Firibgarlik qalqoniHar so'rovdaKarta sinash / tezlik / yangi kassa chegarasi — karta bankka umuman yuborilmaydi (HTTP 403)
Yangi kassa mablag'iT+7 kunPul avval «kutilayotgan balans»da turadi (chargeback oynasi)

Avto to'lov — tokenlangan karta

Mijoz kartasini bir marta bog'laydi, keyingi to'lovlar kod so'ramasdan yechiladi

POST/api/v1/cards/{bind|confirm|charge|remove|list}
Nima uchun kerak

Obuna, bo'lib to'lash, muddatli xizmat, avtomatik to'ldirish — mijoz har safar karta raqami va SMS kodini kiritmasligi kerak bo'lgan holatlar uchun. Karta bir marta bog'lanadi, siz keyin faqat card_id va summa yuborasiz.

Qanday yoqiladi

Avto to'lov tarif bilan avtomatik yoqilmaydi — u har bir kassa uchun alohida ariza asosida ochiladi. Sabab oddiy: bu imkoniyat mijoz kartasidan SMS kodsiz pul yechish huquqini beradi, shuning uchun har bir kassa qo'lda ko'rib chiqiladi.

QadamKim bajaradiNatija
1. ArizaSavdogar — kabinet → Avto to'lov Maqsad, sayt, kutilayotgan hajm, aloqa
2. Ko'rib chiqishinPAY admini Odatda 1 ish kuni
3. TasdiqAdmin Chegaralar (bitta to'lov / kunlik / oylik) va rejim belgilanadi
4. KalitSavdogar o'zi yaratadi key_id + secretbir marta ko'rsatiladi

Chegaralarni admin belgilaydi — tarif faqat taklif qilinadigan boshlang'ich qiymatga ta'sir qiladi. Shubhali holatda imkoniyat to'xtatilishi va kalit bekor qilinishi mumkin.

Chalkashtirmang: tarif bo'yicha avtomatik ochiladigan narsa — bu CardSystem (o'z saytingizda karta qabul qilish), avto to'lov emas.

Oqim
Flow
1. bind      → mijozga form_url beriladi (inpay.uz/cards/bind/<kassa>/<token>)
2. mijoz     → o'sha sahifada karta + SMS kodni kiritadi, rozilik beradi
3. karta     → status active, sizda faqat card_id qoladi (karta raqami YO'Q)
4. charge    → istalgan payt: card_id + amount + idem_key + reason
5. mijozga   → har yechimdan keyin SMS ketadi, u inpay.uz/cards/my da bekor qila oladi
Asosiy qoidalar
  • Karta raqami sizga hech qachon ko'rinmaydi — mijoz uni inPAY sahifasida kiritadi, sizga token emas, card_id qaytadi.
  • Token bazada AES-256-GCM bilan shifrlanadi; kalit veb-ildizdan tashqarida.
  • Har yechishda reason majburiy — mijoz «nimaga yechildi?» deganda javob shu yerdan chiqadi.
  • Har karta uchun mandat raqami (mandate_id) beriladi — nizoda shu raqam bo'yicha isbot qilinadi.
  • Mijoz bog'lashda rozilik chegarasini belgilashi mumkin — undan yuqori summa rad etiladi.
  • Har amal o'zgartirib bo'lmaydigan xesh-zanjirli jurnalga yoziladi.

Kalit va imzo

Card System kassa kalitidan ALOHIDA kalit ishlatadi — har so'rov imzolanadi

HMACX-Inpay-Signature
Headers
HeaderTipMajburiyTavsif
X-Inpay-Keystring✓ HaKalit ID (ap_...)
X-Inpay-Timestampint✓ HaUnix vaqt. ±300 soniyadan chetlashsa rad etiladi
X-Inpay-Noncestring✓ HaTakrorlanmas satr — bir marta ishlatiladi (replay himoyasi)
X-Inpay-Signaturehex✓ HaHMAC-SHA256 imzo (pastda)
Content-Typestring✓ Haapplication/json
Manzil ko'rinishlari

Ikkala ko'rinish ham ishlaydi — birini tanlang va imzoda o'sha yo'lni ishlating:

Ko'rinishManzilImzodagi PATH
Yo'l (tavsiya)POST /api/v1/cards/bind/api/v1/cards/bind
ParametrPOST /api/v1/cards/?action=bind/api/v1/cards/
Imzo formulasi
Signature base
signature = HMAC_SHA256(secret, METHOD + "\n" + PATH + "\n" + TS + "\n" + NONCE + "\n" + sha256(BODY))

METHOD = "POST"  (list uchun "GET")
PATH   = "/api/v1/cards/bind"qaysi manzilga so'rov yuborsangiz, imzoda ham SHU yo'l
         (?query qismi imzoga kirmaydi)
TS     = X-Inpay-Timestamp    NONCE = X-Inpay-Nonce
BODY   = so'rov tanasi (bo'sh bo'lsa ham sha256'si olinadi)
PHP
<?php
function inpayCards($action, $body, $keyId, $secret) {
  $path  = "/api/v1/cards/{$action}";   // bind | confirm | charge | remove | list
  $json  = json_encode($body, JSON_UNESCAPED_UNICODE);
  $ts    = time();
  $nonce = bin2hex(random_bytes(12));
  $base  = "POST\n{$path}\n{$ts}\n{$nonce}\n" . hash('sha256', $json);
  $sig   = hash_hmac('sha256', $base, $secret);

  $ch = curl_init("https://inpay.uz{$path}");
  curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
      'Content-Type: application/json',
      "X-Inpay-Key: {$keyId}",
      "X-Inpay-Timestamp: {$ts}",
      "X-Inpay-Nonce: {$nonce}",
      "X-Inpay-Signature: {$sig}",
    ],
  ]);
  $res = json_decode(curl_exec($ch), true);
  curl_close($ch);
  return $res;
}
Python
import time, json, hmac, hashlib, secrets, requests

def inpay_cards(action, body, key_id, secret):
    path  = f'/api/v1/cards/{action}'
    raw   = json.dumps(body, ensure_ascii=False)
    ts    = str(int(time.time()))
    nonce = secrets.token_hex(12)
    base  = f"POST\n{path}\n{ts}\n{nonce}\n" + hashlib.sha256(raw.encode()).hexdigest()
    sig   = hmac.new(secret.encode(), base.encode(), hashlib.sha256).hexdigest()
    return requests.post(
        f"https://inpay.uz{path}", data=raw.encode(),
        headers={'Content-Type': 'application/json', 'X-Inpay-Key': key_id,
                 'X-Inpay-Timestamp': ts, 'X-Inpay-Nonce': nonce,
                 'X-Inpay-Signature': sig}, timeout=30).json()
Node.js
const crypto = require('crypto');

async function inpayCards(action, body, keyId, secret) {
  const path  = `/api/v1/cards/${action}`;
  const raw   = JSON.stringify(body);
  const ts    = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomBytes(12).toString('hex');
  const hash  = crypto.createHash('sha256').update(raw).digest('hex');
  const sig   = crypto.createHmac('sha256', secret)
                      .update(`POST\n${path}\n${ts}\n${nonce}\n${hash}`).digest('hex');

  const r = await fetch(`https://inpay.uz${path}`, {
    method: 'POST', body: raw,
    headers: {
      'Content-Type': 'application/json', 'X-Inpay-Key': keyId,
      'X-Inpay-Timestamp': ts, 'X-Inpay-Nonce': nonce, 'X-Inpay-Signature': sig,
    },
  });
  return r.json();
}

1. Kartani bog'lash

Mijozga bir martalik xavfsiz sahifa havolasi beriladi

POST/api/v1/cards/bind
Body (JSON)
ParametrTipMajburiyTavsif
customer_refstring✓ HaMijozning sizdagi ID'si (64 belgigacha). Keyin shu bo'yicha kartalarini topasiz
return_urlstringYo'qBog'langach mijoz qaytadigan manzil
Javob
JSON · 200 OK
{
  "success": true,
  "data": {
    "card_id":   128,
    "bind_ref":  "4ce13e39073e13ce98295cb88b89ccef",
    "need_code": true,
    "form_url":  "https://inpay.uz/cards/bind/71337/4ce13e39073e13ce98295cb88b89ccef"
  }
}
form_url — nima qilish kerak

Mijozni shu havolaga yo'naltiring (yoki SMS/xabar bilan yuboring). Sahifa bank darajasida himoyalangan: birinchi ochilgan brauzerga mixlanadi (boshqa qurilmada ochilmaydi), nusxa-joylashtirish taqiqlangan, kod bitta-bittalab kiritiladi va bitta xato koddan keyin havola o'ladi. Havola 30 daqiqa amal qiladi. Bekor bo'lsa — yangi bind so'rovini yuboring.

Agar o'z ilovangizdan kod kiritmoqchi bo'lsangiz — ?action=confirm bilan {card_id, code} yuborasiz. Lekin tavsiya etilgan yo'l — form_url, chunki unda karta raqami umuman sizning tizimingizdan o'tmaydi.

2. Pul yechish

Bog'langan kartadan kod so'ramasdan yechish

POST/api/v1/cards/charge
Body (JSON)
ParametrTipMajburiyTavsif
card_idint✓ Habind qaytargan ID
amountint✓ HaSumma, so'mda (tiyinsiz)
idem_keystring✓ HaTakroriy yechishning oldini oladi. Xuddi shu kalit qayta kelsa — eski natija qaytadi, pul ikki marta yechilmaydi
reasonstring✓ HaNima uchun yechilayotgani (kamida 5 belgi). Mijozga SMS'da shu matn ketadi
order_refstringYo'qSizdagi buyurtma raqami
descriptionstringYo'qQo'shimcha izoh (provayder yozuvida)
Javob
JSON · 200 OK
{
  "success": true,
  "data": {
    "charge_id":    4412,
    "status":       "success",
    "amount":       49000,
    "provider_ref": "PLM-90188233",
    "mandate_id":   "MND-7K3D9F2A"
  }
}
Misol
PHP
$res = inpayCards('charge', [
  'card_id'   => 128,
  'amount'    => 49000,
  'idem_key'  => 'sub-2026-08-user-341',   // oy + mijoz → takrorlanmaydi
  'reason'    => 'Avgust oyi obunasi',
  'order_ref' => 'ORD-9912',
], $keyId, $secret);

if ($res['success']) {
  // $res['data']['charge_id'] ni saqlang — nizoda shu bo'yicha tekshiriladi
}
Yechish qachon rad etiladi
KodHTTPSabab
REASON_REQUIRED400reason yo'q yoki 5 belgidan qisqa
IDEM_KEY400idem_key yuborilmagan
CONSENT_LIMIT400Mijoz bog'lashda belgilagan chegaradan oshdi
LIMIT_TX / LIMIT_DAY / LIMIT_MONTH400Tarif yoki admin chegarasidan oshdi
CARD_STATE400Karta faol emas (mijoz bekor qilgan yoki bloklangan)
CHARGE_FAILED402Bank rad etdi (mablag' yetarli emas, karta yopiq va h.k.)
SIGNATURE / TIMESTAMP / NONCE401Imzo noto'g'ri, vaqt farqi >300s yoki nonce takrorlandi

3. Ro'yxat va bekor qilish

Mijozning bog'langan kartalari

GET/api/v1/cards/list?customer_ref=...
list — javob
JSON · 200 OK
{
  "success": true,
  "data": { "cards": [
    {
      "id":            128,
      "customer_ref":  "user-341",
      "masked_pan":    "860038******1234",
      "expiry":         "09/29",
      "status":         "active",
      "mandate_id":     "MND-7K3D9F2A",
      "consent_max":    100000,
      "last_used_at":   "2026-07-27 09:14:02"
    }
  ] }
}
remove — bog'lanishni bekor qilish
POST /api/v1/cards/remove
{ "card_id": 128 }

// javob: { "success": true, "data": { "card_id": 128, "status": "removed" } }
// token darhol o'chiriladi — qayta tiklab bo'lmaydi
Botingizda «Kartani uzish» tugmasi — majburiy

Mijoz kartani sizning botingiz/ilovangiz orqali bog'lagan bo'lsa, uni o'sha joydan uza olishi kerak. Bu shartnoma talabi: kartani bog'lash oson, uzish qiyin bo'lgan xizmat mijoz shikoyatiga va kassaning muzlatilishiga olib keladi.

Bot menyusi uchun
// «Kartani uzish» tugmasi bosilganda:
POST /api/v1/cards/remove   { "card_id": 128 }

// Mijozga javob:
"Kartangiz uzildi. Bundan keyin avtomatik to'lov amalga oshmaydi."
Mijoz inPAY orqali bekor qilsa

Karta egasida inPAY akkaunti bo'lmasa ham u inpay.uz/kartam sahifasidan telefon raqami + SMS kod bilan kirib bog'lanishni bekor qila oladi. Bu havola har bog'lash va har yechim SMS'ida yuboriladi. Shundan keyin charge so'rovingiz CARD_STATE xatosi bilan qaytadi — bu normal holat, mijozdan qayta bog'lashni so'rang.

Xato kodlari

API javob xatolari va ularning ma'nosi

Xato kodiHTTPTavsif
MISSING_AUTH_TOKEN401Authorization token topilmadi
INVALID_TOKEN401Bearer token noto'g'ri yoki muddati tugagan
MISSING_MERCHANT_ID400merchant_id parametri topilmadi
MERCHANT_NOT_FOUND404Merchant topilmadi
IP_NOT_WHITELISTED_STRICT403IP manzil whitelist da yo'q (Strict mode)
RATE_LIMIT_EXCEEDED429So'rovlar soni limitdan oshdi (100/soat)
CALLBACK_NOT_WHITELISTED403Callback URL whitelist da yo'q
MERCHANT_WEBSITE_NOT_WHITELISTED403Merchant website whitelist da active emas
AMOUNT_TOO_LOW400Summa juda kam (min: 1 000 so'm)
AMOUNT_TOO_HIGH400Summa maksimal limitdan oshdi
TRANSACTION_SAVE_FAILED500Tranzaksiya saqlanmadi (server xatosi)
Xato javobi namunasi
JSON
{
  "success":    false,
  "message":    "Minimal to'lov summasi 1000 so'm",
  "error_code": "AMOUNT_TOO_LOW"
}
  • Har doim success maydonini tekshiring, so'ng error_code asosida xatolarni boshqaring
  • 401/403 xatolarida autentifikatsiyani qayta tekshiring
  • 429 xatolarida biroz kuting va qayta urinib ko'ring
  • 500 xatolarida support bilan bog'laning: @merchants_uz

Xavfsizlik

IP Whitelist, Rate Limiting va token himoyasi

IP Whitelist rejimlari
strict
IP whitelist majburiy. Faqat ro'yxatdagi IP lardan so'rov qabul qilinadi.
optional
Whitelist ixtiyoriy. Barcha IP lardan so'rov qabul qilinadi.
disabled
IP tekshiruvi o'chirilgan. Test rejimi uchun.
Rate Limiting

Har bir IP manzil uchun soatiga 100 ta so'rov limiti. Limit oshirilsa, RATE_LIMIT_EXCEEDED xatosi qaytariladi.

Bearer Token xavfsizligi
  • Token 24 soat amal qiladi — keshda saqlang
  • Tokenni server-side saqlang, hech kimga bermang
  • Har so'rovda Authorization: Bearer {token} headerini yuboring
  • Muddati tugagandan so'ng yangi token oling
Domain Whitelist

Callback URL va merchant website whitelist da bo'lishi kerak. Sozlamalar uchun inPAY platformasiga kiring.

Eng yaxshi amaliyotlar

Ishonchli integratsiya uchun tavsiyalar

Bearer tokenni keshda saqlang
Har so'rovda yangi token olish o'rniga, tokenni 24 soat davomida keshda saqlang va muddati tugaganda yangilang.
Webhookdan foydalaning
To'lov holatini doimiy polling qilish o'rniga webhook orqali real-time bildirishnomalarni qabul qiling. Bu aniqroq va tejamkor.
Xatolarni to'g'ri boshqaring
Har doim error_code ni tekshiring va foydalanuvchiga tushunarli xabar ko'rsating. Barcha so'rovlarni loglang.
Tranzaksiyalarni bazada saqlang
order_id va tranzaksiya ma'lumotlarini o'z bazangizda saqlang. Webhook kelganda bazani yangilang.
HTTPS dan foydalaning
Webhook URL va callback URL lar HTTPS protokolida bo'lishi kerak. HTTP so'rovlar rad etiladi.
Test muhitida sinab ko'ring
Production ga o'tishdan oldin barcha funksiyalarni test muhitida sinab ko'ring: to'lov yaratish, webhook, status tekshirish.