When I started the payments integration for ReclamaAI, I assumed it would be a weekend. Wompi has an SDK, clear docs, and a sandbox that works. Three weeks later I was still fixing edge cases. Here's the list of things I wish someone had told me earlier.
Why Wompi and not Stripe
Stripe in Colombia is possible but awkward: no native PSE, fees are in USD, and refunds in pesos go through conversions that confuse users. Wompi was born in Bogotá, integrates the methods people actually use here (PSE, Nequi, Bancolombia transfer, local cards), and the webhooks arrive in Spanish, in Colombian pesos, no surprises.
The trade-off: Wompi's API is younger than Stripe's. There are undocumented endpoints, inconsistent error messages, and an "events mode" that changed twice in six months. If you're used to Stripe's polish, get ready to improvise.
The webhook that arrived twice
The most expensive trap was idempotency. Wompi can retry a webhook if your server takes more than a few seconds to respond, and the retry arrives with the same transaction.id but a new event.id. If you only dedupe by event, the user ends up with double credits.
What works: apply explicit idempotency using the transaction.id as the key, not the event.id. The exact mechanism matters less than the principle: before applying any payment effect (loading credits, marking an order paid, sending a confirmation email), there must be a guard that silently rejects retries if you already processed that transaction.id. Without that guard, you'll get duplicated emails and double credits sooner or later.
PSE: the most-used method and the one that breaks the most
PSE redirects the user to their bank, where they authorize the debit. When they come back, one of four things can happen: approved, rejected, pending, or never come back. The fourth is the worst — the user closes the tab because their bank kicked them after 10 minutes, and the transaction stays in limbo.
The lesson: never trust the redirect. The source of truth is the webhook, not the return URL. Our flow: the user returns to an intermediate page saying "we're confirming your payment, this can take up to 2 minutes", polls the backend, and only when the webhook confirms do we update state. If after 3 minutes there's no webhook, we show a clear message: "your bank hasn't replied yet, we'll check in the background and email you".
Refunds: the VAT detail
Colombia charges 19% VAT on digital services and that colors every charge. When you refund a payment, Wompi refunds the gross amount, but your accounting needs to track VAT separately. If you don't do it from day one, you'll be reconciling manually at tax time.
Solution: every transaction stores three fields: amount_gross, amount_iva, amount_net. The amount_gross is what the user sees and what Wompi moves; the other two are the breakdown. Refunds store the same breakdown, and reconciliation with the tax authority is just a query.
What's next
Wompi still doesn't have robust recurring billing in Colombia, so we handle subscriptions by hand: expiring credits, top-up reminders, package discounts. It's not elegant but it works and gives us control. When Wompi ships official subscriptions, we'll evaluate migrating — but only if the API is stable. After three weeks integrating this, I learned the most expensive code isn't the code you write, it's the code you rewrite when the provider changes the contract without warning.