Working with money: payments and charges
Dividing responsibility
The payment provider handles the card; you handle the order. Details are entered in the provider's form or an isolated component, and only an identifier is stored on your side. That keeps you out of most of the heavy compliance world.
The amount is computed on the server from your prices. An amount that came from the client is a request, not a fact.
States, not flags
An order moves through defined states: created, awaiting payment, paid, failed, refunded. Every transition is recorded with a time and a reason. A boolean “paid” flag loses the story exactly when you need it.
Notifications from the payment provider arrive out of order and sometimes twice. Handle them by event identifier, in a way that repeats safely.
Reconciliation
Build a daily process that compares your records to the provider's report. Discrepancies always happen; the question is whether you find them or the customer does.
Going deeper
Store amounts as integers in the smallest unit, never as a floating-point decimal, and always with a currency code. Store the exchange rate and the price actually shown too — those are the data needed in any dispute and any report.