iachat/config
Muhsin Keloth e75e8a77f6
feat(shopify): Add mandatory compliance webhooks with HMAC verification (#13549)
Fixes
https://linear.app/chatwoot/issue/CW-6494/add-shopify-mandatory-compliance-webhooks-for-app-store-listing

Shopify requires all public apps to handle three GDPR compliance
webhooks before they can be listed on the App Store. Their automated
review checks for these endpoints and verifies that apps validate HMAC
signatures on incoming requests. We were failing both checks.

This PR adds a single webhook endpoint at `POST /webhooks/shopify` that
receives all three compliance events. When Shopify sends a webhook, it
signs the payload with our app's client secret and includes the
signature in the `X-Shopify-Hmac-SHA256` header. Our controller reads
the raw body, computes the expected HMAC-SHA256 digest, and rejects
mismatched requests with a 401.

Shopify identifies the event type through the `X-Shopify-Topic` header.
For `customers/data_request` and `customers/redact`, we simply
acknowledge with a 200—Chatwoot doesn't persist any Shopify customer
data. All order lookups happen as live API calls at query time. For
`shop/redact`, which Shopify sends after a merchant uninstalls the app,
we delete the integration hook for that shop domain and remove the
stored access token and configuration.


### How to test via Rails console
```
secret = GlobalConfigService.load('SHOPIFY_CLIENT_SECRET', nil)
body = '{"shop_domain":"test.myshopify.com"}'
valid_hmac = Base64.strict_encode64(OpenSSL::HMAC.digest('SHA256', secret, body))
```

  #### Test 1: No HMAC → 401
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code  # => "401"
```
  ####  Test 2: Invalid HMAC → 401
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => 'invalid', 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code  # => "401"
```
  ####  Test 3: Valid HMAC, customers/data_request → 200
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'customers/data_request' }
app.response.code  # => "200"
```

####  Test 4: Valid HMAC, customers/redact → 200
```
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'customers/redact' }
app.response.code  # => "200"
```

#### Test 5: Valid HMAC, shop/redact → 200 (deletes hook)
```  
# First check if a hook exists for this domain:
Integrations::Hook.where(app_id: 'shopify', reference_id: 'test.myshopify.com').count
app.post '/webhooks/shopify', params: body, headers: { 'Content-Type' => 'application/json', 'X-Shopify-Hmac-SHA256' => valid_hmac, 'X-Shopify-Topic' => 'shop/redact' }
app.response.code  # => "200"
```

---------

Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2026-02-17 16:52:13 +05:30
..
agents feat: add more tools (#12116) 2025-08-08 17:57:30 +05:30
environments chore: Improvements to codespaces (#11635) 2025-05-29 21:18:02 -06:00
initializers fix: velma connection limit (#13395) 2026-01-29 20:53:41 +05:30
integration feat: notion OAuth setup (#11765) 2025-06-26 19:16:06 +05:30
languages feat: Updated public portal header design (#8089) 2023-11-08 17:56:59 -08:00
locales fix: Prevent user enumeration on password reset endpoint (#13528) 2026-02-13 13:45:40 +05:30
app.yml Bump version to 4.10.1 2026-01-20 08:43:11 -08:00
application.rb Revert "chore: Upgrade Rails to 7.2.2 and update Gemfile dependencies (#11037)" 2026-02-03 21:09:42 -08:00
boot.rb Upgrade to rails 6 💎 (#11) 2019-08-19 13:49:57 +05:30
cable.yml fix: Redis 6 on Heroku breaks ActionCable config (#4269) 2022-03-24 19:25:07 +05:30
database.yml perf: enable active record connection pool reaper (#10866) 2025-03-17 19:27:05 -07:00
elastic_apm.yml chore: Support for Elastic APM (#5004) 2022-07-22 11:39:37 +02:00
environment.rb Initial Commit 2019-08-14 15:18:44 +05:30
features.yml fix: V2 Assignment service enhancements (#13036) 2026-02-11 12:24:45 +05:30
installation_config.yml feat: add per-account daily rate limit for outbound emails (#13411) 2026-02-03 02:06:51 +05:30
llm.yml feat: add global config for captain settings (#13141) 2026-01-12 19:54:19 +05:30
markdown_embeds.yml fix: Update Arcade embed aspect ratio (#12923) 2025-11-24 20:22:27 +05:30
newrelic.yml fix: logic error when setting new relic logging forwarding (#8687) 2024-01-11 21:05:04 +05:30
puma.rb chore: Fix puma configuration (#5023) 2022-07-12 12:27:33 +02:00
rds-ca-2019-root.pem chore: add aws rds root cert for tls connection (#3812) 2022-02-01 16:01:25 +05:30
routes.rb feat(shopify): Add mandatory compliance webhooks with HMAC verification (#13549) 2026-02-17 16:52:13 +05:30
schedule.yml chore: temporarily disable ProcessStaleContactsJob (#13462) 2026-02-06 13:27:51 +05:30
scout_apm.yml chore: Load only required APMs (#6497) 2023-03-01 14:31:51 +05:30
secrets.yml Use secret_key_base from env 2019-08-15 23:08:36 +05:30
sidekiq.yml chore: add script to throttle bulkreindex job creation and increase meta timeouts(#12626) 2025-10-13 16:21:45 +05:30
spring.rb 🚨Fix Rubocop lint errors 2019-10-20 14:17:26 +05:30
storage.yml Revert "chore: Upgrade Rails to 7.2.2 and update Gemfile dependencies (#11037)" 2026-02-03 21:09:42 -08:00
vite.json feat: Vite + vue 3 💚 (#10047) 2024-10-02 00:36:30 -07:00