Debugging International Payment Failures & Multi-Currency Routing
Cross-border payment processing is one of the most unpredictable engineering challenges in consumer FinTech platforms. During rapid international scaling on AstroKarma, we observed a concerning trend: while domestic transaction authorization rates hovered above 92%, international transactions were dropping to 64.2%, leading to lost revenue and customer frustration.
In this deep-dive case study, I outline how we diagnosed the underlying issuer friction, engineered a pre-authentication gateway router in Python / Django, and used Mixpanel analytics to increase international authorization success rates to 88.7% (+24.5% net improvement).
1. Problem Identification: Where Transactions Failed
When inspecting drop-off logs and analytics funnels, generic decline codes like do_not_honor and generic_decline masked the true underlying friction points.
By querying transaction logs grouped by card issuer country and 3D Secure challenge state, we isolated three root causes:
-- Query to identify failure distribution by card issuing country & 3DS status
SELECT
card_country,
three_ds_status,
COUNT(*) AS total_intents,
COUNT(CASE WHEN status = 'succeeded' THEN 1 END) AS successful,
COUNT(CASE WHEN status = 'failed' THEN 1 END) AS failed,
ROUND(COUNT(CASE WHEN status = 'failed' THEN 1 END) * 100.0 / COUNT(*), 2) AS failure_rate_pct
FROM payment_transactions
WHERE created_at >= NOW() - INTERVAL '30 days'
AND is_international = TRUE
GROUP BY card_country, three_ds_status
HAVING COUNT(*) > 50
ORDER BY failure_rate_pct DESC;
Core Findings:
- 3DS2 Friction & Challenge Drops: European (SCA) and Indian (RBI) issuing banks strictly require 3D Secure 2 authentication. Generic gateway requests were either missing mandatory fields (e.g.
billing_address,email) or incorrectly requestingno_challenge_requested, triggering immediate bank declines. - Cross-Border Interchange Fees: Single-gateway routing through US/EU acquirers led to foreign transaction surcharge blocks by local banks in Southeast Asia and LATAM.
- Lack of Graceful Fallback: When a primary gateway timed out or rejected a card, the mobile client immediately presented an error instead of attempting a secondary acquirer route.
2. Solution: Building the Pre-Auth BIN Gateway Router
To resolve cross-border bank friction without introducing unnecessary 3DS authentication steps for domestic users, I engineered a Pre-Auth BIN Router in our Django backend microservice.
System Architecture Flow:
[Mobile App (iOS/Android)]
โ
โ 1. Submit Card Token + BIN (First 6-8 digits)
โผ
[Django Gateway Router]
โ
โโโบ 2. Query Card BIN Database (Identify Country, Card Type, Issuer)
โโโบ 3. Evaluate Regional 3DS Requirement Rules
โ
โโโโ (If Local/EU) โโโโโโโโโบ [Stripe Local Acquirer + 3DS2 Challenge]
โ
โโโโ (If LatAm / Asia) โโโโโโบ [Regional Gateway Partner + Multi-Currency]
3. Implementation: Dynamic Gateway Selection in Django
import requests
from django.conf import settings
from typing import Dict, Any
class PaymentGatewayRouter:
"""
Dynamically routes charge requests based on Bank Identification Number (BIN)
and regional 3DS compliance requirements.
"""
HIGH_SCA_COUNTRIES = {"DE", "FR", "GB", "IN", "ES", "IT", "NL", "SE"}
@classmethod
def get_bin_metadata(cls, bin_number: str) -> Dict[str, Any]:
"""Lookup BIN country code and card brand from Redis cache or provider."""
cache_key = f"bin_meta:{bin_number[:8]}"
# ... Redis lookup logic ...
return {"country": "IN", "card_type": "credit", "brand": "visa"}
@classmethod
def select_optimal_gateway(cls, card_bin: str, amount: int, currency: str) -> Dict[str, Any]:
bin_info = cls.get_bin_metadata(card_bin)
country = bin_info.get("country")
# 1. Require explicit 3DS2 challenge for SCA / Regional compliance
requires_3ds = country in cls.HIGH_SCA_COUNTRIES or currency != "USD"
# 2. Select Acquirer Gateway based on regional authorization rates
if country in cls.HIGH_SCA_COUNTRIES:
primary_gateway = "stripe_eu_local"
secondary_fallback = "adyen_global"
elif country in {"IN", "SG", "AE"}:
primary_gateway = "razorpay_int"
secondary_fallback = "stripe_global"
else:
primary_gateway = "stripe_global"
secondary_fallback = "checkout_com"
return {
"primary_gateway": primary_gateway,
"secondary_fallback": secondary_fallback,
"requires_3ds": requires_3ds,
"bin_country": country,
}
4. Fallback Retries & Client Re-authentication
If the primary gateway returns a soft decline (e.g. try_another_card or network_timeout), the backend automatically attempts the pre-configured secondary acquirer before notifying the mobile app:
def process_transaction(user, amount: int, currency: str, payment_method_id: str, card_bin: str):
route = PaymentGatewayRouter.select_optimal_gateway(card_bin, amount, currency)
try:
# Attempt Primary Gateway Charge
return execute_charge(route["primary_gateway"], payment_method_id, amount, currency, route["requires_3ds"])
except SoftDeclineException as err:
# Graceful Failover to Secondary Acquirer
logger.warning(f"Primary gateway {route['primary_gateway']} failed. Executing fallback to {route['secondary_fallback']}")
return execute_charge(route["secondary_fallback"], payment_method_id, amount, currency, route["requires_3ds"])
5. Measurable Impact & Key Results
After deploying the BIN router and optimizing 3DS2 challenge prompts across iOS and Android builds:
| Business & Technical Metric | Before Optimization | After Optimization | Net Improvement | | --- | --- | --- | --- | | International Success Rate | 64.2% | 88.7% | +24.5% net gain | | 3DS2 Challenge Completion | 68.4% | 92.8% | +24.4% | | Checkout Abandonment | 31.5% | 11.2% | -20.3% drop-off | | Average Checkout Latency | 3.1s | 1.6s | ~48% faster |
6. Key Takeaways for Engineering Leaders
- Don't Treat All Declines Equally: Differentiating between hard declines (insufficient funds) and soft declines (3DS protocol errors) allows you to implement automatic fallback retries without disrupting the user.
- BIN-Aware Routing Saves Conversions: Passing card issuer country metadata to gateway endpoints dramatically increases bank authorization approval rates for international cards.
- Data-Driven Funnel Tracking: Instrumenting custom Mixpanel events (
Checkout Started,3DS Prompt Shown,Gateway Handshake Complete) is essential for identifying geographic checkout drop-offs.