ChainUnfold
A VISUAL GUIDE4 min read

Payment received versus order fulfilled: designing clear statuses

Keep financial observations separate from the decision to deliver a product.

By chainunfold Reviewed 23 September 2026Revision 1

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.

An observation about money does not certify that a product has been delivered.

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.

  1. 01
    BTCPay Server — Invoices ↗

    Provider-specific invoice lifecycle and exception statuses. Business fulfillment is a separate application decision.

  2. 02
    Stripe — Webhook endpoints ↗

    Primary provider documentation on duplicate events, ordering, and signature checks. Used for delivery semantics, not as a Bitcoin protocol source.

  3. 03
    BTCPay 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