Mundarija (18)
- Bu darsda
- 1. Nega bu kerak?
- 2. OpenAPI hujjatini o'qish
- 3. Oqim
- 4. Xato kodlari
- 5. Idempotentlik: Uzum boshqacha
- 6. 30 daqiqalik tasdiq
- 7. Kanon topilmasi: 99999 ortidagi SQL xatosi
- 8. Boshqa mahalliy tizimlar
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): qaysi kod?
- 2-mashq (o'rta): xato javobi
- 3-mashq (qiyin): /status so'rash simulyatsiyasi
- 11. Loyiha qadami: uzum/merchant.ts
- 12. Real ishda
- Xulosa
- Manbalar
Uzum va boshqa mahalliy to'lov tizimlari: hujjatdan merchant API'gacha
Qisqacha: Uzum Bank Merchant API ham server-to-server: Uzum sizga beshta so'rov yuboradi —
/check,/create,/confirm,/reverse,/status. Summa tiyinda, vaqt millisekundda, xato — HTTP 400 vaerrorCodesatri (10001…10018,99999). Payme'dan muhim farq: o'shatransIdbilan qayta/create— xato (10010), o'sha javob emas. Bu dars yana bir ko'nikmani o'rgatadi: provayder hujjatini (bu yerda OpenAPI spetsifikatsiyasini) o'qib, uni test bilan kodga aylantirish. Kanon birinchi urinishda 99999 ortiga yashiringan SQL xatosini topdi — kutilmagan xatoni doim logga yozing.
Bu darsda
- Uzum Merchant API'ning beshta metodini va javob formatini bilasiz.
- Xato kodlarini va HTTP statuslarini to'g'ri qaytarasiz.
- Payme, Click va Uzum'ning idempotentlik qoidalarini solishtirasiz.
- 30 daqiqalik tasdiq muddati va
/statusso'rovlarini tushunasiz. - Postgres parametr turi xatosini o'z ko'zingiz bilan ko'rasiz.
- Paynet, Octo, Atmos va Payme Subscribe qayerga to'g'ri kelishini bilasiz.
uzum/merchant.tsmodulini yozasiz.
Oldin bilishingiz kerak: Click integratsiyasi, Payme Merchant API, Parametrli so'rovlar va SQL injection, Logging asoslari.
Oldingi ikki darsda Payme va Click hujjatlarini kodga aylantirdik. Uchinchi provayderda takrorlanadigan naqshni ko'rasiz: har yangi hujjatga bir xil savollar ro'yxati bilan qarash.
Diqqat: Uzum Bank Merchant API'si bilan ishlash uchun Uzum Bank bilan hamkorlik shartnomasi kerak. Kanon spetsifikatsiyani developer.uzumbank.uz/merchant sahifasidan o'qidi: sahifa ichiga joylangan OpenAPI 3.1 hujjati ("Merchant API 1.0.0") matn sifatida o'qildi (2026-10-27), kod bajarilmadi. Bu darsning kodi lokal simulyator bilan sinalgan; haqiqiy Uzum — sinalmagan.
1. Nega bu kerak?
Har to'lov provayderi "o'z tilida" gapiradi. Bugun Uzum, ertaga yangi bank ilovasi yoki marketplace. Har safar noldan "qanday qilishni" o'ylash — sekin va xatoli. Tajribali dasturchi yangi hujjatni ochganda bir xil savollarga javob qidiradi:
| Savol | Payme | Click | Uzum |
|---|---|---|---|
| Kim kimni chaqiradi | Payme → biz | Click → biz | Uzum → biz |
| Format | JSON-RPC, text/json |
forma, javob JSON | JSON, REST uslubida |
| Auth | Basic | MD5 imzo | Basic |
| Summa | tiyin | so'm, satr | tiyin |
| Xato | HTTP 200 + error.code |
JSON error |
HTTP 400 + errorCode |
| Takroriy yaratish | o'sha javob | o'sha prepare_id |
xato 10010 |
| Timeout | 12 soat | — | /confirm 30 daqiqa |
Jadvaldagi har katak — kodda bitta qaror. Ularni hujjatdan oldindan yig'sangiz, kod deyarli o'zi yoziladi. Bu darsni shu jadvalning Uzum ustuni bo'yicha boramiz.
2. OpenAPI hujjatini o'qish
OpenAPI — HTTP API'ni mashina o'qiy oladigan formatda (JSON yoki YAML) tasvirlash standarti: yo'llar, metodlar, so'rov va javob sxemalari, statuslar. Uzum hujjati sayt ichida shunday spetsifikatsiya sifatida turadi (OpenAPI 3.1 darsida batafsil). Undan kanon quyidagilarni oldi:
- yo'llar:
POST /check,/create,/confirm,/reverse,/status; - so'rov tanasi — JSON:
serviceId,timestamp,transId(UUID),params.account(buyurtma),amount(int64, tiyin); - muvaffaqiyat javobi — HTTP 200,
status:OK,CREATED,CONFIRMED,REVERSED; - xato — HTTP 400,
status: "FAILED"vaerrorCode(satr); - auth —
Authorization: Basic ….
Kanon simulyatori bilan haqiqiy xato javobi:
Uzum xato (HTTP 400): {"serviceId":101202,"transId":"5c398d7e-76b6-11ee-96da-f3a095c6289d","status":"FAILED","errorCode":"10007"}10007 — "atribut topilmadi", ya'ni bunday buyurtma yo'q. E'tibor bering: Uzum errorCode ni satr sifatida beradi ("10007"), son emas. Sxemani o'qimasdan errorCode: 10007 qaytarsangiz, qat'iy mijoz uni tushunmaydi.
3. Oqim
sequenceDiagram
participant U as Uzum Bank
participant S as Bizning server
participant DB as Baza
U->>S: POST /check (account)
S-->>U: 200 OK
U->>S: POST /create (transId, amount)
S->>DB: INSERT 'yaratilgan', buyurtma 'kutilmoqda'
S-->>U: 200 CREATED
U->>U: kartadan yechish
U->>S: POST /confirm (transId)
S->>DB: 'bajarilgan', 'tolangan', ledger
S-->>U: 200 CONFIRMED
Note over U,S: confirm javobi kelmasa — /status 10 martagachaDiagrammadagi beshta so'rovni bitta jadvalga yig'amiz — har biri nima so'raydi va biz nima qaytaramiz:
| So'rov | Uzum nima so'raydi | Muvaffaqiyatli javob |
|---|---|---|
/check |
"bu buyurtma bormi, to'lasa bo'ladimi?" | OK |
/create |
"shu transId bilan to'lovni ochaman" |
CREATED |
/confirm |
"pul yechildi, tasdiqlang" | CONFIRMED |
/reverse |
"ochilgan to'lovni bekor qiling" | REVERSED |
/status |
"bu transId hozir qaysi holatda?" |
uchtadan biri |
Uzum hujjatidagi ikki muddat qoidasi:
/confirm/createdan keyin 30 daqiqa ichida kelmasa — tranzaksiya muvaffaqiyatsiz hisoblanadi./confirmga javob xato yoki timeout bo'lsa, Uzum holatni aniqlash uchun/statusni 10 martagacha so'raydi.
Ikkinchi qoida — server-to-server oqimining tabiiy davomi: tarmoq uzilsa, provayder taxmin qilmaydi, so'raydi. /status aniq javob berishi shart: CREATED, CONFIRMED yoki REVERSED.
4. Xato kodlari
| Kod | Ma'nosi |
|---|---|
| 10001 | auth xatosi |
| 10002 | JSON tahlil xatosi |
| 10003 | POST emas |
| 10005 | majburiy maydon yo'q |
| 10006 | serviceId boshqa |
| 10007 | buyurtma (atribut) topilmadi |
| 10008 | buyurtma allaqachon to'langan |
| 10009 | buyurtma bekor qilingan |
| 10010 | shu transId bilan allaqachon yaratilgan |
| 10011 | summa noto'g'ri |
| 10012 / 10013 | summa minimaldan kam / maksimaldan ko'p |
| 10014 | tranzaksiya topilmadi |
| 10015 | tranzaksiya bekor qilingan (confirm) |
| 10016 | allaqachon tasdiqlangan |
| 10017 | bekor qilib bo'lmaydi |
| 10018 | allaqachon bekor qilingan |
| 99999 | boshqa (kutilmagan) xato |
Kanon har ishlovchini bitta o'ram (wrapper) bilan yozdi: umumiy tekshiruvlar (POST, auth, JSON, serviceId), keyin metod, xato bo'lsa — HTTP 400 va errorCode:
const ishlov =
(
fn: (
b: Record<string, unknown>,
) => Promise<Record<string, unknown>>,
) =>
async (req: Request, res: Response) => {
let tana: Record<string, unknown> = {};
try {
if (req.method !== "POST") throw new UzumXato("10003");
if (!auth(req.get("authorization")))
throw new UzumXato("10001");
try {
tana = JSON.parse(
typeof req.body === "string" ? req.body : "",
) as Record<string, unknown>;
} catch {
throw new UzumXato("10002");
}
if (typeof tana !== "object" || Array.isArray(tana))
throw new UzumXato("10002");
if (
tana["serviceId"] === undefined ||
tana["timestamp"] === undefined
)
throw new UzumXato("10005");
if (tana["serviceId"] !== s.serviceId)
throw new UzumXato("10006");
res
.status(200)
.json({ serviceId: s.serviceId, ...(await fn(tana)) });
} catch (e) {
// 99999 ortida haqiqiy sabab logda qolsin (birinchi urinishda
// SQL xatosi yashiringan edi)
if (!(e instanceof UzumXato)) (s.log ?? console.error)(e);
const kod = e instanceof UzumXato ? e.kod : "99999";
res
.status(400)
.json({
serviceId: tana["serviceId"] ?? s.serviceId,
...(typeof tana["transId"] === "string"
? { transId: tana["transId"] }
: {}),
status: "FAILED",
errorCode: kod,
});
}
};Diqqat qiling: Payme'da auth JSON'dan keyin (javobda id qaytishi uchun), bu yerda esa oldin. Uzum xato javobida transId ixtiyoriy — shuning uchun tanani o'qishdan oldin rad etsa bo'ladi. Har provayderning javob formati tartibni belgilaydi.
5. Idempotentlik: Uzum boshqacha
Payme'da o'sha id bilan qayta CreateTransaction — o'sha muvaffaqiyatli javob. Uzum hujjatida esa xuddi shu holat uchun alohida kod bor: 10010 — "shu transId bilan tranzaksiya allaqachon yaratilgan". Kanon testi buni tasdiqladi: birinchi /create — CREATED, ikkinchisi (o'sha transId) — 10010.
Bu "idempotentlik yo'q" degani emas: takroriy so'rov holatni o'zgartirmaydi (ikkinchi tranzaksiya yaratilmaydi). Faqat javob boshqacha. Uzum tomoni 10010 ni "yaratilgan ekan" deb tushunishi kerak — buni kanon sinay olmadi (haqiqiy Uzum yo'q).
Bitta jadvalda qoidalar:
const takrorga = {
payme: { yarat: "o'sha javob", tasdiq: "o'sha javob" },
click: { yarat: "o'sha prepare_id", tasdiq: "-4 Already paid" },
uzum: { yarat: "10010 xato", tasdiq: "10016 xato" },
};
for (const [p, q] of Object.entries(takrorga)) {
console.log(p.padEnd(6), "|", q.yarat.padEnd(17), "|", q.tasdiq);
}Konsolda:
payme | o'sha javob | o'sha javob
click | o'sha prepare_id | -4 Already paid
uzum | 10010 xato | 10016 xatoTekshirib ko'ring: Uzum ikkinchi
/createga10010oldi. Buyurtma holati nima bo'ladi va bazada nechta tranzaksiya?
Javob
Hech narsa o'zgarmaydi: buyurtma kutilmoqda, tranzaksiya bitta (UNIQUE (provayder, tashqi_id) va kod ichidagi tekshiruv). Kanon kodi 10010 ni buyurtma holatini tekshirishdan oldin qaytaradi — takroriy so'rov boshqa xatoga aylanib ketmasin.
Saboq: "idempotent" so'zi protokolda boshqa-boshqa javob bilan ifodalanadi. Umumiy kod yozishga (bitta merchant klassi uchala provayderga) shoshilmang — kanon har provayderni alohida modulda yozdi, umumiy qismi faqat tranzaksiya() va ledger.
6. 30 daqiqalik tasdiq
Kanon /confirm da tranzaksiya yoshini tekshiradi: 30 daqiqadan eski bo'lsa — tranzaksiya bekor, buyurtma yana yangi, Uzum'ga 10015. Payme'dagi timeout kabi bekor qilish COMMIT bo'lishi kerak, shuning uchun bu yerda ham xato qaytarilib, keyin tashlanadi.
/create da esa boshqa holat: buyurtmada 30 daqiqadan eski chala tranzaksiya qolib ketgan bo'lsa, u avval yopiladi va yangisiga joy bo'shaydi. Yangi (30 daqiqadan yosh) faol tranzaksiya bo'lsa — 10017.
Testda soat 30 daqiqa + 1 soniya oldinga surildi: /confirm → 10015, /status → REVERSED, keyin o'sha buyurtma yangi transId bilan muvaffaqiyatli to'landi.
/reverse faqat CREATED (yaratilgan) tranzaksiyani bekor qiladi. Tasdiqlangan (CONFIRMED) tranzaksiyani reverse qilish — hujjat aniq aytmagan; kanon qarori: 10017 (bekor qilib bo'lmaydi).
7. Kanon topilmasi: 99999 ortidagi SQL xatosi
Kanonning birinchi urinishida /create har doim 99999 qaytardi. Kod "kutilmagan xato" ni ushlab, 99999 berar va jim qolar edi. Logda hech narsa yo'q — sabab topilmadi. Kutilmagan xatolar logga yozila boshlagach, haqiqiy xabar ko'rindi: Postgres bitta parametrning turini aniqlay olmagan.
INSERT da $4 ikki joyda ishlatilgan edi: tashqi_vaqt ustuniga (bigint) va to_timestamp($4 / 1000.0) ichida (numeric bilan bo'lish). Buni o'z Postgres 17.6 klasterimizda kanon sxemasi ustida takrorladik (biz ishga tushirdik). Avval bitta buyurtma qo'shamiz, keyin xato so'rovni tayyorlaymiz. Ikkinchi chiqishda xato xabaridagi $4 ga qarang:
INSERT INTO buyurtmalar (foydalanuvchi_id, summa)
VALUES (1, 150000);INSERT 0 1\set VERBOSITY terse
PREPARE yarat AS
INSERT INTO tranzaksiyalar (provayder, tashqi_id, buyurtma_id,
summa, holat, tashqi_vaqt, yaratilgan_vaqt)
VALUES ('uzum', $1, $2, $3, 'yaratilgan', $4,
to_timestamp($4 / 1000.0));ERROR: inconsistent types deduced for parameter $4 at character 175Postgres bitta $4 uchun ikki xil tur chiqardi: ustun uchun bigint, bo'lish uchun numeric. Tuzatish — ikkita alohida parametr (kanon kodida $4 va $5, ikkalasiga ham hozir() beriladi):
PREPARE yarat2 AS
INSERT INTO tranzaksiyalar (provayder, tashqi_id, buyurtma_id,
summa, holat, tashqi_vaqt, yaratilgan_vaqt)
VALUES ('uzum', $1, $2, $3, 'yaratilgan', $4,
to_timestamp($5 / 1000.0))
RETURNING id, tashqi_vaqt, yaratilgan_vaqt;
EXECUTE yarat2('t-1', 1, 150000, 1793095200000, 1793095200000);PREPARE
id | tashqi_vaqt | yaratilgan_vaqt
----+---------------+------------------------
1 | 1793095200000 | 2026-10-27 10:00:00+00
(1 row)
INSERT 0 1pg drayveri har parametrli so'rovni shunday tayyorlaydi (Parametrli so'rovlar va SQL injection), shuning uchun xato ilovada ham aynan shu edi.
Asosiy saboq SQL'da emas, xato ishlovida. 99999 — provayder uchun to'g'ri javob. Lekin sizga haqiqiy sabab kerak. Kanon uchala provayderda ham qoidani bir xil qildi: kutilmagan xato avval logga yoziladi (Logging asoslari), keyin umumiy kod qaytariladi — Payme -32400, Click -7, Uzum 99999.
8. Boshqa mahalliy tizimlar
Kanon bularni sinamagan — quyidagi tavsif umumiy, faqat yo'nalish uchun. Har biri bilan ishlashdan oldin rasmiy hujjat va shartnoma kerak.
| Tizim | Qanday to'lov | Qachon kerak |
|---|---|---|
| Paynet | to'lov shoxobchalari va ilova orqali | naqd pul bilan to'lovchi mijozlar |
| Octo, Atmos | karta to'lovi va tokenlash | kartani saqlab, keyin qayta yechish |
| Payme Subscribe (cards API) | karta tokeni orqali yechish | obuna, avtomatik to'lov |
| Uzum Checkout | redirect sahifasi, karta | Uzum Bank ilovasisiz karta to'lovi |
Tokenlash (tokenization) — karta raqami o'rniga provayder bergan token'ni saqlash. Karta ma'lumoti sizning serveringizga tushmaydi, lekin keyingi to'lovni mijoz ishtirokisiz qilish mumkin. Bu obunalar uchun asos (Obuna va davriy to'lovlar).
Yangi tizimga duch kelsangiz — «Nega bu kerak?» bo'limidagi jadvalning yangi ustunini to'ldiring. Ustun to'lmaguncha kod yozmang.
9. Ko'p uchraydigan xatolar
| Xato | Oqibati | To'g'risi |
|---|---|---|
errorCode ni son qilib qaytarish |
sxemaga mos emas | satr: "10007" |
| xatoda HTTP 200 | Uzum muvaffaqiyat deb o'ylaydi | HTTP 400 + FAILED |
takroriy /create ga CREATED |
protokolga zid | 10010 |
/status ni yozmaslik |
timeout'dan keyin holat noma'lum | aniq CREATED/CONFIRMED/REVERSED |
| 30 daqiqa tekshiruvsiz | eski urinish buyurtmani qulflaydi | /create da eskisini yopish, /confirm da 10015 |
99999 ni logsiz qaytarish |
bug yashiringan | avval log, keyin umumiy kod |
| bitta parametr ikki turda | inconsistent types |
alohida parametrlar |
10. Mashqlar
Mashqlar kurs/mashqlar/29/31-uzum/ da.
1-mashq (oson): qaysi kod?
serviceId: 1keldi, sizniki101202.- O'sha
transIdbilan ikkinchi/create. - Tasdiqlangan tranzaksiyaga yana
/confirm. /createdaamount: 1, minimal summa 1 000 so'm.- Hech qachon yaratilmagan
transIdbilan/status.
Yechim
10006. 2.10010. 3.10016. 4.10012. 5.10014. Hammasi HTTP 400 vastatus: "FAILED"bilan.
2-mashq (o'rta): xato javobi
xatoJavob(tana, kod, serviceId) yozing: { http: 400, json } qaytarsin. json da serviceId (tanada bo'lsa — o'shasi, yo'q bo'lsa — bizniki), transId (faqat tanada satr bo'lsa), status: "FAILED", errorCode — satr. Uch holatni sinang: to'liq tana, bo'sh tana (JSON buzuq bo'lgan), transId son bo'lgan tana.
Yechim
function xatoJavob(tana, kod, serviceId) {
const json = { serviceId: tana.serviceId ?? serviceId };
if (typeof tana.transId === "string") json.transId = tana.transId;
json.status = "FAILED";
json.errorCode = String(kod);
return { http: 400, json };
}
const tid = "5c398d7e-76b6-11ee-96da-f3a095c6289d";
console.log(xatoJavob({ serviceId: 1, transId: tid }, 10006, 101202));
console.log(xatoJavob({}, 10002, 101202));
const sonli = { serviceId: 101202, transId: 7 };
console.log(xatoJavob(sonli, 10005, 101202));Konsolda:
{
http: 400,
json: {
serviceId: 1,
transId: '5c398d7e-76b6-11ee-96da-f3a095c6289d',
status: 'FAILED',
errorCode: '10006'
}
}
{
http: 400,
json: { serviceId: 101202, status: 'FAILED', errorCode: '10002' }
}
{
http: 400,
json: { serviceId: 101202, status: 'FAILED', errorCode: '10005' }
}Birinchi holatda serviceId — Uzum yuborgan (noto'g'ri) qiymat: kanon ham shunday qiladi, provayder o'z so'rovini javobda taniydi. Ikkinchisida tana bo'sh — bizniki.
3-mashq (qiyin): /status so'rash simulyatsiyasi
Uzum /confirm javobini olmasa /status ni 10 martagacha so'raydi. holatniAniqla(sorash, maks) yozing: sorash(n) — n-urinishda holatni qaytaradi yoki "timeout" tashlaydi. Funksiya birinchi aniq javobni (CONFIRMED, REVERSED, CREATED) urinishlar soni bilan qaytarsin; maks tugasa — { holat: "noma'lum", urinish: maks }. Ikki ssenariy: 3-urinishda CONFIRMED; doim timeout (maks = 10).
Yechim
function holatniAniqla(sorash, maks) {
for (let n = 1; n <= maks; n++) {
try {
return { holat: sorash(n), urinish: n };
} catch {
// timeout — keyingi urinish
}
}
return { holat: "noma'lum", urinish: maks };
}
const uchinchida = (n) => {
if (n < 3) throw new Error("timeout");
return "CONFIRMED";
};
const doim = () => {
throw new Error("timeout");
};
console.log(holatniAniqla(uchinchida, 10));
console.log(holatniAniqla(doim, 10));Konsolda:
{ holat: 'CONFIRMED', urinish: 3 }
{ holat: "noma'lum", urinish: 10 }"Noma'lum" holat — eng xavflisi: pul yechilgan bo'lishi mumkin, lekin tasdiqlanmagan. Uni avtomatik hal qilib bo'lmaydi; bunday tranzaksiyalar kunlik solishtirishda (reconciliation) chiqadi va qo'lda ko'riladi. Haqiqiy qayta urinishlar orasida kutish bo'ladi (eksponensial kutish — timers va timers/promises chuqur) — mashqda soddalik uchun yo'q.
11. Loyiha qadami: uzum/merchant.ts
O'ram (ishlov) yuqorida ko'rsatildi. /create ishlovchisi — eng ko'p qaror qabul qilinadigan joy (formatlangan nusxa, ma'nosi kanondagidek):
r.all(
"/create",
ishlov((b) =>
tranzaksiya(s.pool, async (c) => {
const oid = buyurtmaId(b);
const tid = transId(b);
const amount = b["amount"];
if (
typeof amount !== "number" ||
!Number.isSafeInteger(amount)
)
throw new UzumXato("10005");
const q = (
await c.query<{
id: string;
summa: string;
holat: string;
}>(
"SELECT id, summa, holat FROM buyurtmalar WHERE id = " +
"$1 FOR UPDATE",
[oid],
)
).rows[0];
if (!q) throw new UzumXato("10007");
if (
(
await c.query(
"SELECT 1 FROM tranzaksiyalar WHERE provayder = " +
"'uzum' AND tashqi_id = $1",
[tid],
)
).rowCount
)
throw new UzumXato("10010");
if (q.holat === "tolangan" || q.holat === "qaytarilgan")
throw new UzumXato("10008");
if (q.holat === "bekor") throw new UzumXato("10009");
if (s.min !== undefined && amount < s.min)
throw new UzumXato("10012");
if (s.maks !== undefined && amount > s.maks)
throw new UzumXato("10013");
if (amount !== Number(q.summa)) throw new UzumXato("10011");
// 30 daqiqadan eski chala tranzaksiya — muvaffaqiyatsiz deb
// yopiladi (hujjat), yangisiga joy bo'shaydi
await c.query(
"UPDATE tranzaksiyalar SET holat = 'bekor', bekor_vaqt = " +
"now() WHERE buyurtma_id = $1 AND holat = 'yaratilgan' " +
"AND yaratilgan_vaqt < to_timestamp($2 / 1000.0)",
[q.id, hozir() - CONFIRM_MUDDAT_MS],
);
if (
(
await c.query(
"SELECT 1 FROM tranzaksiyalar WHERE buyurtma_id = $1 " +
"AND holat = 'yaratilgan'",
[q.id],
)
).rowCount
)
throw new UzumXato("10017");
const t = (
await c.query<Tx>(
"INSERT INTO tranzaksiyalar (provayder, tashqi_id, " +
"buyurtma_id, summa, holat, tashqi_vaqt, " +
"yaratilgan_vaqt) VALUES ('uzum', $1, $2, $3, " +
"'yaratilgan', $4, to_timestamp($5 / 1000.0)) " +
"RETURNING *",
[tid, q.id, amount, hozir(), hozir()],
)
).rows[0];
if (!t) throw new UzumXato("99999");
await c.query(
"UPDATE buyurtmalar SET holat = 'kutilmoqda', " +
"yangilangan_vaqt = now() WHERE id = $1",
[q.id],
);
return { ...tolov(t), status: "CREATED", data: data(oid) };
}),
),
);
Testlar — tolov/test/uzum.test.ts, uchta: to'liq oqim (OK → CREATED → CONFIRMED → /status), xatolar (10001, 10002, 10006, 10007, 10010, 10011, 10012, 10016, 10014, 10017), reverse va 30 daqiqa. Biz ishga tushirdik (kanon nusxasi, Node 24.21.0, Postgres 17.6). Chiqishda har ✔ qatoridagi kodlar ro'yxatiga va pastdagi fail 0 ga qarang:
▶ Uzum Merchant API (simulyator)
✔ to'liq oqim: check OK → create CREATED → confirm CONFIRMED → status (123.0293ms)
✔ xatolar: 10001 auth, 10002 JSON, 10006 serviceId, 10007, 10010 takror create, 10011, 10012, 10016, 10014 (63.521ms)
✔ reverse (CREATED) → REVERSED; takror → 10018; confirm → 10015; 30 daqiqadan keyin confirm → 10015 (64.1536ms)
✔ Uzum Merchant API (simulyator) (1376.5466ms)
ℹ tests 3
ℹ suites 1
ℹ pass 3
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1830.2868Nima bo'ldi? Uchala test o'tdi: to'liq oqim, o'nta xato kodi va 30 daqiqalik qoida simulyatorda kutilganidek ishladi. Qavs ichidagi millisekundlar har ishga tushirishda boshqacha chiqadi — ular natija emas, faqat vaqt.
Kanon o'lchovi: 100 ta to'liq to'lov (check + create + confirm) — mediana 40.3 ms, p95 54.9 ms, 100/100 (lokal simulyator).
Commit xabari:
M='feat(tolov): Uzum Merchant API — 5 metod, 30 daqiqa,'
M+=' kutilmagan xato logda, simulyator'
git commit -m "$M"12. Real ishda
- Hujjat versiyasi: OpenAPI hujjatida versiya bor (
1.0.0). Provayder uni yangilasa, maydonlar o'zgarishi mumkin — integratsiyani qachon tekshirganingizni yozib qo'ying (kanon — 2026-10-27). - Ko'p provayder — bitta
tranzaksiyalarjadvali,provayderustuni bilan. Hisobot va solishtirish bitta joyda. - Monitoring: har provayder uchun "kutilmagan xato" soni alohida.
99999ko'payishi — sizda bug, provayderda emas. - Intervyuda: "Yangi to'lov tizimi hujjatini qanday o'qiysiz?", "Takroriy so'rovga qanday javob berasiz?"
Xulosa
- Uzum: beshta POST metod, JSON, Basic auth, summa tiyinda, xato — HTTP 400 +
errorCodesatri. - Takroriy
/create—10010xatosi (Payme'dan farqli), lekin holat o'zgarmaydi. /confirm30 daqiqada kelmasa — bekor; javob yo'qolsa Uzum/statusni 10 martagacha so'raydi.- Bitta parametr ikki turda —
inconsistent types deduced; kutilmagan xato avval logga, keyin umumiy kodga. - Yangi provayder — yettita savol jadvali: kim chaqiradi, format, auth, summa, xato, takror, timeout.
Keyingi dars: Stripe — xalqaro to'lovlar: PaymentIntent, Checkout Session va imzoli webhook.
Manbalar
- Uzum Bank Merchant API (OpenAPI 3.1, "Merchant API 1.0.0"): https://developer.uzumbank.uz/merchant — kanon 2026-10-27 da o'qigan
- OpenAPI spetsifikatsiyasi: https://spec.openapis.org/oas/v3.1.0
- PostgreSQL
PREPARE: https://www.postgresql.org/docs/18/sql-prepare.html - Kanon (IlmHamroh, 2026-10-27):
tolovpaketi, lokal Uzum simulyatori; haqiqiy Uzum, Paynet, Octo, Atmos, Payme Subscribe — sinalmagan. SQL va testlar — biz ishga tushirdik, PostgreSQL 17.6 (2026-10-28).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!