Dasturchilar uchun

Merchant API

Saytingiz, ilovangiz yoki Telegram botingizni MSavdogar'ga ulang: invoys yarating, xaridorni to'lov sahifasiga yo'naltiring va to'lov natijasini imzolangan webhook orqali darhol oling.

Mundarija
  1. Boshlash
  2. Autentifikatsiya
  3. Invoys yaratish
  4. Invoys holati
  5. Invoyslar ro'yxati
  6. Invoysni bekor qilish
  7. Invoysni qayta ochish
  8. Do'kon ma'lumotlari
  9. Invoys obyekti
  10. Takroriy so'rovlar
  11. Xatolar
  12. Webhooklar
  13. Imzoni tekshirish
  14. Qayta yuborish
  15. Firewall

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:

  1. Serveringiz POST /v1/invoices orqali invoys yaratadi va javobdagi pay_url ni oladi.
  2. Xaridor pay_url sahifasiga yo'naltiriladi va Click, bank kartasi yoki balans orqali to'laydi.
  3. To'lov yakunlangach, MSavdogar serveringizga payment.paid webhookini yuboradi.
  4. 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.

HTTP sarlavha
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

POST/v1/invoices

Belgilangan summaga to'lov so'rovi (invoys) yaratadi. Muvaffaqiyatli javob — 201 Created va invoys obyekti.

MaydonTuriTavsif
amount majburiysonTo'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_idmatn, ≤128Tizimingizdagi buyurtma raqami. Webhookda order_id (va eski nom merchant_order_id) sifatida qaytadi; GET /v1/invoices?order_id= bilan qidirish mumkin.
titlematn, ≤200To'lov sahifasida va chekda ko'rinadigan nom.
commentmatn, ≤300Qo'shimcha izoh.
customer_emailmatn, ≤200Xaridor emaili. Invoys chekida va do'kon panelidagi invoys ma'lumotlarida ko'rinadi.
return_urlmatn, ≤512Faqat https:// manzil (login-parolsiz). To'lov sahifasida «Do'konga qaytish» tugmasi shu manzilga olib boradi; noto'g'ri manzil uchun 422 qaytadi.
expires_minutessonInvoys amal qilish muddati, daqiqada: 5 – 43200. Standart: 1440 (24 soat).
idempotency_keymatn, ≤128Takroriy so'rovdan himoya (batafsil): bir xil kalit bilan qayta yuborilgan so'rov yangi invoys yaratmaydi, mavjudini qaytaradi.
metadataobyekt (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
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"
  }'

Javob namunasi

