جینگو میں ریفرل سے آگاہ تقسیم ادائیگی کا بہاؤ کیسے بنایا جائے۔

اگر آپ کے پروڈکٹ کی ایک ہی ادائیگی ہے، تو ادائیگی کی منطق عام طور پر آسان ہوتی ہے۔ اس کا مطلب ہے کہ آپ صارف سے چارج کرتے ہیں، آرڈر کو بطور ادا شدہ نشان زد کرتے ہیں، اور پھر آگے بڑھتے ہیں۔

لیکن جب آپ کے کاروباری ماڈل میں ابھی ڈپازٹ، بعد میں بیلنس، اور درمیان میں ریفرل یا کوپن انتساب شامل ہوتا ہے، تو مسئلہ بالکل مختلف ہوتا ہے۔

اس وقت، آپ صرف پیسہ نہیں بڑھا رہے ہیں۔ ادائیگی کے ورک فلو کا انتظام کرتا ہے۔

اس ٹیوٹوریل میں، میں آپ کو دکھاؤں گا کہ جینگو میں درج ذیل ریفرل آگاہی تقسیم ادائیگی کے بہاؤ کو کیسے بنایا جائے۔

  • مرحلہ 2: ڈپازٹس اور بیلنس کو الگ سے ٹریک کریں۔

  • کوپن اور پارٹنر انٹیگریشن سپورٹ

  • ڈپلیکیٹ ادائیگی کی پروسیسنگ سے بچیں

  • ڈیٹا بیس کے لین دین کو محفوظ طریقے سے استعمال کریں۔

  • ریفرل ادائیگیوں کو مستقل رکھیں۔

  • ورک فلو مکمل ہونے پر ہی ڈیلیوری ایبلز کو غیر مقفل کریں۔

مرکزی خیال سادہ ہے۔ خیال یہ ہے کہ ادائیگی کو ویب ہک ایونٹ کے بجائے ریاستی منتقلی کے طور پر ہینڈل کیا جائے۔

انڈیکس

شرطیں

ان اقدامات پر عمل کرنے سے پہلے، آپ کو پہلے سے ہی درج ذیل سے واقف ہونا چاہیے:

  • جینگو ماڈلز، آراء، اور سوالات کے سیٹ

  • جینگو میں ڈیٹا بیس کے لین دین

  • بنیادی ویب ہک تصورات

  • Python کلاس پر مبنی یا فنکشن پر مبنی منظر کے نمونے۔

  • سٹرائپ یا پے اسٹیک جیسے ادائیگی فراہم کرنے والے ایونٹ کال بیکس کیسے بھیجتے ہیں۔

آپ کو ادائیگیوں کا ماہر بننے کی ضرورت نہیں ہے، لیکن آپ کو یہ سمجھنا چاہیے کہ Django ڈیٹا بیس کے ساتھ کیسے بات چیت کرتا ہے اور یہ کیسے محفوظ طریقے سے اسٹیٹ کو اسٹور کرتا ہے۔

منصوبے کی ساخت

مطلوبہ حصوں کی ایک سادہ ساخت مندرجہ ذیل ہے:

payments/
├── models.py
├── services.py
├── views.py
├── urls.py
└── webhooks.py

یہ علیحدگی اہم ہے۔

  • models.py اپنی کاروباری حالت کو محفوظ کریں۔

  • services.py اختتامی منطق پر مشتمل ہے۔

  • views.py صارف کا سامنا کرنے والی ادائیگی کی کارروائیوں کو ہینڈل کرتا ہے۔

  • webhooks.py گیٹ وے کال بیک وصول کریں۔

  • urls.py اختتامی مقامات کو جوڑیں۔

ادائیگی کی منطق کو نظروں سے اوجھل رکھنے سے سسٹم کو جانچنا آسان ہو جاتا ہے اور اسے توڑنا زیادہ مشکل ہو جاتا ہے۔

ڈیٹا ماڈل ڈیزائن

سب سے اہم فیصلہ ادائیگی کے مراحل کو واضح طور پر ماڈل بنانا ہے۔

ایک مبہم "ادا کردہ” جھنڈا بچانے کے بجائے، ان اقدامات کی وضاحت کریں جو آپ کا کاروبار درحقیقت استعمال کرتا ہے۔ مثال کے طور پر:

from django.db import models
from django.conf import settings

