Request headers not present for a webhook call

Netlify Site Name: overcomer.netlify.app

Hello,
I am using Next.JS to create a webhook listener for Snipcart.
I noticed that when I hit the endpoint, the headers that were send are not present and I really need a specific token header to validate the webhook call, but it seems to disappear when it reach the endpoint.

Here is the function URL: https://overcomewithchrist.com/.netlify/functions/next_api_snipcart

Here is the header example for a call:

X-Forwarded-For: 10.87.180.33
X-Snipcart-Purpose: Webhooks
X-Http-Method-Override: POST
*X-Snipcart-RequestToken*: 7cab64cf-f741-4ecc-908e-0964f161fff7

I really need the X-Snipcart-RequestToken to be available. Here are the logs from hitting the endpoint using Postman and Snipcart:

6:40:32 PM 2022-01-05T00:40:32.953Z	undefined	INFO	Loaded env from .env
6:40:32 PM a9eabfd0 INFO   [request] /api/snipcart
6:40:32 PM a9eabfd0 INFO   {"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","accept-encoding":"br","client-ip":"100.64.0.62","connection":"keep-alive","forwarded":"for=54.165.144.195;proto=https","sec-fetch-dest":"document","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36 Prerender (+https://github.com/prerender/prerender)","via":"http/1.1 Netlify[ebbcbd64-1560-4314-b9e3-504151193e62] (Netlify Edge Server)","x-bb-ab":"0.745641","x-bb-client-request-uuid":"01FRKTWA95W752TNB0VXJHEQ66","x-bb-ip":"54.165.144.195","x-bb-loop":"1","x-cdn-domain":"www.bitballoon.com","x-country":"US","x-datadog-parent-id":"747442037802369116","x-datadog-sampling-priority":"1","x-datadog-trace-id":"17439331054613234698","x-forwarded-for":"54.165.144.195, 100.64.0.62","x-forwarded-proto":"https","x-nf-cdn-host":"cdn-reg-aws-jfk-10","x-nf-cdn-region":"jfk","x-nf-client-connection-ip":"54.165.144.195","x-nf-connection-proto":"https","x-nf-forwarded-proto":"https","x-nf-request-id":"01FRKTWA95W752TNB0VXJHEQ66","x-nf-request-start":"1641343232293657347","host":"overcomewithchrist.com","x-forwarded-host":"overcomewithchrist.com"}
6:40:32 PM a9eabfd0 INFO   Starting webhook call. tkn: undefined
6:40:33 PM a9eabfd0 INFO   Webhook call was not successfully authenticated.
6:40:33 PM a9eabfd0 Duration: 138.23 ms	Memory Usage: 69 MB	Init Duration: 297.94 ms
6:59:47 PM 2022-01-05T00:59:47.617Z	undefined	INFO	Loaded env from .env
6:59:47 PM 03b0fff0 INFO   [request] /api/snipcart
6:59:47 PM 03b0fff0 INFO   {"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","accept-encoding":"br","client-ip":"100.64.0.9","connection":"keep-alive","forwarded":"for=35.153.36.40;proto=https","sec-fetch-dest":"document","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36 Prerender (+https://github.com/prerender/prerender)","via":"http/1.1 Netlify[ed76a6c8-e4b6-4716-b050-f6560a07914f] (Netlify Edge Server)","x-bb-ab":"0.691241","x-bb-client-request-uuid":"01FRKVZHNDF5M9J7KB03P0M4Y1","x-bb-ip":"35.153.36.40","x-bb-loop":"1","x-cdn-domain":"www.bitballoon.com","x-country":"US","x-datadog-parent-id":"7369284433694139759","x-datadog-sampling-priority":"1","x-datadog-trace-id":"11589551373876637303","x-forwarded-for":"35.153.36.40, 100.64.0.9","x-forwarded-proto":"https","x-nf-cdn-host":"cdn-reg-do-jfk-22","x-nf-cdn-region":"jfk","x-nf-client-connection-ip":"35.153.36.40","x-nf-connection-proto":"https","x-nf-forwarded-proto":"https","x-nf-request-id":"01FRKVZHNDF5M9J7KB03P0M4Y1","x-nf-request-start":"1641344386733935437","host":"overcomewithchrist.com","x-forwarded-host":"overcomewithchrist.com"}
6:59:47 PM 03b0fff0 INFO   Starting webhook call. tkn: undefined
6:59:47 PM 03b0fff0 INFO   Webhook call was not successfully authenticated.
6:59:47 PM 03b0fff0 Duration: 138.99 ms	Memory Usage: 69 MB	Init Duration: 314.16 ms
7:17:38 PM 2022-01-05T01:17:38.828Z	undefined	INFO	Loaded env from .env
7:17:38 PM 87181aac INFO   [request] /api/snipcart
7:17:38 PM 87181aac INFO   {"accept":"*/*","accept-encoding":"br","client-ip":"100.64.0.243","connection":"keep-alive","content-length":"10086","content-type":"application/json","forwarded":"for=71.135.69.58;proto=https","postman-token":"0e4fd953-fd50-4533-bac7-c5006e9db948","user-agent":"PostmanRuntime/7.28.4","via":"http/1.1 Netlify[78a8ba06-084b-49af-8dce-8a37fc97921b] (Netlify Edge Server)","x-bb-ab":"0.559499","x-bb-client-request-uuid":"01FRKX07S83TKN9NKBGYZ4ERYM","x-bb-ip":"71.135.69.58","x-bb-loop":"1","x-cdn-domain":"www.bitballoon.com","x-country":"US","x-datadog-parent-id":"965807999649079289","x-datadog-sampling-priority":"1","x-datadog-trace-id":"9336536725464760778","x-forwarded-for":"10.87.180.33, 71.135.69.58, 100.64.0.243","x-forwarded-proto":"https","x-http-method-override":"POST","x-nf-cdn-host":"cdn-reg-aws-jfk-14","x-nf-cdn-region":"jfk","x-nf-client-connection-ip":"71.135.69.58","x-nf-connection-proto":"https","x-nf-forwarded-proto":"https","x-nf-request-id":"01FRKX07S83TKN9NKBGYZ4ERYM","x-nf-request-start":"1641345457960919990","x-snipcart-purpose":"Webhooks","x-snipcart-requesttoken":"2800003f-0380-457a-b9b8-9f7c558c68e9","host":"overcomewithchrist.com","x-forwarded-host":"overcomewithchrist.com"}
7:17:38 PM 87181aac INFO   Starting webhook call. tkn: undefined
7:17:38 PM 87181aac INFO   Webhook call was not successfully authenticated.
7:17:38 PM 87181aac Duration: 139.88 ms	Memory Usage: 69 MB	Init Duration: 330.63 ms
7:32:08 PM [ERROR] Function logs are currently unavailable. We are working on resolving the issue.

