Files
dtf-system/app/mercadopago_probe.py
Cauê Faleiros 593ddf82c8
All checks were successful
Build and deploy / Validate source (push) Successful in 4s
Build and deploy / Integration suite on a real stack (push) Successful in 2m3s
Build and deploy / Secret scan and release gate (push) Successful in 5s
Build and deploy / Publish images (push) Successful in 45s
feat: add a read-only Mercado Pago account probe
Card payments with the test credentials are refused with 10111 and 10113,
which depend on the account behind the token. python -m
app.mercadopago_probe shows that account (and whether it is a test user),
the card methods it accepts, and how Mercado Pago classifies a card's first
digits: type, issuer and instalments. It creates and charges nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 13:47:39 -03:00

69 lines
2.9 KiB
Python

"""Read-only look at the Mercado Pago account behind MP_ACCESS_TOKEN.
python -m app.mercadopago_probe [--bin 503143] [--valor 50]
Run from the api container's console (Portainer > Containers > api >
Console). It creates nothing and charges nothing: it asks Mercado Pago which
account the token belongs to, which card methods the account accepts, and how
it classifies a card number's first digits (type, issuer, instalments). That
is what payment errors such as 10111 (issuer) and 10113 (method excluded by a
rule) depend on.
"""
import argparse
import os
import sys
import httpx
from .core.secrets import load as load_secret_files
API = 'https://api.mercadopago.com'
def main(argv=None):
parser = argparse.ArgumentParser(prog='python -m app.mercadopago_probe')
parser.add_argument('--bin', default='503143', help="the card's first six digits (default: the Mastercard test card)")
parser.add_argument('--valor', default='50', help='amount in reais for the instalment lookup')
args = parser.parse_args(argv)
load_secret_files()
token = os.environ.get('MP_ACCESS_TOKEN', '')
if not token:
print('MP_ACCESS_TOKEN is not set in this container.')
return 1
http = httpx.Client(base_url=API, timeout=15, headers={'Authorization': f'Bearer {token}'})
print(f"Token: {'TEST' if token.startswith('TEST-') else 'produção'}")
me = http.get('/users/me')
if me.status_code == 200:
user = me.json()
tags = user.get('tags') or []
print(f"Conta: id {user.get('id')} · {user.get('nickname')} · site {user.get('site_id')} · "
f"usuário de teste: {'sim' if 'test_user' in tags else 'não'} · tags {tags}")
else:
print(f'Conta: HTTP {me.status_code} {me.text[:200]}')
methods = http.get('/v1/payment_methods')
if methods.status_code == 200:
cards = [m for m in methods.json() if m.get('payment_type_id') in ('credit_card', 'debit_card')]
print('Cartões aceitos pela conta:')
for m in cards:
print(f" {m.get('id'):<12} {m.get('payment_type_id'):<12} {m.get('status')}")
else:
print(f'Meios de pagamento: HTTP {methods.status_code} {methods.text[:200]}')
quote = http.get('/v1/payment_methods/installments', params={'bin': args.bin, 'amount': args.valor})
if quote.status_code == 200 and quote.json():
print(f'BIN {args.bin}:')
for option in quote.json():
issuer = option.get('issuer') or {}
counts = [c.get('installments') for c in option.get('payer_costs') or []]
print(f" meio {option.get('payment_method_id')} · tipo {option.get('payment_type_id')} · "
f"emissor {issuer.get('id')} ({issuer.get('name')}) · parcelas {counts}")
else:
print(f'BIN {args.bin}: HTTP {quote.status_code} {quote.text[:300]}')
return 0
if __name__ == '__main__':
sys.exit(main())