CommunityCoding & Developmentgithub.com

Blessed-Shammah/payment-webhook-doctor

Diagnose Stripe webhook signature failures, duplicate fulfilment, missing payment credits, and out-of-order payment or refund events. Use when a user says a Stripe payment succeeded but their order is unpaid, a webhook credits twice, signature verification fails, or a refund ledger drifts. Produces evidence-based diagnosis, regression tests, and a repair plan; includes an offline verifier and durable test-mode reference ledger.

What is payment-webhook-doctor?

payment-webhook-doctor is a Claude Code agent skill that diagnose Stripe webhook signature failures, duplicate fulfilment, missing payment credits, and out-of-order payment or refund events. Use when a user says a Stripe payment succeeded but their order is unpaid, a webhook credits twice, signature verification fails, or a refund ledger drifts. Produces evidence-based diagnosis, regression tests, and a repair plan; includes an offline verifier and durable test-mode reference ledger.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Blessed-Shammah/payment-webhook-doctor/tree/HEAD

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Payment Webhook Doctor

Start with the evidence, not a resend. Replaying an event into a broken handler can repeat its side effects.

Route one task at a time

ProblemRead
Signature fails, wrong secret, parsed body, old deliveryreferences/signatures.md
Duplicate credit, delayed fulfilment, failure after success, refunds out of orderreferences/reconciliation.md
4xx/5xx/timeout, recovery, missing event, production rolloutreferences/incident-playbook.md
Need verified protocol facts and version boundariesreferences/sources.md
Need a non-Stripe / M-Pesa adapterreferences/provider-boundary.md

Workflow

  1. Establish scope: provider, snapshot versus thin event, test/live mode, connected account, endpoint framework and API version. Ask only for missing facts that change the diagnosis.
  2. Inspect handler code, migrations, expected order amount/currency, server-created PaymentIntent mapping, and redacted delivery metadata. Keep signing secrets in local environment variables. Don't ask users to paste secrets or customer bodies into chat.
  3. Verify the exact raw request bytes before parsing JSON. Prefer the official Stripe SDK for a deployed handler. The bundled dependency-free verifier is a small, inspectable diagnostic reference, not a replacement SDK.
  4. Use the local tools below to reproduce the fault with synthetic fixtures first. Classify evidence as observed, inferred, or unverified. A valid signature authenticates a delivery; it does not prove the order should be fulfilled.
  5. Repair the smallest cause and add a regression test. Bind payments to a trusted server-side order; compare amount, currency, account scope and mode. Keep inbox, ledger update, and side-effect intent in one database transaction.
  6. Verify duplicates, distinct events for the same object, restart, out-of-order delivery, failed events, and partial/full refunds. Do not use event timestamps as a total ordering mechanism.
  7. Deliver findings, patch, test evidence, unresolved integration requirements, and an explicit rollback/recovery plan. Don't deploy, resend live events, issue refunds, or transfer money without the user's authorized workflow.

Local tools

Run from this skill's root:

npm test
npm run demo

Inspect a saved test-mode fixture using its documented fixture clock:

export DOCTOR_FIXTURE_SECRET='fixture_only_not_a_live_secret'
node scripts/doctor.mjs --body fixtures/payment.json --header fixtures/signature.txt \
  --secret-env DOCTOR_FIXTURE_SECRET --fixture-now 1794130000 \
  --normalized-out /tmp/payment-doctor-event.json
python3 scripts/ledger.py --db /tmp/payment-doctor-reference.sqlite \
  --orders fixtures/orders.json < /tmp/payment-doctor-event.json

The export refuses to overwrite an existing file. Use a fresh output path or remove your own synthetic output deliberately. The Python CLI writes only the explicitly named local SQLite database. It expects the verifier's trusted normalized output; it does not verify signatures itself. Never expose it as a public webhook endpoint.

The normal diagnostic report excludes customer data, raw payloads and signatures. An explicit normalized export retains identifiers and integer amounts needed for local reconciliation. Keep it private and out of version control.

--fixture-now is only for old synthetic test events; it refuses live-mode input. Omit it for an ordinary current delivery. The reference ledger rejects live-mode events, never calls Stripe, and never dispatches outbox rows.

Required report

  • Outcome: what happened and whether it is reproduced.
  • Evidence: files/lines, sanitized delivery metadata, exact commands, observed results.
  • Root cause: confirmed cause versus remaining hypotheses.
  • Repair: minimal patch plus migration/backfill implications.
  • Regression matrix: duplicate, restart, scope mismatch, wrong amount/currency, ordering, refund cases.
  • Operational handoff: test-mode integration evidence still needed; safe replay and rollback criteria.

Boundaries

Support is intentionally narrow: Stripe snapshot payment_intent.succeeded, payment_intent.payment_failed, and refund object events. Thin events, subscription entitlements, Checkout sessions, disputes, multicapture payment semantics, application fees, taxes and Connect transfers need dedicated adapters. A zero-finding result is not a security certification or proof that funds arrived. See the references before extending.

Related Skills