Skip to content

Every New Payment Provider Adds Another API, Webhook Format and Refund Flow

A checkout page looks simple from the outside. A customer taps a button, money moves, and an order appears. Behind that button sits a stack of integrations, and each one arrives with its own documentation, its own authentication scheme and its own idea of what a “successful” payment means.

Teams usually start with one provider and a tidy service class. Six months later the product needs cards, a local bank transfer method and a wallet popular in a new market. Engineers who want to see how a unified layer can sit in front of many providers can look at one platform for every payment option, which shows the idea of a single integration point in practice.

Where the Integration Cost Actually Hides

The first API call is never the expensive part. A developer can create a test charge in an afternoon. Pain arrives with provider number two, when a column named card_token turns out to be quietly sprinkled through forty separate files of the codebase.

Take a small shop selling online courses. For a 49 dollar course, Provider A sends 4900 and the word “succeeded” in one reply. Provider B writes the same price as “49.00” and says “captured” instead. Provider C answers with nothing, then reports the outcome ninety seconds later. Yet the shop keeps a single orders table, and every row in it must read the same.

AreaProvider AProvider BProvider C
Amount formatInteger centsDecimal stringInteger minor units
Final statussucceededcapturedPAID
Webhook signatureHMAC headerPublic keyIP allowlist only
Refund window180 days90 daysProvider decides

Three Layers That Multiply With Every Provider

Most of the pain falls into three layers. The request API is the visible one, but webhooks and refunds quietly cause more late night debugging, because they run on the provider’s schedule rather than yours.

Each layer can be wrapped in an adapter. The trouble is that adapters only hide differences, they do not remove them. Every new provider still needs mapping tables, test fixtures and someone who has read the fine print on edge cases.

Webhooks Arrive Late, Twice or Out of Order

When a bank finally approves a transfer, the provider calls a URL you own. One nests the amount deep, another keeps it flat. Some providers sign the raw body, others sign a canonical string built from selected fields, and a few sign nothing at all.

Seneca Falls Country Club - Billboard 728x440

Delivery is the bigger headache. Retries mean the same event can show up three times over a day. A refund notice may land before the payment confirmation it depends on. If your handler assumes a tidy sequence, an order can end up marked as refunded and unpaid at once. Treat the event as a knock on the door, then fetch the payment yourself before changing any order. Keep a table of processed event IDs and simply skip anything already in it. That small habit prevents a surprising share of support tickets, and it costs perhaps twenty short lines of code in most modern languages.

Refund Flows Rarely Look Alike

On day 100, one provider allows a refund and another flatly refuses. In a test, one provider confirmed a refund in 300 milliseconds, yet the cardholder saw nothing for a full seven days. A few insist on a finance lead’s sign off, and cap partial refunds at two per order. Rules also differ on what can be refunded. A card payment may be reversed in full, while a bank transfer may need the customer’s account details collected again. A support agent should not need to know any of this. Behind a refund button, the agent never sees which vendor answered.

Four questions worth asking the documentation before any new contract gets signed:

  • Does each event carry a stable unique ID?
  • Can a refund be partial, and how many times?
  • Is “paid” ever revised later, after the first confirmation arrives?
  • Can the test environment fake a late or failed callback?

Reconciliation and One Internal Contract

Accountants match deposits to orders monthly. Totals rarely agree on the first try. Settlement reports differ too. One is a Monday CSV. Another is a nightly XML dump. Fees hide inside it. No shared ledger, so month end gets manual. Someone matches rows by hand for several days. A good habit is to store every money movement as an internal record with a normalized currency, amount and status, plus a link to the raw provider payload. A 3.20 mismatch on Tuesday traces back to one single stored payload.

The practical answer is boring and effective. Define a small internal payment model, with a handful of statuses, and translate every provider into it at the edge. Quirks such as Provider C’s missing signature stay locked inside one small module, carefully replayed against saved responses from real failed payments. By provider four, effort drops to a few days instead of a few weeks. Adding provider four then means one new translator file and a round of recorded fixtures, while the checkout screens and the reports never get touched.



Categories: NewsBusiness