The Specific Ways Cross-Border Payment Processing Fails
Cross-border payment failures aren't caused by the API. They're caused by assuming the payment model is the same on both sides of the border. Here are the specific failure modes we hit building PayTable in Mexico.
By Igor Riera
If you’ve read our earlier post on payment processing in Mexico, you know the landscape: different processors, inclusive tax, mandatory electronic invoicing, settlement through PROSA and SPEI. That post covers what it looks like. This one covers what breaks, and why it breaks in ways that aren’t obvious until you’re in production.
The failure pattern is always the same: you assume the payment model works the same way on both sides of the border, and it doesn’t. Not just the API - the model. Tax, fraud, settlement timing, invoicing, and cash handling all behave differently, and each difference has consequences that ripple into systems you didn’t expect.
What “it works in the US” actually assumes
Before we get into the failure modes, it’s worth naming the baseline assumptions baked into a US-centric payment implementation. These are the things you don’t know you’re assuming because they’ve always been true.
Stripe-like processors that abstract the banking rails. Tax that’s additive - the menu price is before tax, the receipt adds it. AVS as a first-line fraud signal. Settlement that’s opaque but predictable: Stripe settles in two business days, the abstraction holds, you wire up a bank account and move on.
None of these hold in Mexico. And the problem isn’t that they’re wrong in some abstract sense - it’s that every one of them is encoded in your data models, your fraud rules, your UI, and your accounting integration before you write a single line of Mexico-specific code.
IVA inclusive pricing is a data model problem, not a tax problem
IVA (Impuesto al Valor Agregado) is Mexico’s value-added tax at 16%. In Mexico, it’s inclusive. The number on the menu is the number the customer pays.
In the US, you add tax after the fact. $10.00 becomes $10.87. In Mexico, the $10.00 is already the final price - the IVA is inside it, not on top.
This sounds like a single configuration change. It isn’t. Here’s where it actually lands:
Receipt rendering. You’re not calculating forward from a subtotal. You’re calculating backward from the total to decompose the base amount and the IVA component. base = total / 1.16, iva = total - base. The display logic is different, the rounding behavior is different, and the receipt format the SAT expects reflects this decomposition explicitly.
Refund logic. A partial refund on an inclusive-tax item requires recalculating the tax component of the portion being refunded. If your refund path just refunds a dollar amount without tracking the tax breakdown, your SAT reporting drifts from your actual transactions. Silently.
Accounting integration. Every transaction needs IVA collected tracked separately from the base amount. The restaurant’s books need to reflect this, and any integration to external accounting software has to model it the same way. If the integration was built for US-style additive tax, it will sum the wrong fields.
Menu pricing in the admin UI. When a restaurant owner sets an item price, they’re setting the final customer-facing price. There is no “price before tax” to enter. An admin interface that shows a net price field and a tax field, as US restaurant software commonly does, is incorrect in the Mexican context - and will confuse every operator you onboard.
One tax rule. Four downstream systems. Each one is a place where the US-centric assumption silently produces wrong output, and none of them will throw an exception to tell you.
No AVS means rebuilding fraud scoring from scratch
In the US, AVS is table-stakes for card-not-present fraud. You send a billing address, the issuing bank returns a match or mismatch, and it feeds your fraud score. It’s not perfect, but it’s a signal every processor and fraud tool expects to have.
Mexican banks don’t participate in the AVS network. Visa and Mastercard only mandate AVS support for the US, Canada, UK, and a subset of European issuers. Processing Mexican-issued cards in Mexico - which is the vast majority of PayTable’s volume - AVS responses are either unavailable or meaningless.
Any fraud ruleset ported from a US platform that uses AVS as a scoring input needs to be disabled. Not tuned down. Disabled. The signal isn’t noisy; it’s absent. And a fraud model with a missing primary signal isn’t degraded - it has a hole where the hole shouldn’t be.
We rebuilt around three inputs. Device fingerprinting via FingerprintJS Pro, captured at session start (on the home screen, before the checkout flow, with a fallback capture on the checkout view as a safety net). Billing zip code (código postal) collected at payment - it doesn’t return an issuer-side match, but it adds friction that deters opportunistic fraud and provides evidence for dispute resolution. And velocity checks across device, session, and card combinations to catch replay patterns.
This isn’t a port of the US playbook with different parameters. It’s a different fraud architecture built to operate without the signal the US playbook depends on.
One additional complexity: the approval rate context matters here. According to CONDUSEF, only 62% of online card transactions in Mexico are approved. The false decline rate across Latin America approaches 50%. Aggressive fraud rules that are appropriate in the US - where baseline approval rates exceed 90% - will crater conversion in Mexico. Fraud scoring has to optimize for precision over recall, because every false decline has a higher business cost than it would in a market where customers are already comfortable paying online.
SPEI is real-time rails, not real-time settlement
SPEI (Sistema de Pagos Electrónicos Interbancarios) is Banxico’s real-time interbank system. It’s genuinely fast - 5.34 billion operations in 2024, settling effectively instantly between bank accounts. It’s a meaningful piece of financial infrastructure.
It’s also the last step in a pipeline that isn’t fast.
Here’s the actual sequence: card authorization runs through the card network. The acquiring bank does its reconciliation cycle. T1 Pagos’s clearing schedule runs. Then SPEI moves money. SPEI is fast. Everything upstream of SPEI runs on its own timeline.
PayTable models payment state explicitly: Pending → Authorized → Captured → Reconciled → Disbursed. Each state is a real event in the banking system, not an internal label we invented. Authorized means T1 confirmed the card and funds. Captured means the charge is submitted for settlement. Reconciled means T1 confirmed it settled with the acquiring bank - typically one to three days. Disbursed means SPEI deposited funds into the restaurant’s account - typically three to seven days from capture.
The UI has to show this clearly. A shift closeout report that only shows captured payments is showing intent, not cash. Restaurant operators making staffing and inventory decisions based on payment data need to know whether “this money was charged” and “this money is in your bank account” are the same thing. For a newly captured payment, they’re not.
T1 fires webhooks at each settlement gate. Each webhook carries an event ID. We deduplicate on that ID before processing - concurrent duplicate events hit a unique constraint and get dropped. State transitions only advance forward. A reconciled payment can’t regress to captured because a stale webhook arrived with old data.
If you build your settlement system assuming the money moves as fast as SPEI moves it, you’ll build a system that shows restaurant owners cash they don’t have yet.
CFDI invoicing can’t be deferred
CFDI (Comprobante Fiscal Digital por Internet) is Mexico’s mandatory electronic invoicing system. Every transaction that requires a tax receipt must generate a digitally signed XML document submitted to the SAT through a certified authorization provider (PAC). This isn’t an enterprise feature or a compliance tier. It’s the law for any business that issues receipts.
In a restaurant context, this means a customer at the table can request an official tax invoice from their phone at the moment of payment. The system generates the XML, the PAC signs and stamps it with a folio fiscal, and the customer receives a legally valid document.
The engineering surface is specific: the XML must conform to SAT’s schema, include the correct IVA breakdown and payment method codes, reference the issuer’s fiscal regime, and be stored retrievably for up to five years. The generation and PAC round-trip has to complete in seconds - a customer waiting at a restaurant counter won’t wait on a slow invoicing process.
We integrated CFDI generation directly into PayTable’s order completion flow. The path is synchronous for the customer and asynchronous for error handling: if the PAC call fails, we retry with backoff and surface the failure to the admin dashboard rather than blocking the table.
What we didn’t initially appreciate is that CFDI creates a second audit trail alongside the payment system. A charge without a matching invoice is visible. A voided transaction without a corresponding CFDI cancellation is an anomaly. This isn’t real-time fraud prevention, but the SAT’s retention requirements mean the forensic record persists. It’s audit infrastructure the regulatory environment provides.
You can’t skip it, defer it, or scope it to large orders. It applies to any transaction where the customer requests it, which means the infrastructure has to be ready on every transaction.
Cash payments alongside digital require timeout logic
PayTable supports cash payments as an explicit mode, not because cash is idealized, but because a significant portion of Mexican restaurant customers pay in cash and a system that can’t handle it isn’t deployable.
The flow: a customer selects cash payment on their phone. The order records a pending cash payment. The waiter confirms the cash transaction in the waiter app within 30 minutes. If the waiter doesn’t confirm, the order times out and reverts.
This dual-mode design creates a specific reconciliation problem. At any moment, the system has orders in three states from a payment perspective: confirmed digital payments, pending cash payments waiting on waiter confirmation, and expired pending cash claims. Shift reconciliation has to classify all three correctly. A restaurant owner looking at end-of-day totals shouldn’t see a pending cash order from four hours ago as an open transaction.
The timeout logic also has to be atomic. Two events can arrive close together: the 30-minute expiry fires, and the waiter confirms at almost the same moment. Without advisory locks on the payment record during state transition, you can end up with a race where both the expiry and the confirmation attempt to process. One of them will win; the question is which one. We use advisory locks at the database level to ensure transitions are serialized, not concurrent.
Designing for failure, not success
The thread running through all of these failure modes is the same one that runs through any financial system built to operate at scale: the expected path is easy, the failure paths are where the system actually gets built.
PayTable’s payment layer uses advisory locks for financial consistency - no concurrent modifications to a payment record during state transitions. Circuit breakers around T1 Pagos, the CFDI PAC, and SPEI webhook ingestion, so that an external system outage degrades gracefully rather than cascading. Hard transaction limits per session and per device, so that a fraud pattern can’t run unchecked while the fraud scoring model evaluates it.
None of these are present in the first working version of a payment system. They’re the output of designing for the failure modes before they happen - which is only possible if you understand what the failure modes are.
The US payment model has its own failure modes. We know them well enough that we’ve built intuition around them. The Mexico failure modes are different, and the intuition doesn’t transfer. What transfers is the principle: build the reconciliation path first, model every state explicitly, and treat every external system as something that will eventually deliver data you didn’t expect.
That’s what cross-border engineering actually costs. Not the API integration. The assumptions you have to find and discard before the integration can be correct.