Topics covering webhook creation & management, event handling, Pub/Sub, and Eventbridge, in Shopify apps.
We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more
Hi, I have most webhooks verifying correctly, but when I get an orders/create webhook, for some reason, the Shopify provided HMAC does not match up with what I generate locally. What I generate locally DOES work for all the other webhook topics I have received so far.
I would understand if this failed for every webhook, but seems strange that it works unless it is order/created.
Thanks!
Here is the Shopify generated HMAC:
FuV2vjolVOKvZYzJmJ00+ljaIvs39Pd9vgacfUIFhmY=
Here is the HMAC that my code generates:
1W8pg0D67CEi5P7kQSiZsVRk0ucdxbDREQgNohkqb18=
Here is the code I am using to generate the HMAC (in Node.js):
const digest = crypto
.createHmac('SHA256', CONFIG.shopify_shared_secret)
.update(new Buffer(payload, 'utf8'))
.digest('base64')
Here is the shared secret (Yes, I have already generated a new one):
62fa616bf4996b0c2133bf30e027585d
And finally, here is the payload (request body):
{"id":147605946387,"email":"aasdlfkj@gmail.com","closed_at":null,"created_at":"2018-01-14T18:17:03-05:00","updated_at":"2018-01-14T18:17:03-05:00","number":4,"note":null,"token":"e3c01016043a1f31760f05940a2d2535","gateway":"shopify_payments","test":true,"total_price":"56.79","subtotal_price":"25.00","total_weight":3629,"total_tax":"0.00","taxes_included":false,"currency":"USD","financial_status":"authorized","confirmed":true,"total_discounts":"0.00","total_line_items_price":"25.00","cart_token":"f34ce7eb136c13a8bf5b291fee7447d9","buyer_accepts_marketing":false,"name":"#1004","referring_site":"","landing_site":"/","cancelled_at":null,"cancel_reason":null,"total_price_usd":"56.79","checkout_token":"238c6fc795237ef5bc11ef0afc1a0667","reference":null,"user_id":null,"location_id":null,"source_identifier":null,"source_url":null,"processed_at":"2018-01-14T18:17:03-05:00","device_id":null,"phone":null,"customer_locale":"en","app_id":580111,"browser_ip":"104.175.174.161","landing_site_ref":null,"order_number":1004,"discount_codes":[],"note_attributes":[],"payment_gateway_names":["shopify_payments"],"processing_method":"direct","checkout_id":483722756115,"source_name":"web","fulfillment_status":null,"tax_lines":[],"tags":"","contact_email":"aasdlfkj@gmail.com","order_status_url":"https://cape-cod-made.myshopify.com/17862787/orders/e3c01016043a1f31760f05940a2d2535/authenticate?ke...","line_items":[{"id":257846771731,"variant_id":50621831379,"title":"Example T-Shirt","quantity":1,"price":"25.00","sku":null,"variant_title":"Lithograph - Height: 9\" x Width: 12\"","vendor":"Acme","fulfillment_service":"manual","product_id":12230177619,"requires_shipping":true,"taxable":true,"gift_card":false,"name":"Example T-Shirt - Lithograph - Height: 9\" x Width: 12\"","variant_inventory_management":null,"properties":[],"product_exists":true,"fulfillable_quantity":1,"grams":3629,"total_discount":"0.00","fulfillment_status":null,"tax_lines":[],"origin_location":{"id":171746820115,"country_code":"US","province_code":"AL","name":"cape-cod-made","address1":"25 Kellogg Rd","address2":"","city":"New Hartford","zip":"06057"},"destination_location":{"id":173186187283,"country_code":"US","province_code":"CA","name":"alskdfj asldkfj","address1":"asdfas","address2":"","city":"asdlkfj","zip":"90066"}}],"shipping_lines":[{"id":123562590227,"title":"Priority Mail","price":"31.79","code":"Priority","source":"usps","phone":null,"requested_fulfillment_service_id":null,"delivery_category":null,"carrier_identifier":null,"discounted_price":"31.79","tax_lines":[]}],"billing_address":{"first_name":"alskdfj","address1":"asdfas","phone":null,"city":"asdlkfj","zip":"90066","province":"California","country":"United States","last_name":"asldkfj","address2":"","company":null,"latitude":null,"longitude":null,"name":"alskdfj asldkfj","country_code":"US","province_code":"CA"},"shipping_address":{"first_name":"alskdfj","address1":"asdfas","phone":null,"city":"asdlkfj","zip":"90066","province":"California","country":"United States","last_name":"asldkfj","address2":"","company":null,"latitude":null,"longitude":null,"name":"alskdfj asldkfj","country_code":"US","province_code":"CA"},"fulfillments":[],"client_details":{"browser_ip":"104.175.174.161","accept_language":"en-US,en;q=0.9","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36","session_hash":null,"browser_width":1440,"browser_height":799},"refunds":[],"payment_details":{"credit_card_bin":"424242","avs_result_code":"Y","cvv_result_code":"M","credit_card_number":"•••• •••• •••• 4242","credit_card_company":"Visa"},"customer":{"id":156579168275,"email":"aasdlfkj@gmail.com","accepts_marketing":false,"created_at":"2018-01-14T18:16:18-05:00","updated_at":"2018-01-14T18:17:03-05:00","first_name":"alskdfj","last_name":"asldkfj","orders_count":0,"state":"disabled","total_spent":"0.00","last_order_id":null,"note":null,"verified_email":true,"multipass_identifier":null,"tax_exempt":false,"phone":null,"tags":"","last_order_name":null,"default_address":{"id":232084701203,"customer_id":156579168275,"first_name":"alskdfj","last_name":"asldkfj","company":null,"address1":"asdfas","address2":"","city":"asdlkfj","province":"California","country":"United States","zip":"90066","phone":null,"name":"alskdfj asldkfj","province_code":"CA","country_code":"US","country_name":"United States","default":true}}}
Anyone can run a test on this here: https://www.liavaag.org/English/SHA-Generator/HMAC/
Again, this works for every other webhook I have tested, just (so far) not on order/created. And at the link above, you will see that generating a base-64 output, text input HMAC gives "1W8pg0D67CEi5P7kQSiZsVRk0ucdxbDREQgNohkqb18=" which is NOT what Shopify API sends.
Is there anything else I can add or do to encourage a Shopify rep to pay attention to this issue?
Thanks,
Brendan
Hi Brendan,
I attempted to replicate this issue in my test shop with my own app, but was unable to. Order/create webhooks are being verified without issue.
If possible, do you mind if I test on the shop you are using? I'm wondering if it's a specific field on your shop that is causing the mismatch.
For reference, here's my verification in ruby.
request.body.rewind
data = request.body.read
digest = OpenSSL::Digest.new('sha256')
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, @secret, data)).strip
Cheers,
To learn more visit the Shopify Help Center or the Community Blog.
Thanks for the reply Busfox, I got a bit distracted tying up other loose ends. While it could be that some particular property in my shop was causing an issue, I found a way to make it work, and I should just document it here in case anyone else runs into it.
Basically, don't allow bodyParser to parse webhook bodies as JSON. It should just be treated as text, instead. Apparently, the JSON parsing/stringifying is causing some change in the payload.
(Examples are in Node.js using Express)
So rather than:
app.use(bodyParser.json())
Use something like:
// use text parser for webhooks
app.use(bodyParser.text({ type: req => req.get('X-Shopify-Hmac-Sha256') }))
// use json parser for non-webhooks
app.use(bodyParser.json({ type: req => !req.get('X-Shopify-Hmac-Sha256') }))
I just tested this a few different ways and so far so good 🙂
Hey Brendan! Thank you sooooo much for your solution here. I was pulling my hair out doing the same thing in PHP. Didn't know that decoding/encoding the body changes the HMAC integrity! You saved my day!