On this page

Ignoring a repeated webhook and preventing a repeated reward are related but different jobs. One protects the intake stream from receiving the same delivery twice. The other protects the business operation when several valid events or workers attempt the same intended action. A reliable integration needs both boundaries.

Imagine a hypothetical order that qualifies for one approved $12 reward. The service receives a repeated delivery, then later receives another legitimate order update that leads a second worker to evaluate the same award. A delivery-only duplicate check can catch the first repetition while still allowing the second worker to start a duplicate business operation.

Separate delivery identity from reward-operation identity

Shopify’s webhook verification guidance identifies delivery metadata that can be used to detect duplicates. Use the current documented delivery identifier for the selected mechanism and retain it within the trusted shop and application context. Shopify webhook delivery verification

The delivery key answers, “Have we already accepted this delivery?” The reward-operation key answers, “Have we already attempted or completed this particular authorized business action?” Those keys should not be the same merely because the webhook started the work.

For the fictional $12 reward, the proposed operation identity could combine trusted shop, approved campaign, purchase reference and the defined award unit. If the program permits several awards per order, that unit must distinguish them. If it permits only one, adding a random job identifier would defeat idempotency by making every retry look new.

A key register makes the distinction concrete:

Key Represents Example behavior
Delivery key One signed event delivery identity Repeated intake is recognized
Purchase key One order in one trusted shop Updates refer to the same purchase
Award-operation key One intended authorized action Competing workers converge on one record
Provider correlation reference Supported external operation relationship Existing outcome can be investigated

These are original design categories. The actual provider’s idempotency and correlation fields must be confirmed rather than assumed.

Do not use an in-memory set as the only durable record. A process restart, another worker or a new deployment can lose that memory. The store must enforce the relevant uniqueness and state transition atomically within the actual database or equivalent durable system.

Choose the key’s lifetime according to the operation contract. Deleting an operation record too early can make an old retry appear new. Keeping it forever without a reviewed data policy is not automatically correct either. The retention decision should preserve the ability to prevent repeated consequential actions for the period the program requires.

Store a durable processing result before repeating side effects

The operation record should represent more than a Boolean called processed. At minimum, the design needs to distinguish reserved work, an attempt whose result is not yet known, a confirmed result and a state that requires review. A crash between the external action and the local update can otherwise leave the system unable to tell whether repeating the action is safe.

A proposed state record might contain the operation key, request fingerprint, current stage, attempt reference, provider correlation reference and last-established outcome. It should not contain card credentials or an entire recipient payload merely to support duplicate prevention.

An original concurrency sketch is:

operation = atomically_get_or_create(unique_business_key)

if operation has_confirmed_result:
    return_existing_result()
else if operation has_unresolved_external_attempt:
    route_to_supported_outcome_resolution()
else if worker_can_atomically_claim_operation:
    persist_intended_request_and_attempt_reference()
    perform_only_the_documented_authorized_operation()
    persist_supported_result_or_unknown_state()
else:
    observe_existing_operation_without_repeating_side_effect()

This is pseudocode for an application design, not a RebateCardX API. Its important feature is the explicit unknown state between an attempted external action and a confirmed outcome.

Use a request fingerprint to detect accidental key reuse with different inputs. If two workers present the same operation key but different reward amounts, the system should not silently accept the later amount or start a second operation. That mismatch indicates a rule-version or normalization problem that needs review.

Avoid holding a long database transaction open while waiting on a provider call unless the actual design specifically supports that tradeoff. A durable operation state and controlled claim can separate local concurrency control from external network timing. The chosen mechanism must still handle worker failure and lease expiry without treating an uncertain external result as a clean failure.

A difficult case is an order update that legitimately changes the approved award. Do not solve that by creating a fresh issuance key automatically. The existing award relationship remains relevant, and the approved correction process determines what operation is permitted next. Idempotency protects the intended action; it does not decide the customer’s entitlement after a change.

Test duplicate and concurrent attempts

The test should measure business outcomes, not just how many handler functions ran. Several workers may safely inspect the same event while only one authorized operation occurs. Conversely, one handler invocation can still cause a duplicate if its internal retry path creates a new operation identity.

Use synthetic fixtures for these cases:

Fixture Required invariant
Same delivery accepted twice One durable delivery identity
Two different events evaluate the same award One intended business-operation record
Two workers start simultaneously Only the permitted claimant performs the action
Worker restarts before external attempt Existing reserved work is recoverable
Worker stops after sending the request Outcome becomes unresolved, not automatically retryable
Same key arrives with different amount Conflict is visible and blocks silent reuse
Completed operation is replayed Existing confirmed result is returned

For the hypothetical $12 reward, run two workers against the same synthetic purchase at the same time. The expected record is one operation with one established outcome, not two awards totaling $24. Retain the worker attempts and the durable operation history so the test demonstrates how the system resolved the race.

Then force a failure after the provider adapter reports acceptance but before the local success update. On restart, the integration should use the supported outcome-resolution path for the existing attempt. It must not generate a new random key and assume that doing so makes the retry safe.

Test the storage constraint directly. If the database’s unique index or equivalent atomic mechanism is removed, the test should expose the race. This makes the verification meaningful: it checks the boundary that prevents duplicate business records rather than merely repeating an implementation’s happy path.

Keep operational diagnostics understandable. A message such as “duplicate delivery ignored” is different from “existing reward operation returned” and from “external outcome unresolved.” Those distinctions help staff and engineers avoid manually replaying a case that is already complete or still uncertain.

The completed design should state its exact guarantee and limit. It can prevent local workers from intentionally starting the same defined operation twice under the enforced contract. It cannot invent provider idempotency semantics or resolve an unknown external outcome without the provider’s supported evidence. Documenting both makes retries controlled instead of hopeful.

The practical result is a durable key and state model with passing concurrency tests. It allows the integration to accept repeated technical signals while preserving one coherent business history for the customer’s purchase-linked reward.

Keep one explicit conflict fixture in the acceptance record: the same intended operation key first carries a $12 request and later carries a $15 request. The second attempt must not quietly reuse the first result as though the inputs matched, and it must not create a second operation under a fresh key. The expected outcome is a visible input conflict tied to the existing purchase and campaign version. This proves that the key protects the meaning of the operation, not merely the number of times a database row is inserted.

Request technical access to review the idempotency contract for your approved reward workflow.

Request technical access

Source references

Back to contents

General information only

This guide is general information, not financial, legal, tax or regulatory advice. Eligibility, card availability, permitted use and responsibilities depend on the applicable offer and card terms.