Order Receipt Delivery: SMS Timeout Retry Idempotency and Status Polling
An order receipt is evidence of a settled payment, so the sending process cannot be allowed to reinterpret the order or quietly render a newer template after a timeout. The application should own an immutable receipt intent: order ID, payment-settlement event ID, recipient, template version, render data, and one idempotency key. A timeout then means unknown outcome , not failed delivery. Short…
An order receipt serves as proof that a payment has been successfully processed, and the system must ensure that it does not alter the order or generate a new template after a timeout. The application must maintain a unique receipt intent containing information such as the order ID, payment settlement event ID, recipient, template version, rendered data, and an idempotency key.
A timeout should be treated as an uncertain outcome rather than a failed delivery. To handle this, persist the intent before sending, reuse the idempotency key for each retry, and poll the attempt after an ambiguous response. Only after reaching a terminal retryable outcome should a new send be permitted. This approach prevents multiple receipts from being created for a single payment.
When handling SMS timeouts, retries, idempotency, and status polling, start by breaking down four distinct facts: payment settlement, notification intent recording, transport attempt acceptance, and recipient network delivery outcome. These events are not mutually exclusive and may not follow in sequence. A client-side timeout indicates that the caller stopped waiting but does not confirm if the transport accepted the message.
Implement a state machine with narrower transitions than the provider's vocabulary. For example, "pending" may dispatch, while "submitting" has an unresolved attempt and requires polling rather than resenting. "Accepted" may still need status polling, and "delivered" or "permanent_failure" are terminal states. If the upstream system has multiple delivery labels, map them at the adapter boundary to avoid leaking them into order processing.
Maintain that one payment settlement event creates one receipt intent, and every transport attempt points back to it. Ensure the database write acknowledging the payment event also creates an outbox record. This approach does not guarantee exactly-once delivery but provides a place to handle ambiguity. Template ownership for an order receipt should belong to the team responsible for order semantics, even if a communications service handles rendering or delivery.
The durable intent should use an immutable template version instead of a mutable alias and include settled amount, currency, order reference, and recipient values authorized for that receipt. A retry later should not use changed cart data, corrected product titles, or a newly deployed template, as this would produce a materially different document for the same payment event.
Operational ownership of version retention, schema validation, and privacy-aware storage should be handled by the application. This model is not suitable for promotional messages, as they require campaign-managed content and independent business user edits. In this case, keep campaign templates in a separate system. Rendering should be treated as a pure function of (template_version, render_data), with data validation performed before dispatching the intent.
Record a content fingerprint and keep transport metadata separate from the business payload to prevent the status webhook or poller from altering receipt content. Clearly express the ambiguous branch in code using a state transition function, as shown in the provided Python example. The language used is incidental, while the persisted compare-and-set boundary is crucial.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.