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

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:
Cauê Faleiros
2026-09-28 15:40:26 -03:00
parent 875a7ef9c7
commit 641aafc87d
7 changed files with 81 additions and 10 deletions

View File

@@ -12,6 +12,7 @@ from urllib.error import HTTPError
from urllib.request import Request, urlopen
from uuid import uuid4
from app.core import db
from tests.smoke_test import BASE, Client, approved_quote, upload_bytes, item_spec, with_host
SECRET = os.environ.get('PAYMENT_WEBHOOK_SECRET', 'local-webhook-secret').encode()
@@ -60,7 +61,22 @@ def run():
# Starting a PIX twice returns the same one. A card in review blocks every
# further attempt, so one quote can never be charged twice.
pix = customer.call('/payments/intent', {'quote_id': quote_id, 'method': {'type': 'pix'}})
assert pix['expires_at']
assert customer.call('/payments/intent', {'quote_id': quote_id, 'method': {'type': 'pix'}})['id'] == pix['id']
# Once the code has expired, asking again opens a new one, and only one.
with db.connect() as c:
c.execute('''UPDATE dtf_local.payment_intents
SET response=jsonb_set(response,'{expires_at}',to_jsonb((now()-interval '1 minute')::text))
WHERE provider_payment_id=%s''', (pix['id'],))
renewed = customer.call('/payments/intent', {'quote_id': quote_id, 'method': {'type': 'pix'}})
assert renewed['id'] != pix['id'], 'an expired PIX was offered again'
assert customer.call('/payments/intent', {'quote_id': quote_id, 'method': {'type': 'pix'}})['id'] == renewed['id']
with db.connect() as c:
statuses = {r['provider_payment_id']: r['status'] for r in c.execute(
"SELECT provider_payment_id,status FROM dtf_local.payment_intents WHERE quote_id=%s AND method='pix'",
(quote_id,)).fetchall()}
assert statuses == {pix['id']: 'expired', renewed['id']: 'pending'}, statuses
print('PASS: a PIX code expires after 30 minutes and is replaced by exactly one new code')
customer.call('/payments/intent', {'quote_id': quote_id, 'method': {'type': 'card'}}, expected=422)
card = {'type': 'card', 'token': 'tok-1', 'payment_method_id': 'visa', 'installments': 1}
customer.call('/payments/intent', {'quote_id': quote_id, 'method': card})