class Journey(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    deposit_paid = models.BooleanField(default=False)
    balance_paid = models.BooleanField(default=False)
    deliverables_released = models.BooleanField(default=False)
    referral_code = models.CharField(max_length=50, blank=True, default="")
    partner_name = models.CharField(max_length=120, blank=True, default="")
    created_at = models.DateTimeField(auto_now_add=True)

class Payment(models.Model):
    STAGE_DEPOSIT = "deposit"
    STAGE_BALANCE = "balance"

    STAGE_CHOICES = [
        (STAGE_DEPOSIT, "Deposit"),
        (STAGE_BALANCE, "Balance"),
    ]

    STATUS_PENDING = "pending"
    STATUS_SUCCEEDED = "succeeded"
    STATUS_FAILED = "failed"

    STATUS_CHOICES = [
        (STATUS_PENDING, "Pending"),
        (STATUS_SUCCEEDED, "Succeeded"),
        (STATUS_FAILED, "Failed"),
    ]

    journey = models.ForeignKey(Journey, on_delete=models.CASCADE, related_name="payments")
    stage = models.CharField(max_length=20, choices=STAGE_CHOICES)
    gateway_reference = models.CharField(max_length=120, unique=True)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    discount_amount = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    net_amount = models.DecimalField(max_digits=10, decimal_places=2)
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING)
    raw_payload = models.JSONField(null=True, blank=True)
    finalized_at = models.DateTimeField(null=True, blank=True)

class ReferralPayout(models.Model):
    payment = models.OneToOneField(Payment, on_delete=models.CASCADE, related_name="referral_payout")
    partner_name = models.CharField(max_length=120)
    amount = models.DecimalField(max_digits=10, decimal_places=2)
    is_paid = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

اس ماڈل کا ڈیزائن صاف علیحدگی فراہم کرتا ہے۔

  • Journey گاہک کی مجموعی ترقی کی نشاندہی کرتا ہے۔

  • Payment ہر مالیاتی تقریب کی نمائندگی کرتا ہے۔

  • ReferralPayout یہ نمائندگی کرتا ہے کہ پارٹنر کو اس ادائیگی سے کتنی آمدنی ہوتی ہے۔

یہ علیحدگی منطق کو قابل انتظام رکھنے کے لیے ہے۔

تقسیم شدہ ادائیگی کیسے کام کرتی ہے۔

قسط کی ادائیگی عام طور پر ایک سادہ پیٹرن کی پیروی کرتی ہے۔

  1. کسٹمر ڈپازٹ ادا کرتا ہے۔

  2. ڈپازٹس کا سسٹم ریکارڈ بنایا

  3. اگر آپ بعد میں ادائیگی کرتے ہیں تو آپ کا بیلنس صاف ہو جائے گا۔

  4. پورا ورک فلو مکمل ہو گیا ہے۔

  5. درست اقدامات کے بعد ہی ڈیلیوری ایبلز کو غیر مقفل کریں۔

اہم حصہ یہ ہے کہ ادائیگی کا ہر مرحلہ واضح ہونا چاہیے۔

اگر آپ ڈپازٹس اور بیلنس کو دو مختلف سنگ میل مانتے ہیں:

  • رعایت صرف ایک درجے پر لاگو ہو سکتی ہے نہ کہ دوسرے درجوں پر۔

  • سفارشی انتساب کو مرحلہ وار ریکارڈ کیا جا سکتا ہے۔

  • ادائیگی صرف اس وقت ہو سکتی ہے جب کوئی مرحلہ درحقیقت مکمل ہو جائے۔

  • منتظمین ورک فلو کی صحیح حیثیت دیکھ سکتے ہیں۔

یہ صرف مقدار سے معنی نکالنے سے کہیں زیادہ محفوظ ہے۔

محفوظ طریقے سے ادائیگی مکمل کریں۔

حتمی منطق سروس فنکشن میں ہونی چاہیے، ویب ہک ویو کے اندر نہیں۔

ایک سادہ مثال یہ ہے:

from django.db import transaction
from django.utils import timezone

def finalize_payment(*, payment):
    with transaction.atomic():
        locked_payment = Payment.objects.select_for_update().select_related("journey").get(pk=payment.pk)

        if locked_payment.status == Payment.STATUS_SUCCEEDED:
            return locked_payment

        locked_payment.status = Payment.STATUS_SUCCEEDED
        locked_payment.finalized_at = timezone.now()
        locked_payment.save(update_fields=["status", "finalized_at"])

        journey = locked_payment.journey

        if locked_payment.stage == Payment.STAGE_DEPOSIT:
            journey.deposit_paid = True
        elif locked_payment.stage == Payment.STAGE_BALANCE:
            journey.balance_paid = True

        if journey.deposit_paid and journey.balance_paid:
            journey.deliverables_released = True

        journey.save(update_fields=["deposit_paid", "balance_paid", "deliverables_released"])

        if journey.referral_code and not hasattr(locked_payment, "referral_payout"):
            ReferralPayout.objects.create(
                payment=locked_payment,
                partner_name=journey.partner_name,
                amount=locked_payment.net_amount * 0.10,
            )

        return locked_payment

یہاں تین اہم خیالات ہیں۔

