What you’ll be able to explain
- Design independent payment and fulfillment states that remain understandable after delayed events and failures.
01 / Understand
“Paid” is doing too many jobs
A shop dashboard displays one green badge: Paid. Does it mean a transaction was detected, the provider considers the invoice settled, the business accepted the amount, or the customer received the product?
Those events often happen near each other on a good day. They separate when something goes wrong. A settled payment can coexist with a failed download entitlement. A shipment can already be on its way when later payment evidence needs review. One overloaded label hides the work required next.
Use two dimensions. The payment observation describes evidence from the payment system. The fulfillment state describes the business action. Keep the provider’s exact raw state as data, then map it deliberately into your application’s vocabulary.
For a fictional digital shop, the display might say: “Payment accepted · Access pending.” That is more informative than either “Paid” or “Failed” alone. It names the successful part and the unfinished part without asking the customer to guess.
The idea, at a glance
Observe payment
Keep provider evidence and exception details.
Decide eligibility
Apply the shop’s explicit policy.
Record fulfillment
Track the actual product delivery separately.
02 / Explore
A small state table with explicit responsibility
| Payment observation | Fulfillment state | Meaning and next step |
|---|---|---|
| Awaiting payment | Not started | Keep the order open under its terms |
| Detected, awaiting required evidence | Not started | Reconcile provider state; do not claim delivery |
| Accepted under business policy | Queued | A durable fulfillment intent exists |
| Accepted under business policy | Completed | Record the product-specific completion evidence |
| Accepted under business policy | Failed, retryable | Retry or reconcile delivery without charging again |
| Late or mismatched payment | Needs review | Preserve evidence and apply exception policy |
These names are our example vocabulary. They are not a claim that every provider exposes this exact enum. BTCPay, for example, distinguishes invoice states and exception conditions, while a merchant remains responsible for the business response.
A refund adds its own workflow. Issuing a refund request, observing its completion, and revoking access where appropriate are separate events. Avoid erasing the original payment and fulfillment history by setting everything back to “unpaid.”
Design transitions that explain themselves
A late old notification should not simply replace a newer decision. Record the observation, reconcile the provider’s current state, and evaluate allowed transitions. If an accepted payment later requires investigation, preserve the already-completed fulfillment record and open a review state. History is useful evidence, not clutter to be overwritten.
Likewise, a fulfillment retry should use the same logical operation identity. A worker crash does not create permission to issue a second license, and a webhook retry does not create a new order.
03 / Build
Replay a failure without charging twice
Walk through this fictional order:
O42: payment accepted / fulfillment queued
worker calls entitlement service
service grants access, response is lost
worker times out
What should the dashboard say? A truthful answer is that delivery is unresolved or retryable while payment remains accepted. Before issuing a second entitlement, the worker should reuse a stable idempotency key or query whether the original operation completed.
Expected end state: one recorded payment, one intended entitlement, and a completion record that corresponds to the actual effect. A customer support view can explain what happened without asking the customer to pay again to solve a fulfillment failure.
For a developer exercise, write a transition table for these fields and add an explicit “needs review” path. Test an old processing event after settlement and a retry after delivery completion. Neither should silently undo the fulfilled order or duplicate its effect.
Pause & explain
An entitlement call times out after a settled payment. Should the application mark the customer unpaid?
Try explaining it in your own words before opening the answer.
Compare your explanation
No. The payment evidence has not changed. Fulfillment needs retry or reconciliation using the original operation identity, while the payment remains recorded as accepted under the applicable policy.
Sources & scope
Primary references behind this explanation. Worked examples and diagrams are original teaching material.
- 01BTCPay Server — Invoices ↗
Provider-specific invoice lifecycle and exception statuses. Business fulfillment is a separate application decision.
- 02Stripe — Webhook endpoints ↗
Primary provider documentation on duplicate events, ordering, and signature checks. Used for delivery semantics, not as a Bitcoin protocol source.
- 03BTCPay Server — Greenfield PHP example ↗
Provider integration example, including webhook validation. The lesson’s pseudocode is not a drop-in handler.
Where this explanation stops
- The example state names are application design choices, not protocol or provider enums.
Keep unfolding
Why a webhook can arrive more than once Pending, confirmed, and reorganized: a payment’s changing status