Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

A Stripe webhook handler grants credits twice when it treats each delivery as a new event and writes the credit every time it runs. Stripe can resend the same event, and it does not guarantee that events arrive in the order they were created. The fix is to make the credit write itself safe to repeat: verify the signature against the raw request body, record the Stripe event ID under a unique constraint, and enforce a second unique rule on the credit ledger for the underlying payment or order.

Stripe is used here as the worked example. The pattern applies to any payment provider that retries webhooks, but the code below is illustrative rather than drop-in, and it describes the failure mode rather than a specific production incident.

Why one payment can produce two credit grants

Stripe’s Webhooks documentation states that “webhook endpoints might occasionally receive the same event more than once.” A handler that runs something like addCredits(userId, 100) on every invocation will apply that grant again whenever the same event is delivered again.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

In practice, double grants tend to come from one of three patterns:

#1 Best Overall
Square Terminal - Credit Card Machine to Accept All Payments | Mobile POS
  • With Square Terminal, you can ring up sales, accept payments, and print receipts, all with one device. Use it at the counter or ring up customers anywhere in your store.
  • Accept all major credit and debit cards and pay one low rate with no hidden fees and no long-term contracts.
  • Process chip cards in just two seconds.
  • Get your money as soon as the next business day.
  • Use it cordlessly with the built-in battery, designed to last all day.
  • Retry after an unconfirmed response. The handler commits the credit, but the response to Stripe is slow or lost, so Stripe sends the event again. In live mode, Stripe’s current guide says automatic retries run for up to three days with exponential backoff (documentation accessed October 7, 2026).
  • Concurrent processing. Two workers pick up the same event, or a queue redelivers a job while the first worker is still running, and both check “has this been credited?” before either one writes.
  • Two event objects for one payment. Stripe can send separate Event objects that refer to the same underlying object and action, so an event-ID check alone does not catch them.

Each pattern needs a different guard, which is why a single check rarely fixes the problem.

Four safeguards, four different jobs

Signature verification, event deduplication, a business-level uniqueness rule, and Stripe’s API idempotency keys are often confused with one another. They protect against different failures, and none of them replaces the others.

Safeguard What it protects What it does not do alone
Raw-body signature verification with constructEvent Rejects requests that fail Stripe’s signature check Does not stop a legitimate repeat delivery of a valid event
Unique processed-event ID (inbox or processed-events table) Prevents the same Stripe event ID from being accepted twice May not catch a different Event object that describes the same payment
Unique credit-ledger key tied to the payment or order, inside a transaction Prevents a second credit from being committed for the same purchase, including under concurrent workers Does not authenticate incoming requests
Stripe API idempotency key Makes a retried Stripe API POST with the same key return its saved result Does not make a local database credit write atomic or durable

Handler design in five stages

The handler below is a sequence of responsibilities rather than a claim about any particular codebase. Each stage narrows the window in which a duplicate can slip through.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
Square Handheld - Portable POS - Credit Card Machine to Accept Payments for Restaurants, Retail, Beauty, and Professional Services
  • With Square Handheld, you can accept payments, take tableside orders, or scan barcodes anywhere. With a slim design and comfortable grip, the POS is easy to carry in your palm or pocket. Square Handheld is designed to withstand water splashes and dust. Add an optional protective case for accidental drops. A long-lasting battery and offline payments let you keep selling.
  • Slim, pocketable, and lightweight so you can accept payments wherever your customers are.
  • Take tableside orders, bust lines, or use the built-in barcode scanner, all with one sleek device.
  • A battery that can power through your shift and offline payments let you keep selling, even if your internet is down.
  • Accept all major credit and debit cards and pay one simple rate with no hidden fees and no long-term contracts required.

1. Verify the signature against the untouched raw body

Pass the raw request body, the Stripe-Signature header, and the endpoint’s signing secret to stripe.webhooks.constructEvent from the official Node.js SDK. If a framework parser has already converted the body to JSON or altered its whitespace, verification can fail even for genuine Stripe requests. Configure the route so the raw bytes reach the handler, and check the installed SDK version’s types for the exact signature.

2. Record the event ID durably before doing any work

Insert event.id into an inbox or processed-events table that has a unique constraint on that column. If the insert succeeds, the delivery is new and may proceed. If it fails on the constraint, the event has already been accepted, so the handler should return a success response without enqueuing a second grant. An in-memory set or process-local cache does not survive restarts or scale across instances, so it cannot serve as this record.

3. Enforce a unique credit on the business key

Apply the credit in a database transaction that writes a ledger row keyed to the payment or order (for example, a unique key of the form payment:<paymentId>). This second guard is what protects you from concurrent workers, queue redelivery, and separate Event objects for the same payment. The schema is an engineering recommendation inferred from Stripe’s duplicate-delivery guidance; Stripe does not prescribe a particular ledger design. The correct business key depends on your entitlement model. If one payment can legitimately grant several credit batches, the key must include whatever distinguishes them.

Rank #3
Verifone Vx520 EMV CLTS 32MB Credit Card Terminal
  • NO ENCRYPTION FOR DEBIT. NEED PIN PAD TO ATTACH WITH THE DEVICE TO WORK FOR DEBI
  • Verifone VX520 terminal with EMV reader, contactless reader, and dual com modem.
  • PCI COMPLIANT

4. Acknowledge promptly, then process asynchronously if needed

