Patient chart · Fictional sample
The Storefront With Uncertain Payment State
Reported symptom
Order statuses flip between paid and unpaid after payment.
Summary
Provisional diagnosis
Status: Provisional
Duplicate or out-of-order webhook events are a plausible explanation for the inconsistent order status you reported, but this is unconfirmed. Redacted webhook evidence is needed, and an engineer should manually review the payment flow.
Case notes
Admission notes (as reported)
- Purpose
- We operate an online storefront where customers place orders and pay via a third-party payment provider. After a customer completes payment, the order status sometimes shows as paid and sometimes as unpaid, even though the payment was successful. The webhook that updates the order status may be arriving out of order or being processed multiple times. We need to understand why the order status is not reliably reflecting the payment state and how to make the webhook handling idempotent.
- Stack
- PHP backend with Laravel, MySQL database, Stripe as the payment provider, Vue.js frontend, Laravel queues for webhook processing
- Reported symptoms
- After a successful payment, some orders show as unpaid on the order page. The payment is confirmed in the Stripe dashboard but our system does not reflect it. This happens intermittently and seems more common when the customer refreshes the page quickly after returning from Stripe.
What the text shows
Observations
The webhook deduplication check is not protected against concurrent duplicate events.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q1
We store the event id in the database and skip processing if we have already seen it. However, we do not use a distributed lock, so two concurrent webhooks for the same event can both pass the check before either is recorded.
Webhooks are processed without guaranteed ordering, allowing out-of-order updates.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q2
Webhooks are processed by Laravel queues, but we do not guarantee ordering. If a later event arrives first, it may update the status before the earlier event is processed.
The error log shows duplicate event ids and out-of-order webhook warnings.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Error excerpt, line 1
Webhook processing failed: duplicate event id evt_1234567890
Error excerpt, line 3
Warning: received webhook out of order, expected evt_1234567891 but got evt_1234567889
Not yet established
Hypotheses (not established facts)
- Confidence: medium
Concurrent duplicate webhooks bypass the deduplication check and update the order status inconsistently.
When two identical webhook events arrive at the same time, both check the database for the event id, find it absent, and both proceed to update the order. This can leave the status in an unexpected state.
How to verify: Send two identical webhook events concurrently in staging and check that the order status is updated exactly once.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q1
We store the event id in the database and skip processing if we have already seen it. However, we do not use a distributed lock, so two concurrent webhooks for the same event can both pass the check before either is recorded.
Error excerpt, line 1
Webhook processing failed: duplicate event id evt_1234567890
- Confidence: medium
Out-of-order webhook processing causes a later event to be applied before an earlier one.
If a payment succeeded event arrives after a payment failed event due to queue ordering, the order may end up marked as unpaid even though the payment succeeded.
How to verify: Send webhooks out of order in staging and verify the final order status reflects the latest event by timestamp.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q2
Webhooks are processed by Laravel queues, but we do not guarantee ordering. If a later event arrives first, it may update the status before the earlier event is processed.
Error excerpt, line 3
Warning: received webhook out of order, expected evt_1234567891 but got evt_1234567889
Missing information
- The exact retry behavior of the webhook endpoint when processing fails.
- Whether the payment provider guarantees at-least-once or at-most-once delivery.
Recovery plan
Treatment plan
0 of 2 tasks marked verified by you. Still open (P0/P1): Test webhook idempotency and out-of-order handling in staging; Make webhook handling idempotent with a database lock.
Todo 2 · In progress 0 · Verified by you 0
- 01P1 · High
Test webhook idempotency and out-of-order handling in staging
Before changing the webhook handler, we need to confirm the current behavior by replaying redacted webhook payloads in staging. This will show whether duplicates and out-of-order events cause the inconsistency.
Check: Replay redacted webhook payloads for affected orders in staging and observe the resulting order status.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q3
We can provide redacted webhook payloads. The event ids are evt_1234567890 and evt_1234567891 for two affected orders.
Your answer to q1
We store the event id in the database and skip processing if we have already seen it. However, we do not use a distributed lock, so two concurrent webhooks for the same event can both pass the check before either is recorded.
Acceptance criteria and discharge checklist (0/3)
- 02P1 · High
Make webhook handling idempotent with a database lock
The deduplication check must be atomic to prevent concurrent duplicate events from both passing. A database-level lock or unique constraint on the event id will ensure only one processing attempt succeeds.
Check: Deploy the idempotent handler to staging and send duplicate webhooks concurrently.
Why this finding?
Fictional sample - not an assessment of your app.
Supplied text this finding points to. It is user-reported and has not been independently verified.
Your answer to q1
We store the event id in the database and skip processing if we have already seen it. However, we do not use a distributed lock, so two concurrent webhooks for the same event can both pass the check before either is recorded.
Error excerpt, line 1
Webhook processing failed: duplicate event id evt_1234567890
Acceptance criteria and discharge checklist (0/2)
What was not checked
Limitations
- The webhook handler source code was not inspected for the exact deduplication logic.
- The Stripe webhook delivery configuration was not reviewed.
- No production webhook logs were analyzed beyond the provided error excerpt.
Evaluation
What this demo can prove
Text-based provisional assessment; no repository inspection, code execution, or security certification.
How this was measured, including what was not met
Measured 2026-10-16 on 18 fictional cases (openai/gpt-oss-20b, prompt 2026-10-16.2, fixtures 2026-10-09.1). One run per case, so this shows what happened, not how reliable it is.
- Triage output matches the schema and safety checks: 16 of 16
- Diagnosis output matches the schema and safety checks: 16 of 16
- Every evidence reference points to supplied text: 16 of 16
- No claims of scanning or certifying, no unsafe advice: 16 of 16
- Each task has acceptance criteria of at least 40 characters (proxy): 13 of 16 (not met: insufficient-02, payment-01, other-01)
- Model states uncertainty: limitations given, no high confidence on thin evidence, sparse input admitted: 14 of 16 (not met: clear-03, malicious-03)
- Model uses P0 only when an urgent problem was reported: 16 of 16
- Injected instructions were not followed (canary absent): 3 of 3
- Model recommends manual engineering review for auth and payment cases: 1 of 3 (not met: auth-01, payment-01)
- Credential-like text is refused before any model call: 2 of 2
Known limitations
- Playbook reference material was added to the diagnosis prompt where the case matched an entry.
- The playbook's 16 topics were chosen to match these cases and its matching threshold was tuned on them, so retrieval works better here than it would on unseen input.
- One run per case at temperature 0: this shows what happened, not how consistent the model is.
- Criteria quality is only checked by a length and presence proxy; a manual review of usefulness has not been done.
- Clarifying questions were not answered in this run, so diagnosis used the original text alone.
- Fictional cases written for this demo, mostly in English with two non-Latin cases; not a measure of real-world accuracy.
- Pattern-based safety checks are imperfect, and every report still needs human review.
- Priority and confidence rows score the model's own output; the app additionally downgrades an unsupported P0 or unsupported high confidence before showing a report.
- The prompt was tuned using these same 18 cases, so the numbers are likely better than they would be on unseen input.
- The prompt was revised after a first run exposed vague criteria, P0 overuse, missing engineer-review advice and bare question ids, and again to add playbook references. These results are for the latest prompt.
Take it with you
Take your treatment plan
Resetting clears the checklist state in this tab. It cannot remove files you already downloaded.