fix(payments): make checkout attempts idempotent and reconcilable

This commit is contained in:
2026-09-30 21:14:16 +03:30
parent 77567178ad
commit 1daa755543
9 changed files with 862 additions and 173 deletions

View File

@@ -97,6 +97,7 @@ class Payment(BaseModel):
related_name="payments",
editable=False,
)
idempotency_key = models.UUIDField(null=True, blank=True, editable=False)
authority = models.CharField(max_length=64, unique=True, null=True, blank=True, editable=False)
status = models.IntegerField(choices=OrderStatusChoices.choices, default=OrderStatusChoices.INIT, editable=False)
ref_id = models.CharField(max_length=64, null=True, blank=True, editable=False)
@@ -104,6 +105,15 @@ class Payment(BaseModel):
card_hash = models.CharField(max_length=128, null=True, blank=True, editable=False)
verified_at = models.DateTimeField(null=True, blank=True, editable=False)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["user", "event", "idempotency_key"],
condition=Q(idempotency_key__isnull=False),
name="payment_user_event_idempotency_key_uniq",
),
]
def clean(self):
if self.discount_amount and self.amount + self.discount_amount != self.base_amount:
raise ValidationError({"amount": "amount + discount_amount must equal base_amount"})
@@ -119,4 +129,3 @@ class Payment(BaseModel):
def __str__(self):
return f"{self.user.email}:{self.event} - {self.get_status_display()}"