Stripe recommends that endpoints return a 2xx response quickly and handle heavier work asynchronously. Return success once the event ID has been durably accepted and the job has been queued. Do not hold the HTTP response open while the credit is calculated, sent to a downstream service, or retried.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

5. Make worker retries safe and keep an audit trail

Queue jobs can run more than once, so the worker must rely on the same unique ledger key as the synchronous path. Log the event ID and the business key on every attempt so you can trace a grant back to the delivery that caused it. Keep a reconciliation query that compares paid purchases with ledger rows, so discrepancies are found on a schedule rather than by a customer complaint.

Illustrative pseudocode

The sketch below is framework-neutral. The raw-body acquisition, the database client, and the transaction API are placeholders for whatever your stack uses.

Rank #4
Stripe Reader Holder & QR Payment Sign with Business Card Holder - Ultra
  • ALL-IN-ONE DESIGN: Combines a Stripe M2 card reader holder and a single QR code for Venmo, Cash App, PayPal, and Zelle in one professional point-of-sale display
  • PREMIUM CONSTRUCTION: Made from 3/8-inch thick high-impact plastic with precision-embossed text and logos, measuring 10" × 6" × 4"
  • SMART FEATURES: Built-in business card dispenser and USB cord pathway for reader power, plus secure dashboard for payment tracking
  • QUICK SETUP: One-minute activation process - simply scan QR code, add payment methods, business information, and customize settings
  • CUSTOMIZED & HANDCRAFTED: Personalize your sign with your business name on top and custom text on the bottom - each piece is handcrafted for a professional, branded look
const event = stripe.webhooks.constructEvent(rawBody, signature, endpointSecret);

// Stage 2: durable acceptance. A unique constraint on event_id decides.
await db.transaction(async (tx) => {
  const accepted = await tx.insertInboxRow({ eventId: event.id, type: event.type });
  if (!accepted) return; // already accepted: acknowledge, enqueue nothing
  await queue.enqueue({ eventId: event.id, objectId: event.data.object.id });
});

// Worker, stage 3: the business key makes the grant safe to repeat.
await db.transaction(async (tx) => {
  await tx.insertCreditLedgerRow({
    uniqueKey: `payment:${paymentId}`, // a unique constraint enforces one grant
    credits: creditsForPayment,
  });
});

Note that the insert that fails on the constraint is treated as a normal outcome, not an error to be retried.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Failure modes the design has to handle

Same event ID, delivered again

This is the common case. Deduplicating on event.id stops the second delivery from being processed. Stripe’s guidance is to track processed event IDs for this purpose.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Different event IDs, same underlying object

Stripe notes that separate Event objects can refer to duplicates. To recognize that case, compare the data.object ID together with the event type. The business-key ledger rule is the stronger protection here, because it works regardless of how many events mention the payment.

Best Value
Verifone Vx520 EMV/Contactless
  • Stylishly Compact
  • Easy to Use
  • Big Performance

Events arriving out of order

Stripe does not guarantee that events arrive in generation order, and the created timestamp is recorded to the second, so it is not a reliable tiebreaker for two events created close together. Do not reject an event because an event that you think should come first has not arrived yet. When state matters, retrieve the current resource from the Stripe API and act on its present status.

Stripe API idempotency keys and their limits

An idempotency key on an outbound Stripe API call protects that call when it is retried with the same key. Stripe may prune keys after at least 24 hours, and reusing a key after it has been pruned can create a new request. For that reason, the key is not a permanent record of what your application has granted, and it cannot replace the ledger.

Quick Recap

Bestseller No. 1
Square Terminal - Credit Card Machine to Accept All Payments | Mobile POS
Square Terminal - Credit Card Machine to Accept All Payments | Mobile POS
Process chip cards in just two seconds.; Get your money as soon as the next business day.; Use it cordlessly with the built-in battery, designed to last all day.
$298.99
Bestseller No. 2
Square Handheld - Portable POS - Credit Card Machine to Accept Payments for Restaurants, Retail, Beauty, and Professional Services
Square Handheld - Portable POS - Credit Card Machine to Accept Payments for Restaurants, Retail, Beauty, and Professional Services
Slim, pocketable, and lightweight so you can accept payments wherever your customers are.
$399.00
Bestseller No. 3
Verifone Vx520 EMV CLTS 32MB Credit Card Terminal
Verifone Vx520 EMV CLTS 32MB Credit Card Terminal
NO ENCRYPTION FOR DEBIT. NEED PIN PAD TO ATTACH WITH THE DEVICE TO WORK FOR DEBI; Verifone VX520 terminal with EMV reader, contactless reader, and dual com modem.
$119.00
Bestseller No. 5
Verifone Vx520 EMV/Contactless
Verifone Vx520 EMV/Contactless
Stylishly Compact; Easy to Use; Big Performance
$118.00

Diagnosing a double grant that has already happened

  • Query the credit ledger for any business key that appears more than once, and confirm whether the unique constraint exists in the deployed schema.
  • Compare those keys with the inbox table to see whether one event ID or two event IDs produced the duplicate rows.
  • Search worker and handler logs for the event IDs, which should show whether the duplicate came from a redelivery, a concurrent worker, or a second Event object.
  • Review the webhook delivery history for the endpoint in the Stripe Dashboard (Developers, then Webhooks) to confirm which deliveries were retried.
  • Correct affected balances with a compensating ledger entry rather than by editing or deleting the original grant, so the audit trail stays intact.

“

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.