Error with HMAC Review

Hello,

I have been trying to submit my app for review but i have been trying for several days to get my hmac review accepted. I dont know what it is happening because i feel my toml and webhook are correct.

Can anyone help me?
This is my toml:
Configuración de la app Reelistic

client_id = “c56e555af76742c061d56”
name = “reelistic-prod”
application_url = “https://www.reelisticapp.com
embedded = true

[webhooks]
api_version = “2026-01”

[[webhooks.subscriptions]]
compliance_topics = [“customers/data_request”, “customers/redact”, “shop/redact”]
uri = “/api/webhooks/shopify”

[access_scopes]

scopes = “read_customers,read_orders,read_products”
optional_scopes =
use_legacy_install_flow = false

[auth]
redirect_urls = [
https://prod-api.reelisticapp.com/api/shopify/oauth2callback”
]

This is my webhook code:
from fastapi import APIRouter, Request, Response

import hmac

import hashlib

import base64

import logging

import json

from app.core.secrets_manager import secrets_manager

router = APIRouter()

logger = logging.getLogger(_name_)

logger.setLevel(logging.INFO)

async def get_raw_body(request: Request) → bytes:

"""Get raw request body for HMAC verification"""

try:

    return await request.body()

except Exception as e:

    logger.error(f"Failed to read request body: {e}")

    return b""

async def log_shopify_request(body_bytes: bytes, request: Request):

"""Logs the raw body and headers of Shopify requests"""

try:

    try:

        body_json = json.loads(body_bytes)

    except Exception:

        body_json = None



    logger.info("=== Shopify Webhook Received ===")

    logger.info(f"Path: {request.url.path}")

    logger.info(f"Headers: {dict(request.headers)}")

    logger.info(f"Raw Body: {body_bytes.decode('utf-8')}")

    logger.info(f"Parsed JSON: {body_json}")

    logger.info("===============================")

except Exception as e:

    logger.error(f"Failed to log Shopify request: {e}")

def verify_webhook(data: bytes, hmac_header: str) → bool:

"""Verify Shopify webhook HMAC signature"""

try:

    client_secret = secrets_manager.get("SHOPIFY_SECRET")

    if not client_secret:

        logger.error("SHOPIFY_SECRET not found in secrets manager")

        return False



    computed_hmac = hmac.new(

        client_secret.encode("utf-8"),

        data,

        hashlib.sha256

    ).digest()



    computed_hmac_b64 = base64.b64encode(computed_hmac).decode("utf-8")



    logger.info(f"Webhook verification - Received HMAC: {hmac_header}")

    logger.info(f"Webhook verification - Computed HMAC: {computed_hmac_b64}")

    logger.info(f"Webhook verification - Body length: {len(data)} bytes")

    logger.info(f"Webhook verification - Match: {hmac.compare_digest(computed_hmac_b64, hmac_header)}")



    \# DEBUG: Mostrar secreto del backend (solo pruebas)

    logger.info(f"\[DEBUG\] Backend SHOPIFY_SECRET = {client_secret}")



    return hmac.compare_digest(computed_hmac_b64, hmac_header)



except Exception as e:

    logger.error(f"HMAC verification failed with exception: {e}", exc_info=True)

    return False

@router.post(“/customers/data_request”)

async def customers_data_request(request: Request):

"""GDPR: Customer requests their data"""

body = await get_raw_body(request)

hmac_header = request.headers.get("X-Shopify-Hmac-SHA256", "")



\# DEBUG: mostrar HMAC recibido

logger.info(f"\[DEBUG\] Received HMAC header = {hmac_header}")



if not hmac_header:

    logger.error("Missing X-Shopify-Hmac-SHA256 header")

    return Response(status_code=401)



if not verify_webhook(body, hmac_header):

    logger.error("Webhook verification failed for customers/data_request")

    return Response(status_code=401)



await log_shopify_request(body, request)



try:

    payload = json.loads(body)

    logger.info(f"Processing data request for shop: {payload.get('shop_domain')}")

except Exception as e:

    logger.error(f"Error processing data request: {e}")



return Response(status_code=200)

@router.post(“/customers/redact”)

async def customers_redact(request: Request):

"""GDPR: Delete customer data"""

body = await get_raw_body(request)

hmac_header = request.headers.get("X-Shopify-Hmac-SHA256", "")



if not hmac_header:

    logger.error("Missing X-Shopify-Hmac-SHA256 header")

    return Response(status_code=401)



if not verify_webhook(body, hmac_header):

    logger.error("Webhook verification failed for customers/redact")

    return Response(status_code=401)



await log_shopify_request(body, request)



try:

    payload = json.loads(body)

    logger.info(f"Processing customer redaction for shop: {payload.get('shop_domain')}")

except Exception as e:

    logger.error(f"Error processing customer redaction: {e}")



return Response(status_code=200)

@router.post(“/shop/redact”)

async def shop_redact(request: Request):

"""GDPR: Delete shop data after uninstall"""

body = await get_raw_body(request)

hmac_header = request.headers.get("X-Shopify-Hmac-SHA256", "")



if not hmac_header:

    logger.error("Missing X-Shopify-Hmac-SHA256 header")

    return Response(status_code=401)



if not verify_webhook(body, hmac_header):

    logger.error("Webhook verification failed for shop/redact")

    return Response(status_code=401)



await log_shopify_request(body, request)



try:

    payload = json.loads(body)

    logger.info(f"Processing shop redaction for shop: {payload.get('shop_domain')}")

except Exception as e:

    logger.error(f"Error processing shop redaction: {e}")



return Response(status_code=200)

Hi @Reelistic_Company

I’m not quite clear on your testing process. You can simulate a Shopify webhook to verify your solution with this command:shopify app webhook trigger --topic shop/redact --address https://your-app-url/webhooks/shop/redact --delivery-method http --api-version 2025-07

1 Like