Mundarija
Boshlash
Asosiy manzil (base URL)
https://api.msavdogar.uz
Format
JSON · UTF-8 · vaqt ISO 8601 (UTC)
Odatiy to'lov oqimi to'rt qadamdan iborat:
- Serveringiz
POST /v1/invoicesorqali invoys yaratadi va javobdagipay_urlni oladi. - Xaridor
pay_urlsahifasiga yo'naltiriladi va Click, bank kartasi yoki balans orqali to'laydi. - To'lov yakunlangach, MSavdogar serveringizga
payment.paidwebhookini yuboradi. - Ixtiyoriy: buyurtmani yakunlashdan oldin
GET /v1/invoices/{number}orqali holatni qayta tekshiring.
Barcha summalar so'mda (UZS) va matn ko'rinishidagi o'nlik son sifatida qaytariladi, masalan "150000.00". So'rovda summani son sifatida yuborishingiz mumkin.
Autentifikatsiya
Har bir so'rov X-Api-Key sarlavhasida API kalitini yuborishi kerak. Kalitni Do'kon panelining API bo'limida yaratasiz; to'liq kalit faqat yaratilgan paytda bir marta ko'rsatiladi.
X-Api-Key: msk_a1b2c3_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Muqobil ravishda Authorization: Bearer <API kalit> sarlavhasini ishlatishingiz mumkin.
Kalitni maxfiy saqlang
API kalitini faqat serveringizda saqlang. Uni brauzer, mobil ilova yoki ochiq repozitoriyga joylamang. Kalit oshkor bo'lsa, panelda darhol bekor qiling va yangisini yarating.
Limit: har bir kalit uchun daqiqasiga 300 ta so'rov. Limitdan oshganda 429 javobi qaytadi.
Invoys yaratish
Belgilangan summaga to'lov so'rovi (invoys) yaratadi. Muvaffaqiyatli javob — 201 Created va invoys obyekti.
| Maydon | Turi | Tavsif |
|---|---|---|
amount majburiy | son | To'lov summasi, UZS, noldan katta. Minimal summa platforma sozlamasida belgilanadi (standart: 1 000 UZS), ko'pi bilan 100 000 000 UZS; chegaradan tashqari summa uchun 400 qaytadi. Son yoki matn ("150000") bo'lishi mumkin. |
order_id | matn, ≤128 | Tizimingizdagi buyurtma raqami. Webhookda order_id (va eski nom merchant_order_id) sifatida qaytadi; GET /v1/invoices?order_id= bilan qidirish mumkin. |
title | matn, ≤200 | To'lov sahifasida va chekda ko'rinadigan nom. |
comment | matn, ≤300 | Qo'shimcha izoh. |
customer_email | matn, ≤200 | Xaridor emaili. Invoys chekida va do'kon panelidagi invoys ma'lumotlarida ko'rinadi. |
return_url | matn, ≤512 | Faqat https:// manzil (login-parolsiz). To'lov sahifasida «Do'konga qaytish» tugmasi shu manzilga olib boradi; noto'g'ri manzil uchun 422 qaytadi. |
expires_minutes | son | Invoys amal qilish muddati, daqiqada: 5 – 43200. Standart: 1440 (24 soat). |
idempotency_key | matn, ≤128 | Takroriy so'rovdan himoya (batafsil): bir xil kalit bilan qayta yuborilgan so'rov yangi invoys yaratmaydi, mavjudini qaytaradi. |
metadata | obyekt (matn → matn) | O'zingizning ma'lumotlaringiz: ko'pi bilan 10 ta kalit, kalit ≤40, qiymat ≤200 belgi. Invoys obyekti va webhooklarda metadata sifatida qaytadi. |
curl -X POST https://api.msavdogar.uz/v1/invoices \
-H "X-Api-Key: $MSAVDOGAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 150000,
"order_id": "ORDER-1024",
"title": "Premium obuna (1 oy)",
"customer_email": "mijoz@example.com",
"return_url": "https://example.uz/order/1024",
"idempotency_key": "order-1024"
}'
import os
import requests
API = "https://api.msavdogar.uz"
HEADERS = {"X-Api-Key": os.environ["MSAVDOGAR_API_KEY"]}
resp = requests.post(f"{API}/v1/invoices", headers=HEADERS, timeout=15, json={
"amount": 150000,
"order_id": "ORDER-1024",
"title": "Premium obuna (1 oy)",
"customer_email": "mijoz@example.com",
"return_url": "https://example.uz/order/1024",
"idempotency_key": "order-1024",
})
resp.raise_for_status()
invoice = resp.json()
# Xaridorni shu manzilga yo'naltiring
print(invoice["number"], invoice["pay_url"])
Javob namunasi
{
"number": "PAY_MYSHOP_7K2M9Q",
"status": "pending",
"kind": "api",
"amount": "150000.00",
"tip_amount": "0.00",
"total_amount": "150000.00",
"currency": "UZS",
"title": "Premium obuna (1 oy)",
"comment": null,
"order_id": "ORDER-1024",
"fee_percent": null,
"net_amount": null,
"pay_url": "https://pay.msavdogar.uz/pay/PAY_MYSHOP_7K2M9Q",
"created_at": "2026-10-18T09:05:12.481203+00:00",
"paid_at": null,
"expires_at": "2026-10-19T09:05:12.481203+00:00",
"payment_method": null,
"metadata": {}
}
Invoys holati
Invoysning joriy holatini qaytaradi. Faqat o'z do'koningiz invoyslarini ko'ra olasiz; boshqa invoys uchun 404 qaytadi.
curl https://api.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q \ -H "X-Api-Key: $MSAVDOGAR_API_KEY"
resp = requests.get(f"{API}/v1/invoices/PAY_MYSHOP_7K2M9Q", headers=HEADERS, timeout=15)
invoice = resp.json()
if invoice["status"] == "paid":
mark_order_paid(invoice["order_id"])
Javob namunasi
{
"number": "PAY_MYSHOP_7K2M9Q",
"status": "paid",
"kind": "api",
"amount": "150000.00",
"tip_amount": "0.00",
"total_amount": "150000.00",
"currency": "UZS",
"title": "Premium obuna (1 oy)",
"comment": null,
"order_id": "ORDER-1024",
"fee_percent": "5.00",
"net_amount": "142500.00",
"pay_url": "https://pay.msavdogar.uz/pay/PAY_MYSHOP_7K2M9Q",
"created_at": "2026-10-18T09:05:12.481203+00:00",
"paid_at": "2026-10-18T09:07:40.877120+00:00",
"expires_at": "2026-10-19T09:05:12.481203+00:00",
"payment_method": "click",
"metadata": {}
}
Invoyslar ro'yxati
Filtrlar: status (holat), order_id (sizning buyurtma raqamingiz, aniq moslik), limit (1 – 200).
Eng yangi invoyslar ro'yxati, {"items": [...]} ko'rinishida.
| Parametr | Tavsif |
|---|---|
status | Ixtiyoriy filtr: holatlardan biri (masalan pending, paid). Noma'lum qiymat bo'sh ro'yxat qaytaradi. |
limit | Natijalar soni, 1 – 200 (chegaradan tashqari qiymat shu oraliqqa keltiriladi). Standart: 50. |
curl "https://api.msavdogar.uz/v1/invoices?status=paid&limit=20" \ -H "X-Api-Key: $MSAVDOGAR_API_KEY"
{
"items": [
{ "number": "PAY_MYSHOP_7K2M9Q", "status": "paid", "...": "invoys obyekti, yuqoridagidek" }
]
}
Invoysni bekor qilish
Hali to'lanmagan (pending) invoysni bekor qiladi va yangilangan invoys obyektini qaytaradi. Boshqa holatdagi invoys uchun 409 ({"detail": "Invoice is paid."} kabi) qaytadi. Muvaffaqiyatda 200 va "status": "cancelled" bo'lgan invoys obyekti qaytadi.
curl -X POST https://api.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q/cancel \ -H "X-Api-Key: $MSAVDOGAR_API_KEY"
Invoysni qayta ochish
To'lanmagan invoysga o'sha raqam bilan yangi muddat beradi: holat pending ga qaytadi, expires_at = hozir + expires_minutes. Muddati o'tgan (expired), bekor qilingan (cancelled) yoki hali pending invoysni qayta ochish mumkin; pay_url o'zgarmaydi. To'langan, qaytarilgan yoki qaytarilayotgan (paid, refunded, refund_pending) invoys uchun 409 qaytadi. Faqat API orqali yaratilgan invoyslar qayta ochiladi.
| Maydon (ixtiyoriy tana) | Tavsif |
|---|---|
expires_minutes | 5 – 43200, standart 1440 (24 soat). |
curl -X POST https://api.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q/reopen \
-H "X-Api-Key: $MSAVDOGAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"expires_minutes": 1440}'
Muvaffaqiyatda 200 va yangilangan invoys obyekti qaytadi. Qayta ochish alohida webhook yubormaydi; keyingi to'lov xuddi shu raqam bilan payment.paid yuboradi.
Do'kon ma'lumotlari
API kaliti tegishli bo'lgan do'kon va uning hisobidagi mablag'lar holati.
{
"username": "myshop",
"business_name": "My Shop",
"kyc_status": "verified",
"wallet": {
"held": "450000.00",
"available": "1250000.00",
"payout_pending": "0.00"
}
}
| Maydon | Ma'nosi |
|---|---|
wallet.held | Yaqinda tushgan, xavfsizlik muddati tugashini kutayotgan mablag'. |
wallet.available | Yechib olish mumkin bo'lgan mablag'. |
wallet.payout_pending | Pul yechish so'rovlarida turgan mablag'. |
kyc_status | unverified, pending, verified yoki rejected. |
Invoys obyekti
| Maydon | Tavsif |
|---|---|
number | Invoysning noyob raqami, masalan PAY_MYSHOP_7K2M9Q. |
status | Holat (quyidagi jadvalga qarang). |
kind | Invoys turi; API orqali yaratilganlar uchun api. Webhookda boshqa turlar ham bo'lishi mumkin (masalan, do'kon sahifasidan sotilgan product). |
amount | Asosiy summa. |
tip_amount | Xaridor qo'shgan ixtiyoriy ustama. |
total_amount | Xaridordan yechilgan jami summa (amount + tip_amount). |
currency | Har doim UZS. |
title, comment | Invoys nomi va izohi. |
order_id | Siz yuborgan buyurtma raqami. |
metadata | Siz yuborgan metadata (yuborilmagan bo'lsa {}). |
fee_percent, net_amount | Xizmat haqi foizi va sizga tushadigan sof summa (amount − xizmat haqi + tip_amount) — to'lovdan so'ng to'ldiriladi, undan oldin null. |
pay_url | Xaridor uchun to'lov sahifasi. |
created_at, paid_at, expires_at | Vaqt belgilari (ISO 8601, UTC). |
payment_method | To'lovdan oldin null; keyin click, card, card_token yoki balance. |
Holatlar
| Holat | Ma'nosi |
|---|---|
pending | To'lov kutilmoqda. |
paid | To'langan — buyurtmani bajarishingiz mumkin. |
cancelled | Bekor qilingan. |
expired | Muddati o'tgan, to'lab bo'lmaydi. |
refund_pending | Qaytarish boshlangan, to'lov provayderi javobi kutilmoqda. Provayder rad etsa, invoys paid holatiga qaytadi. Click ilovasi/sahifasi orqali qilingan to'lovni API orqali qaytarib bo'lmaydi: bunda qaytarish administrator pulni Click kabinetida qo'lda qaytarib tasdiqlaguncha refund_pending da turadi, so'ng refunded bo'ladi va payment.refunded yuboriladi. |
refunded | Pul xaridorga qaytarilgan. |
Takroriy so'rovlar (idempotency)
POST /v1/invoices so'rovida idempotency_key (≤128 belgi) yuboring, masalan buyurtma raqamingiz. Kalit har bir do'kon doirasida noyob:
- Birinchi so'rov yangi invoys yaratadi va
201qaytaradi. - Xuddi shu kalit bilan keyingi so'rov yangi invoys yaratmaydi — mavjud invoysni (xuddi shu
numbervapay_url) qaytaradi. - Invoys raqami (
number) hech qachon o'zgarmaydi: xuddi shu kalit har doim asl invoysni qaytaradi — uning holati qanday bo'lishidan qat'i nazar (pending,paid,expired,cancelled,refunded). Muddati o'tgan invoys uchun yangi invoys yaratilmaydi — uni qayta oching. - Kalit bir xil, lekin
amountyokiorder_idboshqa bo'lsa — bu qayta urinish emas, xato:409va{"code": "idempotency_mismatch", "number": "...", "detail": "..."}qaytadi, hech narsa yaratilmaydi. Boshqa summa yoki buyurtma uchun yangi kalit ishlating (title,commentva boshqa maydonlar solishtirilmaydi).
Tarmoq xatosi yoki 5xx bo'lganda so'rovni shu kalit bilan xavfsiz qayta yuborishingiz mumkin. Kalitsiz har bir so'rov yangi invoys yaratadi.
Raqam barqarorligi: to'lov urinishi muvaffaqiyatsiz bo'lsa (karta rad etildi, noto'g'ri SMS kod, Click xatosi), invoys ham, uning raqami ham o'zgarmaydi — xaridor shu pay_url da qayta urinadi. Barcha webhooklar (payment.paid, payment.expired, payment.cancelled, payment.refunded) boshidan oxirigacha bir xil number ni ko'rsatadi, shuning uchun tizimingiz yangi raqamni o'rganishi shart emas.
Xatolar
Xato javoblari standart HTTP kodlari va {"detail": "..."} ko'rinishidagi tana bilan qaytadi (422 da detail — maydonlar bo'yicha xatolar ro'yxati).
| Kod | Sabab |
|---|---|
400 | So'rov bajarilmadi, masalan summa ruxsat etilgan chegaradan tashqarida. |
401 | API kaliti yuborilmagan, noto'g'ri yoki bekor qilingan. |
403 | Do'kon faol emas. |
404 | Invoys topilmadi yoki boshqa do'konga tegishli. |
409 | Invoys holati amalga ruxsat bermaydi (masalan, to'langan invoysni bekor qilish yoki qayta ochish) yoki idempotency_key boshqa summa/order_id bilan ishlatilgan ("code": "idempotency_mismatch"). |
422 | Maydonlar talablarga mos emas (turi, uzunligi yoki qiymati). |
429 | So'rovlar limiti oshib ketdi — birozdan so'ng qayta urining. |
5xx | Vaqtinchalik nosozlik. idempotency_key bilan qayta urinib ko'ring. |
Webhooklar
Do'kon panelining API bo'limida webhook manzilini kiriting va webhookni yoqing; maxfiy kalit (secret) panelda ko'rsatiladi. Har bir hodisada MSavdogar shu manzilga POST so'rov yuboradi (User-Agent: MSavdogar-Webhook/4.0).
Manzilga qo'yiladigan talablar
- Faqat
https://; login-parol URL ichida bo'lmasligi kerak. - Port faqat 443 (standart) yoki 8443.
- Host ochiq (global) IP manzilga ega bo'lishi kerak:
localhost, ichki va xususiy tarmoq manzillari (masalan10.x,192.168.x,127.x) qabul qilinmaydi. Tekshiruv manzil saqlanganda ham, har bir yuborishda ham DNS orqali qayta bajariladi. - Yo'naltirishlar (
3xx) kuzatilmaydi.
| Hodisa | Qachon yuboriladi |
|---|---|
payment.paid | Do'koningizga tegishli istalgan invoys to'langanda (API invoyslari ham, do'kon sahifasidagi sotuvlar ham — turini data.kind ko'rsatadi). |
payment.refunded | To'lov xaridorga muvaffaqiyatli qaytarilganda. |
payment.cancelled | API orqali yaratilgan invoys bekor qilinganda (POST /v1/invoices/{number}/cancel). |
payment.expired | API orqali yaratilgan invoysning muddati to'lovsiz tugaganda. |
Sarlavhalar
| Sarlavha | Qiymat |
|---|---|
X-MSavdogar-Event | Hodisa nomi, masalan payment.paid. |
X-MSavdogar-Timestamp | Yuborilgan vaqt, Unix soniyalarda. Har bir urinishda yangilanadi. |
X-MSavdogar-Signature | HMAC-SHA256(secret, timestamp + "." + body), hex ko'rinishida. |
X-MSavdogar-Delivery | Yetkazish ID raqami; bir hodisaning barcha urinishlarida bir xil — takroriy qayta ishlashdan himoya uchun. |
Content-Type | application/json |
payment.paid namunasi
{
"event": "payment.paid",
"created_at": "2026-10-18T09:07:41.102938+00:00",
"data": {
"invoice": "PAY_MYSHOP_7K2M9Q",
"number": "PAY_MYSHOP_7K2M9Q",
"kind": "api",
"status": "paid",
"amount": "150000.00",
"tip_amount": "5000.00",
"total_amount": "155000.00",
"currency": "UZS",
"fee_percent": "5.00",
"net_amount": "147500.00",
"order_id": "ORDER-1024",
"merchant_order_id": "ORDER-1024",
"title": "Premium obuna (1 oy)",
"comment": null,
"customer_email": "mijoz@example.com",
"pay_url": "https://pay.msavdogar.uz/pay/PAY_MYSHOP_7K2M9Q",
"created_at": "2026-10-18T09:05:12.481203+00:00",
"paid_at": "2026-10-18T09:07:40.877120+00:00",
"refunded_at": null,
"payment_method": "click",
"metadata": {"user_id": "42"}
}
}
payment.refunded namunasi
{
"event": "payment.refunded",
"created_at": "2026-10-19T11:20:03.554210+00:00",
"data": {
"invoice": "PAY_MYSHOP_7K2M9Q",
"number": "PAY_MYSHOP_7K2M9Q",
"status": "refunded",
"order_id": "ORDER-1024",
"amount": "150000.00",
"tip_amount": "5000.00",
"total_amount": "155000.00",
"refund_amount": "155000.00",
"reason": "Xaridor so'rovi bo'yicha",
"refunded_at": "2026-10-19T11:20:03.554210+00:00",
"payment_method": "click",
"metadata": {"user_id": "42"},
"...": "invoys obyektining boshqa maydonlari ham bor (kind, currency, title, pay_url, paid_at ...)"
}
}
payment.cancelled va payment.expired hodisalarining data qismi ham shu invoys obyekti (status: cancelled / expired). Hodisa nomi X-MSavdogar-Event sarlavhasida va tanadagi event maydonida keladi. Barcha hodisalarda data.order_id — sizning buyurtma raqamingiz.
Imzoni tekshirish
Imzo: hex( HMAC-SHA256( secret, timestamp + "." + raw_body ) ), bunda secret — paneldagi webhook kaliti, timestamp — X-MSavdogar-Timestamp sarlavhasi, raw_body — so'rovning xom tanasi (JSON'ni qayta formatlamasdan, kelgan baytlarni ishlating). Imzo X-MSavdogar-Signature sarlavhasida kichik harfli hex ko'rinishida keladi, prefikssiz. Solishtirishda doimiy vaqtli taqqoslashdan foydalaning. Eski so'rovlar qayta yuborilishining oldini olish uchun vaqt belgisini ham tekshiring (masalan, 5 daqiqadan eskisini rad eting) — bu tekshiruv sizning tomoningizda bajariladi.
import hashlib
import hmac
import os
import time
from fastapi import FastAPI, HTTPException, Request
app = FastAPI()
WEBHOOK_SECRET = os.environ["MSAVDOGAR_WEBHOOK_SECRET"].encode()
@app.post("/msavdogar/webhook")
async def msavdogar_webhook(request: Request):
raw_body = await request.body()
timestamp = request.headers.get("X-MSavdogar-Timestamp", "")
signature = request.headers.get("X-MSavdogar-Signature", "")
expected = hmac.new(WEBHOOK_SECRET, timestamp.encode() + b"." + raw_body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, signature):
raise HTTPException(status_code=400, detail="invalid signature")
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
raise HTTPException(status_code=400, detail="stale request")
event = await request.json()
if event["event"] == "payment.paid":
data = event["data"]
# Idempotent bo'ling: bir hodisa bir necha marta kelishi mumkin
mark_order_paid(data["merchant_order_id"], data["invoice"])
return {"ok": True}
const crypto = require("crypto");
const express = require("express");
const app = express();
const SECRET = process.env.MSAVDOGAR_WEBHOOK_SECRET;
app.post("/msavdogar/webhook", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-MSavdogar-Timestamp") || "";
const signature = req.get("X-MSavdogar-Signature") || "";
const expected = crypto
.createHmac("sha256", SECRET)
.update(Buffer.concat([Buffer.from(timestamp + "."), req.body]))
.digest("hex");
const valid = signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
if (!valid || !fresh) return res.status(400).end();
const event = JSON.parse(req.body.toString("utf8"));
if (event.event === "payment.paid") {
// buyurtmani to'langan deb belgilang (idempotent)
}
res.json({ ok: true });
});
<?php
$secret = getenv('MSAVDOGAR_WEBHOOK_SECRET');
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_MSAVDOGAR_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MSAVDOGAR_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $timestamp . '.' . $body, $secret);
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
http_response_code(400);
exit;
}
$event = json_decode($body, true);
if ($event['event'] === 'payment.paid') {
$invoice = $event['data']['invoice'];
// buyurtmani to'langan deb belgilang (idempotent)
}
header('Content-Type: application/json');
echo json_encode(['ok' => true]);
Javob va qayta yuborish
- Serveringiz 15 soniya ichida
2xx(200–299) kodi bilan javob berishi kerak — shundagina yetkazish muvaffaqiyatli hisoblanadi. Boshqa kod (jumladan3xx), xato yoki vaqt tugashi — muvaffaqiyatsiz urinish. Og'ir ishlarni javobdan keyin, fonda bajaring. - Jami 5 ta urinish: birinchisi darhol, keyingilari muvaffaqiyatsizlikdan so'ng mos ravishda 1 daqiqa, 5 daqiqa, 15 daqiqa va 1 soat o'tgach. Beshinchi urinish ham muvaffaqiyatsiz bo'lsa, yetkazish
faileddeb belgilanadi va avtomatik qayta yuborilmaydi. - Yetkazishlar worker tomonidan navbat bilan bajariladi, shuning uchun urinishlar ko'rsatilgan vaqtdan bir necha soniya kechikishi mumkin.
- Bir hodisa bir necha marta kelishi mumkin.
X-MSavdogar-Deliveryyokiinvoiceraqami bo'yicha takroriy ishlovni oldini oling. - Yetkazishlar tarixi (oxirgi 20 ta) va oxirgi xatolar Do'kon panelining API bo'limida ko'rinadi.
Firewall va IP manzil
Webhooklar MSavdogar serveridan quyidagi IP manzildan yuboriladi:
89.167.99.75
Serveringiz yoki hostingingiz (Cloudflare, WAF, iptables, fail2ban va h.k.) himoyasi ostida bo'lsa, shu manzilni ruxsat etilganlar ro'yxatiga (allowlist) qo'shing va bloklamang. Bloklangan webhooklar yetkazilmaydi; 5 ta urinishdan keyin to'lov haqidagi xabar sizga kelmaydi (invoys holatini baribir GET /v1/invoices/{number} orqali tekshirishingiz mumkin).
Integratsiya bo'yicha savolingiz bormi?
Qo'llab-quvvatlash xizmatiga yozing — dasturchilarimiz yordam beradi.