On this page

A webhook endpoint sits at a consequential boundary: an outside request can eventually lead to a reward evaluation or another authorized operation. The service must establish the request’s origin before trusting its business fields. A payload that looks like a Shopify order is not enough.

Consider a hypothetical integration receiving an order update for ORDER-TEST-42. An attacker can copy the shape of a JSON order, while an ordinary middleware change can accidentally alter legitimate bytes before verification. The intake design needs to reject the first case and preserve the second case’s original signed body so valid deliveries continue to work.

Preserve the signed request body at intake

Shopify’s HTTPS webhook verification uses an HMAC signature derived from the raw request body and the app’s client secret. The signature is supplied in the X-Shopify-Hmac-SHA256 header. Shopify webhook verification

The raw body matters because a parsed and reserialized JSON object may have different bytes even when it represents the same values. Whitespace, property order and encoding can change. Verification should use the bytes received at the defined intake boundary, before a general JSON parser or transformation replaces them.

Map the middleware order explicitly. The route first captures a bounded raw body, then performs the supported verification, then parses the payload for the authorized processing path. Logging, tracing and error middleware should not copy the unverified body into unrestricted storage merely because it arrived before authentication.

For the fictional integration, the intake specification includes a body-size policy appropriate to the expected contract, a raw-byte capture method supported by the framework, a header validation step and a verified-event queue. The exact framework implementation should follow current supported Shopify libraries or a carefully reviewed manual verifier.

Separate request transport from business meaning. HMAC verification establishes the signed delivery boundary for HTTPS. It does not prove that the order qualifies for a reward, that the event is new or that the shop is currently connected under the program. Those checks occur after origin verification in their own stages.

A useful intake record contains a generated request reference, receipt time, route category, verification outcome and safe delivery metadata. It does not require logging the complete body, secret or signature. Keep enough information to diagnose a rejection without turning the intake log into a copy of customer data.

Verify origin before parsing business fields

Use the current supported verification library where it fits the application’s framework. If implementing the check manually, calculate HMAC-SHA256 over the original body with the correct app secret and compare the decoded digest safely. Reject missing, malformed or mismatched signatures before the business-processing queue.

The original pseudocode below shows the boundary without copying a vendor sample:

raw_bytes = capture_bounded_original_body(request)
provided_digest = decode_expected_signature_header(request)

if provided_digest is malformed:
    reject_without_business_enqueue()
else:
    expected_digest = hmac_sha256(active_app_secret, raw_bytes)
    if digest_lengths_differ or not constant_time_equal(expected_digest, provided_digest):
        reject_without_business_enqueue()
    else:
        payload = parse_validated_body(raw_bytes)
        validate_supported_topic_and_connected_shop_context()
        durably_accept_verified_event_for_later_processing()

This is original pseudocode. The actual implementation must use the supported library and error behavior for the selected environment. It is not a RebateCardX endpoint or SDK example.

Validate digest length before calling comparison functions that require equal-length buffers. A malformed header should produce a controlled rejection, not an uncaught exception that turns a simple invalid request into a service failure.

Keep the secret in an approved secret-management system and restrict which execution context can read it. Do not paste it into test fixtures, source code or logs. Secret rotation should follow the current Shopify contract and a reviewed transition procedure, with evidence that legitimate deliveries remain verifiable throughout the supported change.

Do not choose a secret based solely on untrusted payload fields. The verifier should use the configured application identity and the supported authentication architecture. A request must not be able to select a weaker or unrelated secret by naming another shop or environment.

A difficult case is a reverse proxy or framework that decompresses or transforms the request before the application captures it. Identify the actual byte boundary expected by the supported verification method. Test through the same proxy path used in production rather than only calling the route handler with an already prepared string in a unit test.

The response timing and durable acceptance design should follow Shopify’s current delivery requirements. Avoid doing the entire reward workflow synchronously inside the intake request. Once the request is verified and safely accepted under the designed queue contract, later business processing can occur independently of the HTTP handler.

Test valid, altered and missing-signature deliveries

The verification test suite should prove that the queue receives only accepted deliveries. Checking the HTTP status alone is not enough if an error path still enqueues the payload before returning a rejection.

Use synthetic payloads and locally generated signatures under a test secret. A useful matrix is:

Test input Required result
Original bytes with matching signature Accepted into the verified intake path
One changed byte with old signature Rejected before business enqueue
Parsed and reserialized body with original signature Verification uses original bytes or fails safely
Missing signature header Controlled rejection
Malformed base64 or wrong digest length Controlled rejection without crash
Correct signature but unsupported topic No unauthorized business processing
Correct signature for disconnected context Reviewed connection-state handling
Oversized request Bounded intake rejection under the configured policy

For the hypothetical order update, retain two filled test records: one accepted original request and one request with its quantity byte changed after signing. The first should create a verified event record. The second should create only a safe rejection diagnostic, with no reward job or purchase mutation downstream.

Test middleware ordering by inserting the actual production parser configuration into the test environment. This catches a regression where a general JSON parser is moved before the verifier during an unrelated application refactor. The same payload should either be verified from its preserved original bytes or rejected in a controlled way; it should never bypass verification to make the test pass.

Also inspect error reports and traces. An invalid request should not cause the framework to dump the entire body or secret-bearing headers into an external error service. The security boundary includes the diagnostic path, not just the happy-path handler.

The acceptance artifact should show the route configuration, verifier version, synthetic fixture references and downstream queue counts. For each rejected fixture, the expected count of business events is zero. That concrete observation demonstrates that origin verification happens before consequential processing.

Once this boundary is established, duplicate detection and reward-operation idempotency can handle repeated valid events. Signature verification remains focused on its own job: proving that an HTTPS delivery meets the supported origin check before the integration begins trusting what it says about a purchase.

Include a test where the signature is valid but the JSON is malformed. The intake should distinguish verified origin from a usable business payload and stop before reward processing when parsing fails. This avoids an unsafe shortcut in which a valid signature is treated as permission to skip structural validation. Retain a safe parse-error category and the request reference, then verify that no downstream order record was created. The fixture demonstrates that each stage grants only the authority required for the next stage, rather than turning one successful cryptographic check into unconditional acceptance of every business field.

Request technical access to confirm the authenticated event boundary for your integration.

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.