The payment provider processes money; your platform owns order meaning
Commerce systems need a clear separation between an order and a provider transaction. Provider identifiers, payment intents, captures and refunds are financial integration state. The order is your business record.
Model payment state explicitly
public enum PaymentStatus
{
Pending,
Authorized,
Captured,
PartiallyRefunded,
Refunded,
Failed,
Cancelled
}Do not infer financial state from whether the customer reached a thank-you page.
Webhooks are part of the transaction model
The browser can close after a successful payment. Provider callbacks therefore need independent verification and idempotent handling.
if (await webhookStore.ExistsAsync(eventId))
return Ok();
VerifySignature(request);
await paymentService.ApplyProviderEventAsync(
eventType,
payload,
cancellationToken);Refunds deserve their own audit trail
A refund should record who initiated it, why, the provider reference, amount, currency, order relationship and final result. This becomes essential for support, finance and dispute handling.
Deposits and partial payment
Booking and high-value products may require a deposit now and a remaining balance later. Model the financial schedule explicitly rather than overloading one 'paid amount' field.
Failure paths are normal paths
Timeout, decline, duplicate submission, webhook delay and partial refund are not rare exceptions. They are ordinary payment states and should be tested as deliberately as successful checkout.
Business value
Why it matters to the business
- Safer checkout and lower duplicate-payment risk.
- Clear reconciliation between orders and provider transactions.
- Easier support for cards, wallets and alternative payment methods.
- Auditable refund workflows.
- Better recovery when browsers or networks fail.
Practical considerations
- Payment integrations require strong testing.
- Provider-specific behaviour must remain isolated behind your platform contract.