diff --git a/app/api/operator.py b/app/api/operator.py index 6492c85..7908785 100644 --- a/app/api/operator.py +++ b/app/api/operator.py @@ -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') diff --git a/web/kanban.js b/web/kanban.js index 3517abe..5e8a330 100644 --- a/web/kanban.js +++ b/web/kanban.js @@ -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));