Mundarija
Boshlash
Asosiy manzil (base URL)
https://api-beta.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. Kamida 1 000 UZS, ko'pi bilan 100 000 000 UZS. |
order_id | matn, ≤128 | Tizimingizdagi buyurtma raqami. Webhookda merchant_order_id sifatida qaytadi. |
title | matn, ≤200 | To'lov sahifasida va chekda ko'rinadigan nom. |
comment | matn, ≤300 | Qo'shimcha izoh. |
customer_email | matn, ≤200 | Xaridor emaili — PDF chek shu manzilga yuboriladi. |
return_url | matn, ≤512 | To'lovdan so'ng xaridor qaytariladigan sahifa. |
expires_minutes | son | Invoys amal qilish muddati, daqiqada: 5 – 43200. Standart: 1440 (24 soat). |
idempotency_key | matn, ≤128 | Takroriy so'rovdan himoya: bir xil kalit bilan qayta yuborilgan so'rov yangi invoys yaratmaydi, mavjudini qaytaradi. |
curl -X POST https://api-beta.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-beta.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-beta.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
}
Invoys holati
Invoysning joriy holatini qaytaradi. Faqat o'z do'koningiz invoyslarini ko'ra olasiz; boshqa invoys uchun 404 qaytadi.
curl https://api-beta.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"])
Invoyslar ro'yxati
Eng yangi invoyslar ro'yxati, {"items": [...]} ko'rinishida.
| Parametr | Tavsif |
|---|---|
status | Ixtiyoriy filtr: pending, paid, cancelled, expired, refunded. |
limit | Natijalar soni, 1 – 200. Standart: 50. |
curl "https://api-beta.msavdogar.uz/v1/invoices?status=paid&limit=20" \ -H "X-Api-Key: $MSAVDOGAR_API_KEY"
Invoysni bekor qilish
Hali to'lanmagan (pending) invoysni bekor qiladi va yangilangan invoys obyektini qaytaradi. Boshqa holatdagi invoys uchun 409 qaytadi.
curl -X POST https://api-beta.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q/cancel \ -H "X-Api-Key: $MSAVDOGAR_API_KEY"
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. |
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. |
fee_percent, net_amount | Xizmat haqi foizi va sizga tushadigan sof summa — to'lovdan so'ng to'ldiriladi. |
pay_url | Xaridor uchun to'lov sahifasi. |
created_at, paid_at, expires_at | Vaqt belgilari (ISO 8601, UTC). |
payment_method | 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. |
refunded | Pul xaridorga qaytarilgan. |
Xatolar
Xato javoblari standart HTTP kodlari va {"detail": "..."} ko'rinishidagi tana bilan qaytadi.
| Kod | Sabab |
|---|---|
400 | So'rov bajarilmadi, masalan summa ruxsat etilgan chegaradan tashqarida. |
401 | API kaliti yuborilmagan yoki noto'g'ri. |
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). |
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 xavfsiz qayta urinishingiz mumkin. |
Webhooklar
Do'kon panelida webhook manzili (faqat ochiq https:// manzil) va maxfiy kalitni sozlang. Har bir hodisada MSavdogar shu manzilga POST so'rov yuboradi.
| Hodisa | Qachon yuboriladi |
|---|---|
payment.paid | Invoys muvaffaqiyatli to'langanda. |
payment.refunded | To'lov xaridorga qaytarilganda. |
Sarlavhalar
| Sarlavha | Qiymat |
|---|---|
X-MSavdogar-Event | Hodisa nomi, masalan payment.paid. |
X-MSavdogar-Timestamp | Yuborilgan vaqt, Unix soniyalarda. |
X-MSavdogar-Signature | HMAC-SHA256(secret, timestamp + "." + body), hex ko'rinishida. |
X-MSavdogar-Delivery | Yetkazishning noyob ID raqami — 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",
"kind": "api",
"status": "paid",
"amount": "150000.00",
"tip_amount": "5000.00",
"total_amount": "155000.00",
"currency": "UZS",
"fee_percent": "5",
"net_amount": "147500.00",
"merchant_order_id": "ORDER-1024",
"title": "Premium obuna (1 oy)",
"comment": null,
"paid_at": "2026-10-18T09:07:40.877120+00:00",
"payment_method": "click"
}
}
payment.refunded namunasi
{
"event": "payment.refunded",
"created_at": "2026-10-19T11:20:03.554210+00:00",
"data": {
"invoice": "PAY_MYSHOP_7K2M9Q",
"amount": "155000.00",
"reason": "Xaridor so'rovi bo'yicha"
}
}
Imzoni tekshirish
Imzo so'rovning xom tanasi (raw body) bo'yicha hisoblanadi — JSON'ni qayta formatlamasdan, kelgan baytlarni ishlating. Shuningdek, eski so'rovlar qayta yuborilishining oldini olish uchun vaqt belgisini tekshiring (masalan, 5 daqiqadan eski so'rovlarni rad eting).
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 istalgan
2xxkodi bilan javob berishi kerak. Og'ir ishlarni javobdan keyin, fonda bajaring. - Javob kelmasa yoki
2xxbo'lmasa, yetkazish 1 daqiqa, 5 daqiqa, 15 daqiqa, 1 soat va 6 soatdan so'ng qayta urinib ko'riladi. - Yo'naltirishlar (
3xx) kuzatilmaydi — webhook manzili to'g'ridan-to'g'ri ishlovchi manzil bo'lsin. - Bir hodisa bir necha marta kelishi mumkin.
X-MSavdogar-Deliveryyokiinvoiceraqami bo'yicha takroriy ishlovni oldini oling. - Yetkazishlar tarixi va oxirgi xatolar Do'kon panelining webhook bo'limida ko'rinadi.
Integratsiya bo'yicha savolingiz bormi?
Qo'llab-quvvatlash xizmatiga yozing — dasturchilarimiz yordam beradi.