For each of the calls, the logged header did not have the request header I need. I am not sure how I should go about getting this header to be present correctly.

Let me know if I need to provide more information.
Thank you for your help.

Hi @LunarShadow

In the third call, I see see the headers.

Was this call made with Postman or Snipcart?

This call was made with Postman, as I can see the postman token in the header too. I didn’t notice that the snipcart stuff had made it with the postman call.

So, it could be an issue with the way Snipcart makes calls?

Of course you can! I could have too if I looked :man_facepalming:

Have you tried a tool such as Request catcher to see what data is sent by Snipcart? Will show headers, and other post data. And free.

Thanks for that suggestion! I tried it and here are the headers I got from snipcart:

POST / HTTP/1.1
Host: overcome.requestcatcher.com
Accept-Encoding: gzip, deflate
Content-Length: 7413
Content-Type: application/json; charset=utf-8
User-Agent: Snipcart/1.0
X-Forwarded-For: 10.87.180.33
X-Http-Method-Override: POST
X-Snipcart-Purpose: Webhooks
X-Snipcart-Requesttoken: 948dbc59-a795-4114-a658-91381a7c3f49
-----------------------
POST / HTTP/1.1
Host: overcome.requestcatcher.com
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Length: 1923
Content-Type: application/json; charset=utf-8
User-Agent: Snipcart/1.0
X-Forwarded-For: 10.87.180.33
X-Http-Method-Override: POST
X-Snipcart-Purpose: Webhooks
X-Snipcart-Requesttoken: 0edc6e93-39e9-47db-8df4-0e1272ad8477


So it looks like snipcart is sending the headers it’s saying it’s sending.

When I try again with my endpoint I get this in the logs:

