feat: report Mercado Pago's notifications in the account check
All checks were successful
Build and deploy / Validate source (push) Successful in 5s
Build and deploy / Integration suite on a real stack (push) Successful in 2m16s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 45s

Without access to the Mercado Pago panel, "Verificar conta" now also says
how many payment notifications arrived and passed the signature in the last
24 hours, how many were refused by it, and the last one received: the PIX
payments created in test mode notify the webhook, so this shows whether
Mercado Pago reaches the server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cauê Faleiros
2026-09-29 14:35:47 -03:00
parent b5bb03cbb4
commit 64c1260a9f
2 changed files with 16 additions and 2 deletions

View File

@@ -310,7 +310,18 @@ def mercadopago_check(user=Depends(operator)):
raise HTTPException(409, 'Mercado Pago não está configurado')
from ..mercadopago_probe import check
audit('mercadopago_check', operator=user)
return check(payment.access_token)
result = check(payment.access_token)
# Whether Mercado Pago's notifications reach this server and pass the
# signature: every accepted one is recorded, a refused one is audited.
with db.connect() as c:
received = c.execute('''SELECT count(*) AS n, max(received_at) AS last FROM dtf_local.payment_events
WHERE provider='mercadopago' AND received_at > now()-interval '24 hours' ''').fetchone()
last = c.execute('''SELECT received_at,status,outcome FROM dtf_local.payment_events
WHERE provider='mercadopago' ORDER BY received_at DESC LIMIT 1''').fetchone()
refused = c.execute('''SELECT count(*) AS n FROM dtf_local.security_events
WHERE event='payment_webhook_rejected' AND created_at > now()-interval '24 hours' ''').fetchone()
result['webhooks'] = {'accepted_24h': received['n'], 'refused_24h': refused['n'], 'last': last}
return result
@router.get('/api/operator/tiny/callback')

View File

@@ -679,7 +679,10 @@ function renderIntegrations(){
const conta=r.account.error?'conta: '+r.account.error:'conta '+r.account.nickname+' ('+r.account.id+') · '+
(r.account.test_user?'usuário de teste':'não é usuário de teste');
const bin=Array.isArray(r.bin)?(r.bin.length?r.bin.map(o=>o.method+' '+o.type+' · '+o.issuer+' · até '+Math.max(...o.installments)+'x').join('; '):'cartão de teste não reconhecido'):'cartão: '+r.bin.error;
say('Mercado Pago: credencial '+(r.token==='test'?'de teste':'de produção')+' · '+conta+' · '+bin,false,60000);
const w=r.webhooks||{};
const avisos='avisos nas últimas 24 h: '+w.accepted_24h+' aceitos, '+w.refused_24h+' recusados pela assinatura'+
(w.last?' · último em '+new Date(w.last.received_at).toLocaleString('pt-BR')+' ('+w.last.status+')':' · nenhum recebido ainda');
say('Mercado Pago: credencial '+(r.token==='test'?'de teste':'de produção')+' · '+conta+' · '+bin+' · '+avisos,false,60000);
},'btn ghost')]:[];
cards.push(integration('Mercado Pago',mp==='mercadopago'?'Ativo':'Não configurado',mp==='mercadopago'?'ok':'off',
mp==='mercadopago'?'PIX e cartão ativos.':'Aguardando credenciais.',mpActions));