feat: PIX codes expire after 30 minutes, with a countdown
All checks were successful
Build and deploy / Validate source (push) Successful in 11s
Build and deploy / Integration suite on a real stack (push) Successful in 2m50s
Build and deploy / Secret scan and release gate (push) Successful in 8s
Build and deploy / Publish images (push) Successful in 1m54s
All checks were successful
Build and deploy / Validate source (push) Successful in 11s
Build and deploy / Integration suite on a real stack (push) Successful in 2m50s
Build and deploy / Secret scan and release gate (push) Successful in 8s
Build and deploy / Publish images (push) Successful in 1m54s
A PIX is created with a 30-minute date_of_expiration, and the PIX page counts down to it. When it runs out the page says the code expired and offers a new one. The API keeps one open code per quote: the same code until it expires, then exactly one new attempt (serialised per quote, with its own idempotency key), the old one marked expired. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,14 +84,25 @@ def intent(body: PaymentIntent, session_id=Depends(owner)):
|
||||
AND created_at < now() - make_interval(mins => %s))))''',
|
||||
(body.quote_id, CHALLENGE_MINUTES)).fetchone():
|
||||
raise HTTPException(409, 'A payment for this quote is already approved or in review')
|
||||
method = body.method.model_dump()
|
||||
if body.method.type == 'pix':
|
||||
existing = c.execute('''SELECT * FROM dtf_local.payment_intents WHERE quote_id=%s AND method='pix'
|
||||
# One open PIX per quote: the same code until it expires, and a new
|
||||
# one only after that, when the old code can no longer be paid.
|
||||
# Serialised per quote, so two clicks never open two codes.
|
||||
c.execute('SELECT pg_advisory_xact_lock(hashtext(%s))', ('pix:'+str(body.quote_id),))
|
||||
existing = c.execute('''SELECT *, COALESCE((response->>'expires_at')::timestamptz <= now(), false) AS expired
|
||||
FROM dtf_local.payment_intents WHERE quote_id=%s AND method='pix'
|
||||
AND status='pending' ORDER BY created_at DESC LIMIT 1''', (body.quote_id,)).fetchone()
|
||||
if existing:
|
||||
if existing and not existing['expired']:
|
||||
return existing['response']
|
||||
if existing:
|
||||
c.execute("UPDATE dtf_local.payment_intents SET status='expired', updated_at=now() WHERE id=%s",
|
||||
(existing['id'],))
|
||||
method['attempt'] = c.execute('''SELECT count(*) AS n FROM dtf_local.payment_intents
|
||||
WHERE quote_id=%s AND method='pix' ''', (body.quote_id,)).fetchone()['n'] + 1
|
||||
try:
|
||||
created = payment.create(str(body.quote_id), quote['approved']['total_cents'],
|
||||
quote['approved']['customer'], body.method.model_dump())
|
||||
quote['approved']['customer'], method)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(422, str(exc))
|
||||
except Exception:
|
||||
|
||||
Reference in New Issue
Block a user