Barcha maqolalar
PaymentsBillingPaymeNestJS

Pulni registr sifatida modellashtirish (va nima uchun billing-ni ikki marta qayta yozishim kerak bo'ldi)

Nashr etilgan 18-sentabr, 2025 Β· 5 daqiqa o'qish

Birinchi versiya qanday ko'ringan edi

Halol aytganda, uyatli edi. Ijarachi yozuvida subscription_active mantiqiy qiymati. Oyning oxirida to'lov kelmagan bo'lsa uni false-ga o'zgartiradigan cron ishi.

Birinchi ikki hafta yaxshi ishladi. Keyin qo'llab-quvvatlash xabarlari boshlandi: "To'ldirdim lekin hisobim hali ham bloklangan." "Nima uchun ikki marta to'lov oldingiz?"

Asosiy muammo oddiy edi: men pulni holat (faol / nofaol) sifatida ko'rib chiqardim, aslida pul voqealar tarixi. Mantiqiy qiymatdan nima bo'lganini qayta qurib bo'lmaydi.

Pul β€” bu registr

Yechim: qoldiqlarni saqlashni to'xtatib, harakatlarni saqlashni boshlash.

Pul tizimga har safar tegsa, wallet_transactions-da qatorga aylanadi. Qatorda tur, imzolangan miqdor (ijobiy = kredit, manfiy = debet), havola ID va vaqt belgisi bor.

Men ishlatiladigan turlar:

  • top_up β€” Payme to'lovi hamyonga kreditlangan
  • subscription_fee β€” oylik avtomatik ayirish
  • refund β€” qo'llab-quvvatdan qo'lda kredit
  • adjustment β€” kamdan-kam qo'lda tuzatish

Istalgan vaqtdagi qoldiq shunchaki SUM so'rovidir:

Bu hammasi. Bitta so'rov, har doim aniq, sinxronlashdan chiqib keta olmaydi β€” chunki sinxronlash kerak bo'lgan narsa yo'q.

Payme integratsiyasi

Payme Callback-asosidagi oqimdan foydalanadi. Siz JSON-RPC endpoint-ni ochasiz, Payme to'lov holati o'zgarganda uni chaqiradi.

To'g'ri bajarish kerak bo'lgan bitta narsa: idempotentlik. Payme callbacklarni qayta urinadi.

PerformTransaction uchun ishlovchim:

  1. payme_transaction_id = X bilan top_up qatori mavjudligini tekshiring
  2. Ha bo'lsa β†’ muvaffaqiyat qaytaring, hech narsa yozmang
  3. Yo'q bo'lsa β†’ DB tranzaksiyasida qatorni kiriting, muvaffaqiyat qaytaring

Obuna avtomatik ayirish

Har oyning birinchisi NestJS cron har faol ijarachini to'lashga harakat qiladi:

  1. Joriy qoldiqni o'qing (SUM)
  2. Qoldiq β‰₯ to'lov bo'lsa: manfiy subscription_fee tranzaksiyasini kiriting
  3. Qoldiq < to'lov bo'lsa: payment_overdue = true qo'ying, bildirishnomani navbatga qo'ying

Qiyin yo'l bilan o'rgangan chekka holatlar

Bir vaqtda yozishlar. Agar to'ldirish va ayirish ikkalasi ham bir vaqtda qoldiqni o'qib, u 0 bo'lsa, ikkalasi ham salbiy qoldiq yaratuvchi tarzda davom etishi mumkin. Yechim: atomik tekshirish-debet kerak bo'lganda SELECT FOR UPDATE.

Vaqt mintaqasi. "Oyning birinchisi" Toshkentda (UTC+5) oyning birinchisini anglatadi. Bu xatoni bir marta qildim.

Qisman ish muvaffaqiyatsizligi. Cron 500 ta ijarachidan 400 tasini to'lov qilib qulasa, sodda restart ba'zi ijarachilardan ikki marta to'lov oladi. Idempotent paketlarda qayta ishlang.

Billing zerikarli, keyin emas. Pulni voqealar sifatida modellashtiring, idempotentlik tekshiruvlarini baxtli yo'ldan oldin yozing.