سب سے پہلے transaction.atomic() یقینی بنائیں کہ اپ ڈیٹس ایک یونٹ کے طور پر کیے گئے ہیں۔

دوسرا select_for_update() دو عملوں کو ایک ہی وقت میں ایک ہی ادائیگی کو مکمل کرنے سے روکنے کے لیے قطاروں کو مقفل کریں۔

تیسرا، یہ چیک کرنے کی اہلیت کہ آیا کارروائی کرنے سے پہلے ادائیگی پر کارروائی ہو چکی ہے۔

یہ ایک محفوظ اور دوبارہ قابل تکمیل راستہ فراہم کرتا ہے۔

ویب ہکس کو سمجھداری سے ہینڈل کرنا

ادائیگی کا گیٹ وے ایک ہی ویب ہک کو ایک سے زیادہ بار بھیج سکتا ہے۔

اس کا مطلب یہ ہے کہ ویب ہُک ہینڈلر کا آئیڈیمپوٹینٹ ہونا ضروری ہے، جس کا سیدھا مطلب یہ ہے کہ اسے ڈپلیکیٹ ریکارڈ بنائے یا کرپٹ ہونے والی حالت کے بغیر کئی بار محفوظ طریقے سے چلایا جا سکتا ہے۔

ایک صاف نمونہ یہ ہوگا:

import json
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

@csrf_exempt
@require_POST
def payment_webhook(request):
    payload = json.loads(request.body.decode("utf-8"))

    event_type = payload.get("event")
    data = payload.get("data", {})
    reference = data.get("reference")

    if not reference:
        return JsonResponse({"error": "Missing reference"}, status=400)

    if event_type != "charge.success":
        return HttpResponse(status=200)

    payment = Payment.objects.filter(gateway_reference=reference).first()
    if not payment:
        return JsonResponse({"error": "Payment not found"}, status=404)

    finalize_payment(payment=payment)
    return HttpResponse(status=200)

یہ نظریہ جان بوجھ کر چھوٹا رکھا گیا ہے۔

یہ کاروباری قوانین کا تعین کرنے کی کوشش نہیں کرتا ہے۔ یہ صرف ویب ہک کو پڑھتا ہے، ادائیگی کی رقم تلاش کرتا ہے، اور اسے سروس لیئر میں منتقل کرتا ہے۔

یہ جانچ اور ڈیبگنگ کو بہت آسان بنا دیتا ہے۔

کوپنز اور ریفرل کنٹریبیوشنز کا اطلاق کریں۔

جب ادائیگیوں کو متعدد مراحل میں تقسیم کیا جاتا ہے تو کوپن اور پارٹنر کوڈ مشکل ہو جاتے ہیں۔

مثال کے طور پر، کوپن صرف ڈپازٹس پر لاگو ہو سکتا ہے۔ یا یہ صرف آپ کے بیلنس پر لاگو ہو سکتا ہے۔ یا شاید یہ دونوں کو متاثر کرتا ہے۔

بہترین حل یہ ہے کہ ان اصولوں کو واضح طور پر محفوظ کیا جائے۔

اسٹیج سے آگاہی کوپن منطق کے لیے یہاں ایک سادہ ماڈل ہے۔

class DiscountCode(models.Model):
    APPLIES_DEPOSIT = "deposit"
    APPLIES_BALANCE = "balance"
    APPLIES_BOTH = "both"

    APPLIES_CHOICES = [
        (APPLIES_DEPOSIT, "Deposit only"),
        (APPLIES_BALANCE, "Balance only"),
        (APPLIES_BOTH, "Both stages"),
    ]

    code = models.CharField(max_length=50, unique=True)
    partner_name = models.CharField(max_length=120, blank=True, default="")
    applies_to = models.CharField(max_length=20, choices=APPLIES_CHOICES, default=APPLIES_BOTH)
    percent_off = models.PositiveSmallIntegerField(default=0)
    is_active = models.BooleanField(default=True)

چیک آؤٹ فلو اب چیک کر سکتے ہیں کہ آیا کوپن لاگو کرنے سے پہلے موجودہ مرحلے کے لیے درست ہے۔

مددگار افعال ہیں:

def calculate_discount(amount, coupon, stage):
    if not coupon or not coupon.is_active:
        return 0

    if coupon.applies_to == DiscountCode.APPLIES_DEPOSIT and stage != Payment.STAGE_DEPOSIT:
        return 0

    if coupon.applies_to == DiscountCode.APPLIES_BALANCE and stage != Payment.STAGE_BALANCE:
        return 0

    return amount * (coupon.percent_off / 100)

یہ آپ کی سفارش اور کوپن منطق کو قابل قیاس رکھتا ہے۔

ریفرل ادائیگیوں کو واضح ہونے کی ضرورت کیوں ہے۔

