Skip to content
← Articles / engineering

Adding online payments to your app: what to plan for

Adding online payments to a web or mobile app? What to plan beyond the checkout form: webhooks, double charges, refunds, disputes, tax and test mode.

TC
Tri Cahyo
3 min read · engineering

"Can we take payments online?" sounds like one feature. In practice it touches almost every part of an app: orders, emails, the admin side, accounting and support. Most of the work isn't the payment form. It's everything that has to happen after someone pays, or after a payment fails.

This is what I plan for when a business asks me to add payments to a web or mobile app.

Taking the money is the easy part. Knowing for sure that you took it is the work.

Let the provider handle the card

I don't build my own card form. Providers like Stripe, PayPal or Adyen offer hosted checkout pages and embedded fields where the card details go straight to them and never touch your server. That keeps you out of most of the card-security rules, and it gives customers a form they already trust, with wallets like Apple Pay and Google Pay included.

Pick the provider by where your customers are, which currencies and payment methods they expect, and what your accountant needs from it. Not by which one has the nicest logo.

Trust the webhook, not the browser

The most common mistake I see in payment code: the app marks an order as paid when the customer lands on the "thank you" page. But people close tabs, lose signal and press back. And some payment methods take minutes or days to confirm.

The reliable signal is the provider's webhook, a message from their server to yours saying the payment succeeded, failed or was refunded. The order changes status only when that message arrives and is verified. The thank-you page just shows what the server already knows.

Make every step safe to repeat

Webhooks can arrive twice, late or out of order. A customer can double-click "pay". So each step has to be safe to run more than once:

  • the payment request carries a unique key, so a retry doesn't charge twice
  • a webhook that was already handled is ignored the second time
  • an order can't go from "refunded" back to "paid" because an old message arrived late

This is invisible when it works and very visible when it doesn't, usually as a customer charged twice.

Plan for the unhappy paths

Success is one path. These are the others, and each needs a decision:

  • Failed payment: keep the cart, show a clear reason, let them try another method.
  • Refund, full or partial: who can issue it, and what happens to the order and the stock.
  • Dispute or chargeback: who gets told, and what evidence you keep.
  • Subscription renewal fails: how many retries, what the customer sees, when access stops.

Write these down with the people who handle money and support. They are business rules, not code details.

Don't forget the paperwork

Payments come with obligations that vary by country: tax on the invoice, receipts, which details the invoice must show. Many providers can calculate tax and send receipts for you. Decide early what your provider does and what your app or accounting tool has to do, so nothing is done twice or not at all.

Test with fake money first

Every major provider has a test mode with test cards for success, failure, slow confirmation and disputes. I run through every path above in test mode, including webhooks that arrive twice, before anything goes live. Then one small real payment and a refund on production, to be sure.

# payments
hosted checkout or provider fields ✓
order paid only on a verified webhook ✓
retries can't charge twice ✓
refunds and disputes have an owner ✓
tax and receipts decided ✓
every path tried in test mode ✓
one real payment and refund on live ✓

Adding payments to your app

Tell me what you sell, where your customers are and what your app runs on today. Describe it on the scope page or get in touch, and I will tell you which parts above matter most for your case.

TC
Written by Tri Cahyo

Freelance full stack engineer in Jakarta. I build web and mobile apps, rescue unmaintained software and join busy teams.

Inherited software that keeps breaking?