201 Created · JSON
{
  "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

GET/v1/invoices/{number}

Invoysning joriy holatini qaytaradi. Faqat o'z do'koningiz invoyslarini ko'ra olasiz; boshqa invoys uchun 404 qaytadi.

cURL
curl https://api.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q \
  -H "X-Api-Key: $MSAVDOGAR_API_KEY"

Javob namunasi

200 OK · JSON
{
  "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

GET/v1/invoices?status=paid&limit=50

Filtrlar: status (holat), order_id (sizning buyurtma raqamingiz, aniq moslik), limit (1 – 200).

Eng yangi invoyslar ro'yxati, {"items": [...]} ko'rinishida.

ParametrTavsif
statusIxtiyoriy filtr: holatlardan biri (masalan pending, paid). Noma'lum qiymat bo'sh ro'yxat qaytaradi.
limitNatijalar soni, 1 – 200 (chegaradan tashqari qiymat shu oraliqqa keltiriladi). Standart: 50.
cURL
curl "https://api.msavdogar.uz/v1/invoices?status=paid&limit=20" \
  -H "X-Api-Key: $MSAVDOGAR_API_KEY"
200 OK · JSON
{
  "items": [
    { "number": "PAY_MYSHOP_7K2M9Q", "status": "paid", "...": "invoys obyekti, yuqoridagidek" }
  ]
}

Invoysni bekor qilish

POST/v1/invoices/{number}/cancel

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
curl -X POST https://api.msavdogar.uz/v1/invoices/PAY_MYSHOP_7K2M9Q/cancel \
  -H "X-Api-Key: $MSAVDOGAR_API_KEY"

Invoysni qayta ochish

POST/v1/invoices/{number}/reopen

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_minutes5 – 43200, standart 1440 (24 soat).
cURL
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

GET/v1/me

API kaliti tegishli bo'lgan do'kon va uning hisobidagi mablag'lar holati.

Javob · JSON
{
  "username": "myshop",
  "business_name": "My Shop",
  "kyc_status": "verified",
  "wallet": {
    "held": "450000.00",
    "available": "1250000.00",
    "payout_pending": "0.00"
  }
}
MaydonMa'nosi
wallet.heldYaqinda tushgan, xavfsizlik muddati tugashini kutayotgan mablag'.
wallet.availableYechib olish mumkin bo'lgan mablag'.
wallet.payout_pendingPul yechish so'rovlarida turgan mablag'.
kyc_statusunverified, pending, verified yoki rejected.

Invoys obyekti

MaydonTavsif
numberInvoysning noyob raqami, masalan PAY_MYSHOP_7K2M9Q.
statusHolat (quyidagi jadvalga qarang).
kindInvoys turi; API orqali yaratilganlar uchun api. Webhookda boshqa turlar ham bo'lishi mumkin (masalan, do'kon sahifasidan sotilgan product).
amountAsosiy summa.
tip_amountXaridor qo'shgan ixtiyoriy ustama.
total_amountXaridordan yechilgan jami summa (amount + tip_amount).
currencyHar doim UZS.
title, commentInvoys nomi va izohi.
order_idSiz yuborgan buyurtma raqami.
metadataSiz yuborgan metadata (yuborilmagan bo'lsa {}).
fee_percent, net_amountXizmat haqi foizi va sizga tushadigan sof summa (amount − xizmat haqi + tip_amount) — to'lovdan so'ng to'ldiriladi, undan oldin null.
pay_urlXaridor uchun to'lov sahifasi.
created_at, paid_at, expires_atVaqt belgilari (ISO 8601, UTC).
payment_methodTo'lovdan oldin null; keyin click, card, card_token yoki balance.

Holatlar

HolatMa'nosi
pendingTo'lov kutilmoqda.
paidTo'langan — buyurtmani bajarishingiz mumkin.
cancelledBekor qilingan.
expiredMuddati o'tgan, to'lab bo'lmaydi.
refund_pendingQaytarish 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.
refundedPul 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 201 qaytaradi.
  • Xuddi shu kalit bilan keyingi so'rov yangi invoys yaratmaydi — mavjud invoysni (xuddi shu number va pay_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 amount yoki order_id boshqa bo'lsa — bu qayta urinish emas, xato: 409 va {"code": "idempotency_mismatch", "number": "...", "detail": "..."} qaytadi, hech narsa yaratilmaydi. Boshqa summa yoki buyurtma uchun yangi kalit ishlating (title, comment va 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).

KodSabab
400So'rov bajarilmadi, masalan summa ruxsat etilgan chegaradan tashqarida.
401API kaliti yuborilmagan, noto'g'ri yoki bekor qilingan.
403Do'kon faol emas.
404Invoys topilmadi yoki boshqa do'konga tegishli.
409Invoys 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").
422Maydonlar talablarga mos emas (turi, uzunligi yoki qiymati).
429So'rovlar limiti oshib ketdi — birozdan so'ng qayta urining.
5xxVaqtinchalik 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 (masalan 10.x, 192.168.x, 127.x) qabul qilinmaydi. Tekshiruv manzil saqlanganda ham, har bir yuborishda ham DNS orqali qayta bajariladi.
  • Yo'naltirishlar (3xx) kuzatilmaydi.
HodisaQachon yuboriladi
payment.paidDo'koningizga tegishli istalgan invoys to'langanda (API invoyslari ham, do'kon sahifasidagi sotuvlar ham — turini data.kind ko'rsatadi).
payment.refundedTo'lov xaridorga muvaffaqiyatli qaytarilganda.
payment.cancelledAPI orqali yaratilgan invoys bekor qilinganda (POST /v1/invoices/{number}/cancel).
payment.expiredAPI orqali yaratilgan invoysning muddati to'lovsiz tugaganda.

Sarlavhalar

SarlavhaQiymat
X-MSavdogar-EventHodisa nomi, masalan payment.paid.
X-MSavdogar-TimestampYuborilgan vaqt, Unix soniyalarda. Har bir urinishda yangilanadi.
X-MSavdogar-SignatureHMAC-SHA256(secret, timestamp + "." + body), hex ko'rinishida.
X-MSavdogar-DeliveryYetkazish ID raqami; bir hodisaning barcha urinishlarida bir xil — takroriy qayta ishlashdan himoya uchun.
Content-Typeapplication/json

payment.paid namunasi

POST · JSON
{
  "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

POST · JSON
{
  "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.

Python (FastAPI)
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}

Javob va qayta yuborish

  • Serveringiz 15 soniya ichida 2xx (200–299) kodi bilan javob berishi kerak — shundagina yetkazish muvaffaqiyatli hisoblanadi. Boshqa kod (jumladan 3xx), 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 failed deb 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-Delivery yoki invoice raqami 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.

Murojaat yuborish