Common Integration Mistakes
Quick Reference
1. Using production credentials in sandbox
Symptom: 401 Unauthorized on every request to sandbox.api.a55.tech.
Fix: Sandbox and production use separate Cognito pools. Use sandbox client_id/client_secret with the sandbox auth URL, and production credentials with the production auth URL.
2. Not handling 3DS redirect
Symptom: Charges with 3DS-enrolled cards stay in awaiting_3ds forever.
Fix: When the charge response includes a redirect_url, redirect the cardholder to that URL. After authentication, the user returns to your callback_url and you must poll the charge status.
3. Hardcoding API tokens
Symptom: Tokens expire after 1 hour, and every request fails with 401.
Fix: Never hardcode tokens. Implement a token manager that calls the OAuth2 endpoint before expiry:
import time
class TokenManager:
def __init__(self):
self._token = None
self._expires_at = 0
def get_token(self):
if time.time() > self._expires_at - 60:
self._refresh()
return self._token
def _refresh(self):
# Call OAuth2 token endpoint
self._token = response["access_token"]
self._expires_at = time.time() + response["expires_in"]
4. Not verifying webhook signatures
Symptom: Your system processes forged webhook payloads, leading to incorrect order status updates.
Fix: Always verify the HMAC-SHA256 signature before processing:
import hmac
import hashlib
def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
5. Missing idempotency keys on retries
Symptom: Customer is charged twice because a timeout triggered a retry without the same Idempotency-Key.
Fix: Generate a UUID v4 once per business operation and send it on every attempt:
import uuid
idempotency_key = str(uuid.uuid4()) # Generate ONCE
# Use the same key on retries
headers = {"Idempotency-Key": idempotency_key}
6. Wrong amount format
Symptom: 422 Unprocessable Entity with validation error on amount.
Fix: Amount format depends on the currency's decimal places:
| Currency | Decimals | Correct | Wrong |
|---|---|---|---|
| BRL, MXN, USD, EUR, PEN, ARS | 2 | 150.00 | 150, "150", 150.0 |
| CLP, COP | 0 | 50000 | 50000.00, 50000.50 |
Chilean Peso and Colombian Peso have no fractional units. The backend rounds amounts to integers for these currencies. Sending 50000.50 for CLP results in 50000. Always send whole numbers for CLP and COP to avoid unexpected truncation.
7. Missing required payer fields
Symptom: 422 error listing missing fields like payer.document or payer.email.
Fix: Every charge requires at minimum:
{
"payer": {
"name": "Customer Name",
"email": "customer@example.com",
"document": "12345678909",
"document_type": "CPF"
}
}
Document requirements vary by country (CPF for Brazil, RFC for Mexico).
8. Not implementing retry with exponential backoff
Symptom: Your system hammers the API after a 500 error, gets rate-limited, and makes the outage worse.
Fix: Implement exponential backoff with jitter:
import time
import random
for attempt in range(4):
response = make_request()
if response.status_code < 500:
break
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
9. Ignoring rate limits
Symptom: Sudden 429 Too Many Requests responses during peak traffic.
Fix: Read rate limit headers (X-RateLimit-Remaining, X-RateLimit-Reset) and throttle proactively. See the rate limiting guide.
10. Not checking charge status after creation
Symptom: Order marked as paid immediately after charge creation, but the charge was actually pending or declined.
Fix: After creating a charge, check the status field in the response. For async methods (PIX, Boleto), set up webhooks to receive status updates — do not assume the charge is confirmed.
11. Missing webhook_url in charge creation
Symptom: No webhook notifications arrive, even though your endpoint works.
Fix: Include webhook_url in the charge creation request body, or configure a default webhook URL in your merchant settings.
12. Wrong currency for country
Symptom: 422 error or unexpected conversion fees.
Fix: Use the correct currency for each country. Pay special attention to zero-decimal currencies:
| Country | Currency | Decimals | Document type | Example amount |
|---|---|---|---|---|
| Brazil | BRL | 2 | CPF / CNPJ | 150.00 |
| Mexico | MXN | 2 | RFC / CURP | 2500.00 |
| Chile | CLP | 0 | RUT | 50000 |
| Colombia | COP | 0 | CC (Cédula) | 200000 |
| Peru | PEN | 2 | DNI | 350.00 |
| Argentina | ARS | 2 | DNI / CUIT | 85000.00 |
13. Not handling partial captures correctly
Symptom: Authorized amount is fully captured when only a partial shipment was made.
Fix: When capturing less than the authorized amount, explicitly set the capture amount:
{
"amount": "75.00"
}
The remaining authorized amount is automatically released.
14. Missing error handling in code
Symptom: Unhandled exceptions crash your application when the API returns unexpected responses.
Fix: Wrap every API call in try/catch and handle all possible HTTP status codes. Log the request_id for every failed request.
15. Not testing all card decline scenarios
Symptom: Application crashes or shows generic error when a card is declined.
Fix: Test with all decline test cards and verify your UI shows appropriate messages:
| Test Card | Scenario |
|---|---|
4000 0000 0000 0002 | Card declined |
4000 0000 0000 0069 | Processing error |
4000 0000 0000 0119 | Insufficient funds |