11:09:19 PM: 2022-01-06T05:09:19.549Z	undefined	INFO	Loaded env from .env
11:09:19 PM: 6e3488a5 INFO   [request] /api/snipcart
11:09:19 PM: 6e3488a5 INFO   {"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","accept-encoding":"br","client-ip":"100.64.0.165","connection":"keep-alive","forwarded":"for=35.168.124.254;proto=https","sec-fetch-dest":"document","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36 Prerender (+https://github.com/prerender/prerender)","via":"http/1.1 Netlify[2112d459-4d98-43cc-a87d-f2c00cdd45bd] (Netlify Edge Server)","x-bb-ab":"0.801960","x-bb-client-request-uuid":"01FRPWN5SJ14PSE9GKFWD8YSAT","x-bb-ip":"35.168.124.254","x-bb-loop":"1","x-cdn-domain":"www.bitballoon.com","x-country":"US","x-datadog-parent-id":"6915025803744972184","x-datadog-sampling-priority":"1","x-datadog-trace-id":"10506961947069998215","x-forwarded-for":"35.168.124.254, 100.64.0.165","x-forwarded-proto":"https","x-nf-cache-gen":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg.eyJnZW4iOiI2MWQ0ZTY5ZjE4ZGJmMzAwMDkxZTZkNTk6MTY0MTM0Mjg2NTUyMSJ9Cg.69jM4oEOzqghvnG5P0uz6T4T6FKcJLQjam3vtld3PYA","x-nf-cdn-host":"cdn-reg-aws-jfk-16","x-nf-cdn-region":"jfk","x-nf-client-connection-ip":"35.168.124.254","x-nf-connection-proto":"https","x-nf-forwarded-proto":"https","x-nf-request-id":"01FRPWN5SJ14PSE9GKFWD8YSAT","x-nf-request-start":"1641445758770885031","host":"overcomewithchrist.com","x-forwarded-host":"overcomewithchrist.com"}
11:09:19 PM: 6e3488a5 INFO   Starting webhook call. tkn: undefined
11:09:19 PM: 6e3488a5 INFO   Webhook call was not successfully authenticated.
11:09:19 PM: 6e3488a5 Duration: 119.25 ms	Memory Usage: 69 MB	Init Duration: 282.99 ms	11:09:22 PM: 6d9ac663 INFO   [request] /api/snipcart
11:09:22 PM: 6d9ac663 INFO   {"accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","accept-encoding":"br","client-ip":"100.64.0.246","connection":"keep-alive","forwarded":"for=54.156.21.17;proto=https","sec-fetch-dest":"document","sec-fetch-mode":"navigate","sec-fetch-site":"none","sec-fetch-user":"?1","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/91.0.4472.114 Safari/537.36 Prerender (+https://github.com/prerender/prerender)","via":"http/1.1 Netlify[e60b36c5-15e0-41e5-aeae-260cd6cef031] (Netlify Edge Server)","x-bb-ab":"0.588430","x-bb-client-request-uuid":"01FRPWN9K05WVSS5DTE0TBHWQC","x-bb-ip":"54.156.21.17","x-bb-loop":"1","x-cdn-domain":"www.bitballoon.com","x-country":"US","x-datadog-parent-id":"7400277249048670978","x-datadog-sampling-priority":"1","x-datadog-trace-id":"12683771937819905045","x-forwarded-for":"54.156.21.17, 100.64.0.246","x-forwarded-proto":"https","x-nf-cache-gen":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9Cg.eyJnZW4iOiI2MWQ0ZTY5ZjE4ZGJmMzAwMDkxZTZkNTk6MTY0MTM0Mjg2NTUyMSJ9Cg.69jM4oEOzqghvnG5P0uz6T4T6FKcJLQjam3vtld3PYA","x-nf-cdn-host":"cdn-reg-aws-jfk-14","x-nf-cdn-region":"jfk","x-nf-client-connection-ip":"54.156.21.17","x-nf-connection-proto":"https","x-nf-forwarded-proto":"https","x-nf-request-id":"01FRPWN9K05WVSS5DTE0TBHWQC","x-nf-request-start":"1641445762656083187","host":"overcomewithchrist.com","x-forwarded-host":"overcomewithchrist.com"}
11:09:22 PM: 6d9ac663 INFO   Starting webhook call. tkn: undefined
11:09:22 PM: 6d9ac663 INFO   Webhook call was not successfully authenticated.
11:09:22 PM: 6d9ac663 Duration: 62.56 ms	Memory Usage: 69 MB

Seems I’m still having the same issue when it calls my endpoint and it seems Netlify is not passing/using the same headers as the webhook call, and is adding a lot of other fillers in the header as well.

Not sure how to handle this? :confused:

With a simple function

exports.handler = async (event, context) => {
  return {
    statusCode: 200,
    body: JSON.stringify(event)
  };
};

I used cURL to POST the headers you are wanting/needing/looking for

curl -X POST \
-H "X-Forwarded-For: 10.87.180.33" \
-H "X-Http-Method-Override: POST" \
-H "X-Snipcart-Purpose: Webhooks" \
-H "X-Snipcart-Requesttoken: 0edc6e93-39e9-47db-8df4-0e1272ad8477" \
-d {} \
https://someplace.example.com/blah/blah/blah

As the function returns the event in the body as a string I see

{
  "rawUrl": "https://someplace.example.com",
  "rawQuery": "",
  "path": "/blah/blah/blah",
  "httpMethod": "POST",
  "headers": {
    // lots of other headers
    "x-forwarded-for": "10.87.180.33, REDACTED, REDACTED",
    "x-http-method-override": "POST",
    "x-snipcart-purpose": "Webhooks",
    "x-snipcart-requesttoken": "0edc6e93-39e9-47db-8df4-0e1272ad8477"
  },
  "multiValueHeaders": {
    "X-Forwarded-For": ["10.87.180.33, REDACTED, REDACTED"],
    "X-Http-Method-Override": ["POST"],
    "X-Snipcart-Purpose": ["Webhooks"],
    "X-Snipcart-Requesttoken": ["0edc6e93-39e9-47db-8df4-0e1272ad8477"],
  },
  "queryStringParameters": {},
  "multiValueQueryStringParameters": {},
  "body": "{}",
  "isBase64Encoded": false
}

Could you share the function code you are using to read the headers?

Sure thing, here is the link to the file on github:

Ah, you are trying to integrate this directly into a Next.js API call, as opposed to creating a serverless function (as I did.) While the Netlify Next.js plugin does (I understand; I don’t do a lot with Next.js) migrate the API functions to serverless functions, I cannot say if they work the same way and receive the same information. Given the experience you have had, I might assume they don’t.

Are you wanting to show this information to the user placing the order, or is it strictly for your use? If the latter, I suggest migrating to a Netlify function. If the former, I’m not certain (but also unsure) that it is possible.

Yes, I am integrating directly to Next.js API call. And No it’s not supposed to be user/customer facing.

If I switch it to a server less function, do I need to create a new project and have a yaml? I have worked with serverless sparingly, so not sure what the setup procedure would be like?

Would it be as simple as changing the export default function name to export.handler?

If I switch it to a server less function, do I need to create a new project and have a yaml? I have worked with serverless sparingly, so not sure what the setup procedure would be like?

You shouldn’t need to create a new project to do this, you’ll just need to create a Netlify Function. Here is a link to the doc which goes over this in more detail: Configure and deploy functions | Netlify Docs

Would it be as simple as changing the export default function name to export.handler ?

This is close, but you’ll actually need to get rid of the default keyword and reformat the return statements as described in this blog post (eg. providing a return statement with both a statusCode property for the status code and a body property).

One more note - we are taking this to our internal Next.js experts to confirm if this functionality will be supported natively in the future or if you’ll need to continue to convert it to Netlify Functions. We should receive word back on this by next Wednesday, 12 January and we can confirm with you then.

Let me know if this helps or if you have any other questions!

2 Likes

Okay, thank you.
I will work on creating a Netlify Function while I wait to hear back on 12 January 2022.
If anything else comes up, still related to this, in the mean time, I will post it here.

1 Like

So, I switched over to a netlify function for this webhook listener and I am still having the same issue.
When SnipCart sends the info, I automatically get a 400 error, and no logging is seen.

I ran the function locally using the netlify cli, and was able to hit the netlify function’s endpoint in netlify dev using the same headers snipcart sends (after getting it from requestheaders.com a fresh).

So locally and in dev I can hit the endpoint correctly using postman and the snipcart headers.
But in prod when snipcart tries to do it, or even when I try to hit it with postman with the snipcart headers, it gets an automatic 400 - Bad request.

the 400 is not coming from my code, as I return only 401 for unauthorized requests.

Here are the request as recorded by requestheaders.com:

POST / HTTP/1.1
Host: overcome.requestcatcher.com
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Length: 7410
Content-Type: application/json; charset=utf-8
Request-Context: appId=cid-v1: 48f65c7b-986c-4ec3-89de-eb0160f55639
Request-Id: |880184996de58943931fdc3d34d7881c.c495e6ab8c581d4f.
Traceparent: 00-880184996de58943931fdc3d34d7881c-c495e6ab8c581d4f-00
User-Agent: Snipcart/1.0
X-Forwarded-For: 10.87.180.33
X-Http-Method-Override: POST
X-Snipcart-Purpose: Webhooks
X-Snipcart-Requesttoken: f4aab955-40ca-45d2-a1bd-0c9069d27fe1

{
  "eventName": "order.completed",
  "mode": "Test",
  "createdOn": "2022-01-06T04:59:38.4480094Z",
  "content": {
    "discounts": [],
    "items": [
      {
        "paymentSchedule": {
          "interval": 0,
          "intervalCount": 1,
          "trialPeriodInDays": null,
          "startsOn": "2022-01-06T00:00:00Z"
        },
        "pausingAction": "None",
        "cancellationAction": "None",
        "token": "token",
        "name": "Test 2",
        "price": 20.07,
        "quantity": 1,
        "fileGuid": null,
        "url": "https://overcomewithchrist.com/shop/6181938eeec1bf08593dd5c2",
        "id": "fake-id",
        "initialData": "",
        "description": "Test 2",
        "categories": [],
        "totalPriceWithoutTaxes": 20.07,
        "weight": null,
        "image": "https://images-api.printify.com/mockup/6181938eeec1bf08593dd5c2/18398/50/test-2.jpg",
        "originalPrice": null,
        "uniqueId": "fake-id",
        "stackable": false,
        "minQuantity": null,
        "maxQuantity": null,
        "addedOn": "2022-01-06T04:58:52Z",
        "modificationDate": "2022-01-06T04:59:02Z",
        "shippable": true,
        "taxable": true,
        "duplicatable": false,
        "width": null,
        "height": null,
        "length": null,
        "metadata": null,
        "totalPrice": 20.07,
        "totalWeight": 0.0,
        "taxes": [],
        "alternatePrices": {},
        "customFields": [
          {
            "name": "Size",
            "placeholder": "",
            "displayValue": "M",
            "type": "dropdown",
            "options": "S|M|L|XL|2XL",
            "required": true,
            "value": "M",
            "operation": null,
            "optionsArray": [
              "S",
              "M",
              "L",
              "XL",
              "2XL"
            ]
          },
          {
            "name": "sku",
            "placeholder": "",
            "displayValue": "a-sku",
            "type": "readonly",
            "options": "",
            "required": false,
            "value": "sku-value",
            "operation": null,
            "optionsArray": null
          },
          {
            "name": "Note",
            "placeholder": "",
            "displayValue": "Jan 5 test",
            "type": "textbox",
            "options": "",
            "required": false,
            "value": "Jan 5 test",
            "operation": null,
            "optionsArray": null
          }
        ],
        "unitPrice": 20.07,
        "hasDimensions": false,
        "hasTaxesIncluded": false,
        "totalPriceWithoutDiscountsAndTaxes": 20.07
      }
    ],
    "plans": [],
    "refunds": [],
    "taxes": [],
    "user": {
      "id": "fake-id",
      "email": "test@test.com",
      "mode": "Test",
      "statistics": {
        "ordersCount": 0,
        "ordersAmount": null,
        "subscriptionsCount": 0
      },
      "creationDate": "2021-12-30T05:59:38.103Z",
      "billingAddressFirstName": null,
      "billingAddressName": "John Doe",
      "billingAddressCompanyName": null,
      "billingAddressAddress1": "123 Address",
      "billingAddressAddress2": "",
      "billingAddressCity": "Farmers",
      "billingAddressCountry": "US",
      "billingAddressProvince": "AZ",
      "billingAddressPostalCode": "00000",
      "billingAddressPhone": "",
      "shippingAddressFirstName": null,
      "shippingAddressName": "John Doe",
      "shippingAddressCompanyName": null,
      "shippingAddressAddress1": "123 Address Lane",
      "shippingAddressAddress2": "",
      "shippingAddressCity": "Farmers",
      "shippingAddressCountry": "US",
      "shippingAddressProvince": "AZ",
      "shippingAddressPostalCode": "00000",
      "shippingAddressPhone": "",
      "shippingAddressSameAsBilling": true,
      "status": "Unconfirmed",
      "sessionToken": null,
      "gravatarUrl": "https://www.gravatar.com/avatar/0a309da61585c00f46f6237d3e163866?s=70&d=https%3a%2f%2fcdn.snipcart.com%2fassets%2fimages%2favatar.jpg",
      "billingAddress": {
        "fullName": "John Doe",
        "firstName": null,
        "name": "John Doe",
        "company": null,
        "address1": "123 Address Lane",
        "address2": "",
        "fullAddress": "123 Address Lane",
        "city": "Farmers ",
        "country": "US",
        "postalCode": "00000",
        "province": "AZ",
        "phone": "",
        "vatNumber": null,
        "hasMinimalRequiredInfo": true,
        "validationErrors": {}
      },
      "shippingAddress": {
        "fullName": "John Doe",
        "firstName": null,
        "name": "John Doe",
        "company": null,
        "address1": "123 Address Lane",
        "address2": "",
        "fullAddress": "123 Address Lane",
        "city": "Farmers",
        "country": "US",
        "postalCode": "75234",
        "province": "AZ",
        "phone": "",
        "vatNumber": null,
        "hasMinimalRequiredInfo": true,
        "validationErrors": {}
      }
    },
    "token": "token",
    "isRecurringOrder": false,
    "isRecurringV3Order": false,
    "parentToken": null,
    "parentInvoiceNumber": null,
    "subscriptionId": null,
    "currency": "usd",
    "creationDate": "2022-01-06T04:58:52Z",
    "modificationDate": "2022-01-06T04:59:37Z",
    "recoveredFromCampaignId": null,
    "status": "Processed",
    "paymentStatus": "Paid",
    "email": "test@email.com",
    "willBePaidLater": false,
    "billingAddress": {
      "fullName": "John Doe",
      "firstName": null,
      "name": "John Doe",
      "company": null,
      "address1": "123 Address Lane",
      "address2": "",
      "fullAddress": "123 Address Lane",
      "city": "Farmers",
      "country": "US",
      "postalCode": "75234",
      "province": "AZ",
      "phone": "",
      "vatNumber": null,
      "hasMinimalRequiredInfo": true,
      "validationErrors": {}
    },
    "shippingAddress": {
      "fullName": "John Doe",
      "firstName": null,
      "name": "John Doe",
      "company": null,
      "address1": "123 Address Lane",
      "address2": "",
      "fullAddress": "123 Address Lane",
      "city": "Farmers",
      "country": "US",
      "postalCode": "75234",
      "province": "AZ",
      "phone": "",
      "vatNumber": null,
      "hasMinimalRequiredInfo": true,
      "validationErrors": {}
    },
    "shippingAddressSameAsBilling": true,
    "creditCardLast4Digits": "4242",
    "trackingNumber": null,
    "trackingUrl": null,
    "shippingFees": null,
    "shippingProvider": null,
    "shippingMethod": null,
    "cardHolderName": null,
    "paymentMethod": "CreditCard",
    "notes": null,
    "customFieldsJson": "[{\"Name\":\"subscribeToNewsletter\",\"Type\":\"checkbox\",\"Options\":null,\"Required\":false,\"Value\":null,\"Operation\":null,\"OptionsArray\":[\"true\",\"false\"]},{\"Name\":\"privacy-policy\",\"DisplayValue\":\"true\",\"Type\":\"checkbox\",\"Options\":null,\"Required\":false,\"Value\":\"true\",\"Operation\":null,\"OptionsArray\":[\"true\",\"false\"]}]",
    "userId": "49abafdd-9a55-4226-a715-c4c1138d1154",
    "completionDate": "2022-01-06T04:59:36Z",
    "cardType": "Visa",
    "paymentGatewayUsed": "SnipcartPaymentService",
    "paymentDetails": {
      "iconUrl": null,
      "display": null,
      "instructions": null
    },
    "taxProvider": "Default",
    "lang": "en",
    "refundsAmount": 0.0,
    "adjustedAmount": 20.07,
    "finalGrandTotal": 20.07,
    "billingAddressFirstName": null,
    "billingAddressName": "John Doe",
    "billingAddressCompanyName": null,
    "billingAddressAddress1": "123 Address Lane",
    "billingAddressAddress2": "",
    "billingAddressCity": "Farmers",
    "billingAddressCountry": "US",
    "billingAddressProvince": "AZ",
    "billingAddressPostalCode": "75234",
    "billingAddressPhone": "",
    "shippingAddressFirstName": null,
    "shippingAddressName": "John Doe",
    "shippingAddressCompanyName": null,
    "shippingAddressAddress1": "123 Address Lane",
    "shippingAddressAddress2": "",
    "shippingAddressCity": "Farmers",
    "shippingAddressCountry": "US",
    "shippingAddressProvince": "AZ",
    "shippingAddressPostalCode": "75234",
    "shippingAddressPhone": "",
    "totalNumberOfItems": 0,
    "invoiceNumber": "OWC17",
    "billingAddressComplete": true,
    "shippingAddressComplete": true,
    "shippingMethodComplete": false,
    "savedAmount": 0.0,
    "subtotal": 20.07,
    "baseTotal": 20.07,
    "itemsTotal": 20.07,
    "totalPriceWithoutDiscountsAndTaxes": 20.07,
    "taxableTotal": 20.07,
    "grandTotal": 20.07,
    "total": 20.07,
    "totalWeight": 0.0,
    "totalRebateRate": 0.0,
    "customFields": [
      {
        "name": "subscribeToNewsletter",
        "type": "checkbox",
        "options": null,
        "required": false,
        "value": null,
        "operation": null,
        "optionsArray": [
          "true",
          "false"
        ]
      },
      {
        "name": "privacy-policy",
        "displayValue": "true",
        "type": "checkbox",
        "options": null,
        "required": false,
        "value": "true",
        "operation": null,
        "optionsArray": [
          "true",
          "false"
        ]
      }
    ],
    "shippingEnabled": true,
    "numberOfItemsInOrder": 1,
    "paymentTransactionId": "adfsdfadfadfad",
    "metadata": null,
    "taxesTotal": 0.0,
    "itemsCount": 1,
    "summary": {
      "subtotal": 20.07,
      "taxableTotal": 20.07,
      "total": 20.07,
      "payableNow": 20.07,
      "paymentMethod": "CreditCard",
      "taxes": [],
      "discountInducedTaxesVariation": 0.0,
      "adjustedTotal": 20.07,
      "shipping": null
    },
    "ipAddress": "71.135.69.58",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15",
    "hasSubscriptions": false
  }
}

Here is the code:

Here is the dev environment logs:

Request from ::1: GET /.netlify/functions/snipcart
{"content-length":"10086","host":"localhost:8888","postman-token":"de62a4b6-e026-4cae-bfc2-d3263fd789ce","traceparent":"00-bcd95675b9c6034990acf5f894cb7ed3-7f9f4c38686bc645-00","request-id":"bcd95675b9c6034990acf5f894cb7ed3.7f9f4c38686bc645.","request-context":"appId=cid-v1:48f65c7b-986c-4ec3-89de-eb0160f55639","content-type":"application/json; charset=utf-8","accept-encoding":"gzip, deflate","x-http-method-override":"POST","x-snipcart-purpose":"Webhooks","x-forwarded-for":"::1","user-agent":"Snipcart/1.0","x-snipcart-requesttoken":"e12e9ecc-d931-4571-bef3-219ec928fb9f","connection":"close","client-ip":"::1"}
Starting webhook call. tkn: e12e9ecc-d931-4571-bef3-219ec928fb9f
Webhook call was not successfully authenticated.
Response with status 401 in 377 ms.

Here is what the SnipCart dash board for the request looks like if it will help:

Let me know what other information I need to provide.

I got same logs when trying with Postman. Basically, I got 401 statuscode and I checked your function logs which contained logs similar to the ones you’ve posted here.

So, how exactly do we authenticate here?

Hey hrisbikesh,

My issue isn’t authentication, my issue is that Snipcart is not able to hit the endpoint correctly for some reason, it always returns a 400 (not a 401).
And I get the same issue in Prod when I send the same headers as snipcart (without the extra postman headers except for the postman token).

So I am trying to pinpoint at which end the problem is. Whether my code or something with Snipcart?

But to answer your question, authentication happens by taking the x-Snipcart-requesttoken and calling a Snipcart endpoint with that token. If the token is not expired, Snipcart will return a 200 status. (I have confirmed that this works already by using a non expired token)

However, when Snipcart sends its webhook, it just get a 400 error. It doesn’t actually seem to even hit my code. And I don’t get any more detail than the 400 - Bad request error :confused:

I suggest (unfortunately) is it likely your code as when you use another service (you mentioned https://requestheaders.com/ which is non-reachable address; did you meanhttps://requestcatcher.com/as I previously mentioned?) SnipCart does not receive a 400.

If you create a super-simple function to capture the POST from SnipCart and log out the token, do you get the same result, or does SnipCart receive the OK and the token get logged to the function logs?

Something like this

exports.handler = async (event, context) => {
  const token = event.headers['x-snipcart-requesttoken']

  console.log("SnipCart token: %s", token)

  return {
    statusCode: 200,
    body: "OK"
  };
};

This is the SnipCart dashboard after deploying the code above that you gave.
I got the 200 once, then after that, it was all 400.

And for the successful call, the logs didn’t have the token present in it:

5:18:39 PM: 6b6e269b INFO   SnipCart token: undefined
5:18:39 PM: 6b6e269b Duration: 3.50 ms	Memory Usage: 56 MB	Init Duration: 182.66 ms	

:confused:

Very strange indeed :confused:

What if you change this function to log the headers (and even the multiValueHeaders) and return a 200 to Snipcart.

exports.handler = async (event, context) => {
  
console.log("Event headers:\n %s", event.headers);
console.log("Event multiValueHeaders:\n %s", event.multiValueHeaders);

  return {
    statusCode: 200,
    body: "OK"
  };
};

Does either event.headers or event.multiValueHeaders contain the Snipcart headers you are looking for?

:confused:

Just kept getting 400 errors. The endpoint didn’t get hit.

I just hit the function with

X-Snipcart-RequestToken: 'i-am-coelmay'

and received OK.

Does this appear in the function logs?

I suggest contacting Snipcart support to see if they can shed any light on the subject, or the Snipcart Forum. Snipcart’s documentation on Webhook is a little lacking unfortunately.

Yes, it did hit the function.

9:19:58 PM 5249db35 INFO   Event headers:
 {
  accept: '*/*',
  'client-ip': '100.64.0.89',
  connection: 'keep-alive',
  'content-length': '2',
  'content-type': 'application/x-www-form-urlencoded',
  forwarded: 'for="[2001:8003:430b:ce01:6994:46da:db39:97b5]";proto=https',
  host: 'overcomewithchrist.com',
  'user-agent': 'curl/7.77.0',
  via: 'http/1.1 Netlify[e9549e1c-4597-4520-896e-8e0d36bdac12] (Netlify Edge Server)',
  'x-bb-ab': '0.243644',
  'x-bb-client-request-uuid': '01FRVV6C8MMN4EMB3F2K8DGHWY',
  'x-bb-ip': '2001:8003:430b:ce01:6994:46da:db39:97b5',
  'x-bb-loop': '1',
  'x-cdn-domain': 'www.bitballoon.com',
  'x-country': 'AU',
  'x-datadog-parent-id': '2579610731178645492',
  'x-datadog-sampling-priority': '1',
  'x-datadog-trace-id': '17874152178162033762',
  'x-forwarded-for': '2001:8003:430b:ce01:6994:46da:db39:97b5, 100.64.0.89',
  'x-forwarded-proto': 'https',
  'x-nf-cdn-host': 'cdn-reg-aws-syd-5',
  'x-nf-cdn-region': 'syd',
  'x-nf-client-connection-ip': '2001:8003:430b:ce01:6994:46da:db39:97b5',
  'x-nf-connection-proto': 'https',
  'x-nf-forwarded-proto': 'https',
  'x-nf-request-id': '01FRVV6C8MMN4EMB3F2K8DGHWY',
  'x-nf-request-start': '1641611997460593677',
  'x-snipcart-requesttoken': 'i-am-coelmay'
}
9:19:58 PM 5249db35 INFO   Event multiValueHeaders:
 {
  Accept: [Array],
  'Client-Ip': [Array],
  Connection: [Array],
  'Content-Length': [Array],
  'Content-Type': [Array],
  Forwarded: [Array],
  'User-Agent': [Array],
  Via: [Array],
  'X-Bb-Ab': [Array],
  'X-Bb-Client-Request-Uuid': [Array],
  'X-Bb-Ip': [Array],
  'X-Bb-Loop': [Array],
  'X-Cdn-Domain': [Array],
  'X-Country': [Array],
  'X-Datadog-Parent-Id': [Array],
  'X-Datadog-Sampling-Priority': [Array],
  'X-Datadog-Trace-Id': [Array],
  'X-Forwarded-For': [Array],
  'X-Forwarded-Proto': [Array],
  'X-Nf-Cdn-Host': [Array],
  'X-Nf-Cdn-Region': [Array],
  'X-Nf-Client-Connection-Ip': [Array],
  'X-Nf-Connection-Proto': [Array],
  'X-Nf-Forwarded-Proto': [Array],
  'X-Nf-Request-Id': [Array],
  'X-Nf-Request-Start': [Array],
  'X-Snipcart-Requesttoken': [Array],
  host: [Array]
}
9:19:58 PM 5249db35 Duration: 5.85 ms	Memory Usage: 56 MB	Init Duration: 179.45 ms

Yeah, I’ve reached out to them twice this week. I haven’t gotten a reply since after their first reply.

I will probably just stop trying to use SnipCart at this point. It’s really putting me behind.

Thank you guys for all your help and support.

1 Like