A customer pays, closes the checkout tab, and waits for confirmation. Your payment dashboard shows a successful transaction, but the website still shows the order as pending. Someone on the team now has to work out what happened.
This is the kind of problem worth planning for before an integration goes live. The payment, the order record, the confirmation email, and the shipping request are separate pieces of work. A webhook helps connect them, but the receiving application still has to handle the message properly.
Here is how I would approach webhook reliability for an online store or a Laravel business application, including the awkward cases that a successful test checkout can miss.
What is a payment webhook?
A webhook is a notification sent from one system to another when an event happens. Stripe's webhook documentation describes notifications for events such as payment confirmation and subscription activity.
For a simple store, the receiving endpoint might use a verified notification to update an existing order. A background job could then prepare the receipt or notify another service.
Keep payment confirmation separate from the customer's visit to the success page. The browser can close or lose its connection; your server still needs a reliable way to establish what happened to the transaction.
Why the same notification can arrive twice
Shopify explains in its delivery verification guide that duplicate deliveries can occur after a timeout or retry. It recommends operations that remain safe when repeated.
Imagine your application saves an order update, but the response never reaches the sender. The sender cannot tell that your work succeeded and tries again. If the receiving code starts the whole workflow from scratch, the customer could receive another confirmation or the warehouse could receive another dispatch request.
The useful question is: what happens if this exact message arrives again?
1. Verify the request before using its contents
A public webhook URL can receive requests from sources other than your provider. Before updating an order, validate the request using the provider's supported verification method.
Shopify's HTTPS deliveries include an HMAC signature calculated from the raw request body. Its documentation warns that parsing or changing that body before verification can cause problems. Use the official library for your stack where one is available.
For a proposed implementation, I would also check the expected account or store, accepted event types, and required identifiers. A valid signature establishes the message's source; the application still decides whether the requested update makes sense for its own records.
2. Separate receiving a message from completing the work
I would give incoming notifications a small persistent inbox. Each record would track the provider, account, delivery or event identifier, receipt time, processing state, and a reference to the related order.
The endpoint can validate a request and save the work durably before acknowledging it. A worker then handles the longer operation. If the application cannot save the notification, it should not claim that it accepted the work.
For this design, I would use separate states such as received, processing, completed, and failed. Otherwise, a record saying only 'seen' can conceal work that stopped halfway through.
A recovery job should be able to find accepted messages that never reached a worker. That matters when the database save succeeds but the following queue dispatch fails.
3. Make repeated processing safe
For Shopify HTTPS deliveries, the documented delivery identifier is X-Shopify-Webhook-Id. Shopify distinguishes that identifier from X-Shopify-Event-Id, which can connect deliveries caused by the same merchant action.
I would enforce uniqueness in the database for the appropriate provider, account, and message identifier. A preliminary lookup alone can miss two requests arriving together.
I would also protect the business operation itself. A new notification should not create a second local order for a transaction already linked to one.
External side effects need their own handling. A database rollback cannot unsend an email or reverse a shipping request. For a shipping API, I would check whether it supports an idempotency key or a stable request reference, and record the result so a retry can resolve an uncertain outcome.
4. Expect updates to arrive out of order
Stripe states that event delivery order is not guaranteed. Its guidance also notes that event timestamps are not a dependable way to establish ordering; missing objects can be retrieved through the API.
For example, a subscription application should not assume that its local customer record will always be ready before a billing notification arrives.
My preferred design would define the allowed changes for each business record. Where a notification conflicts with local state, the worker can retrieve the relevant provider record and apply the business rules to that information. An older message should not casually undo a more recent confirmed change.

A laptop, monitor, and notebook in a naturally lit workspace, illustrating the review of software logs and order records. AI-generated image; screens are illustrative.
Illustrative workspace for reviewing delivery logs, background jobs, and order records.
5. Let Laravel jobs wait for committed data
Laravel's queue documentation on database transactions explains a timing issue: a worker can start a job before the transaction creating its records has committed.
Laravel 12 provides an after_commit queue setting and an afterCommit() dispatch method to delay dispatch until the transaction commits. Use the documentation matching your application's version when implementing this.
For a webhook inbox, this can stop a worker from trying to load a record that is not yet visible. It does not replace the need to recover pending work if dispatch fails after the commit.
I would include the queue worker in deployment checks too. An endpoint can keep accepting notifications while a stopped worker leaves every order update waiting.
6. Give the team a way to investigate and recover
A useful support screen should answer three questions: did the message arrive, did processing finish, and what can we do next?
- Show the provider reference, related local order, receipt time, and current processing state.
- Keep a short error summary and attempt history for failed work.
- Allow an authorized staff member to retry a specific item after the cause is fixed.
- Alert the responsible person when accepted work stays pending beyond an agreed threshold.
- Keep secrets and unnecessary customer details out of logs.
I would also plan a reconciliation check: compare a defined set of recent provider records with local records and flag mismatches. Start with a manageable time window and clear rules for which differences can be repaired automatically.
For a store owner, the result should be understandable. 'Order update failed because the shipping service was unavailable' is more useful than a page of raw exception output.
What I would test before launch
A successful payment is only one test. For the workflow described here, I would also check:
- The same verified message arrives twice, including two deliveries at the same time.
- The database saves the notification, but queue dispatch fails.
- A worker crashes after updating the order and before marking the inbox item complete.
- A shipping request times out after the remote service may have accepted it.
- A notification arrives before a related local record exists.
- The signature is invalid or the account does not match the expected store.
- The queue worker stops, restarts, and resumes the pending work.
For each case, agree on the expected order state and any permitted side effects. A test should check whether another parcel request was created, rather than merely whether the endpoint returned success.
Questions store owners often ask
Does a duplicate webhook mean the customer was charged twice?
A repeated notification alone does not establish that. Check the provider's transaction records and references before drawing a conclusion or starting a refund.
Can a plugin handle this for me?
It may already handle the standard payment flow. For custom shipping, CRM, or membership behavior, ask which component owns retries and duplicate protection. Test that complete workflow rather than adding another handler blindly.
Should every webhook trigger an email?
I would tie the email to a defined business change, such as the first confirmed transition to paid. The arrival of another notification is not, by itself, a reason to send another message.
Need help with a store or billing integration?
I build Shopify integrations, WordPress functionality, and Laravel applications. For a webhook review, useful starting information includes the affected order reference, the expected behavior, and the provider's delivery record. Share those through an appropriate private channel rather than a public comment.
For a related application example, see Maven Pro, my Laravel company intranet project, which includes subscription billing and access controls. You can get in touch through my portfolio to discuss your own integration.
Sources
Stripe: Receive events in your webhook endpoint
Shopify: Verify webhook deliveries
Laravel 12: Jobs and database transactions
Documentation checked on 22 October 2026. The implementation design and examples above are proposed approaches, not a description of a specific client's production system.