بہت سے نظام غلطی سے درج ذیل خیالات کو ملا دیتے ہیں:

  • ادائیگی مکمل ہو گئی۔

  • کوپن لگا دیا گیا ہے۔

  • آپ کا حوالہ جمع کر دیا گیا ہے۔

  • حوالہ دینے والے نے ادائیگی کی۔

وہ ایک ہی چیز نہیں ہیں۔

آپ ادائیگی کے ساتھ ریفرل کوڈ منسلک کر سکتے ہیں، لیکن اصل ادائیگی صرف اسی صورت میں پیدا کی جانی چاہیے جب اسے آپ کے کاروباری قواعد کے مطابق محفوظ سمجھا جائے۔

مثال کے طور پر، آپ فیصلہ کر سکتے ہیں:

  • ایک بار جب آپ ڈپازٹ ادا کر دیں گے، آپ کے پارٹنر کو کریڈٹ کر دیا جائے گا۔

  • آپ کا بیلنس ختم ہونے کے بعد ہی ادائیگیاں تیار کی جائیں گی۔

  • ادائیگی کی رقم حتمی خالص ادائیگی کی رقم پر مبنی ہے۔

یہ صارفین کو جلد ادائیگی کرنے سے روکتا ہے اگر وہ پورا بہاؤ مکمل نہیں کرتے ہیں۔

صحیح وقت پر ڈیلیوری ایبلز کو غیر مقفل کریں۔

تقسیم شدہ ادائیگی کے نظام کے ساتھ سب سے بڑی غلطیوں میں سے ایک پہلی ادائیگی کے بعد ہر چیز کو غیر مقفل کرنا ہے۔

اس سے آپریشنل اور اعتماد کے مسائل پیدا ہوتے ہیں۔

ایک بہتر اصول یہ ہوگا:

ہم اس منطق کو بہت سادہ رکھ سکتے ہیں۔ Journey ماڈل:

def update_delivery_state(journey):
    journey.deliverables_released = journey.deposit_paid and journey.balance_paid
    journey.save(update_fields=["deliverables_released"])

منطق پڑھنے کے قابل، قابل آزمائش، اور منتظمین کے لیے سمجھنا آسان ہے۔

عام غلطیاں

وہ غلطیاں جو عام طور پر قسطوں کی ادائیگی کے نظام میں مسائل کا باعث بنتی ہیں ان میں شامل ہیں:

1. ہر چیز کے لیے ایک ادائیگی کا جھنڈا استعمال کریں۔

اکیلا paid=True اگر آپ کے کاروبار میں ادائیگی کے متعدد مراحل ہیں، تو کافی فیلڈز نہیں ہیں۔

2. ویب ہکس کو براہ راست متعدد ٹیبلز پر لکھنے کی اجازت دیں۔

یہ بہاؤ کو جانچنے میں مشکل اور ٹوٹ پھوٹ کا شکار بناتا ہے۔ اس کے بجائے سروس لیئر استعمال کریں۔

3. بے حسی کو بھول جائیں۔

اگر گیٹ وے ویب ہک کی دوبارہ کوشش کرتا ہے، تو اسے ڈپلیکیٹ ادائیگیاں نہیں کرنی چاہئیں یا سفر کو دو بار اپ ڈیٹ نہیں کرنا چاہیے۔

4. اقدامات کی تصدیق کیے بغیر کوپن لگائیں۔

ایک کوڈ جو ڈپازٹ کے لیے درست ہے وہ آپ کے بیلنس کے لیے درست نہیں ہو سکتا۔

5. بہت جلد نتائج جاری کرنا

مکمل ادائیگی کا مطلب ہمیشہ یہ نہیں ہوتا کہ ورک فلو مکمل ہو گیا ہے۔

نتیجہ

ادائیگی کے گیٹ ویز کی بدولت حوالہ سے آگاہ تقسیم ادائیگی کا نظام مشکل نہیں ہے۔ یہ مشکل ہے کیونکہ کاروباری قواعد کثیر سطحی ہیں۔

اپنے سسٹم کے استحکام کو برقرار رکھنے کے لیے، آپ کو:

  • ادائیگی کے ہر قدم کو واضح طور پر ماڈل بنائیں

  • کوپن اور سفارشی منطق کو الگ سے محفوظ کریں۔

  • اندرونی طور پر ادائیگی مکمل کریں۔ transaction.atomic()

  • ایک قطار بند کرو select_for_update()

  • ویب ہُک پروسیسنگ کو کمزور بنانا

  • مکمل ورک فلو مکمل ہونے پر ہی ڈیلیوری ایبلز کو غیر مقفل کریں۔

یہ طریقہ Django ایپس کو ایماندار، قابل شناخت، اور پروڈکٹ کے بڑھنے کے ساتھ برقرار رکھنے میں بہت آسان بنا دیتا ہے۔

Scroll to Top