0

In trying to verify Authorize.net webhook notifications in Node JS I've run into the following issue having to do with decimal numbers/trailing zeros:

Authorize.net forms a hash using HMAC-SHA512, with the body of the webhook notification and the merchant's Signature Key. That hash, and the hash I generate do not match.

Here is the JSON response from a test transaction in Postman which returns a decimal number for "authAmount" for an input of "300":

{
  "_links": {
    "self": {
      "href": "/rest/v1/notifications/92782537-6a15-4e8f-8e2f-0710b1e6105d"
    }
  },
  "notificationId": "92782537-6a15-4e8f-8e2f-0710b1e6105d",
  "deliveryStatus": "Delivered",
  "eventType": "net.authorize.payment.authcapture.created",
  "eventDate": "2022-08-19T15:34:56.467",
  "webhookId": "a8a17f6f-79eb-49f4-a047-436db0938bbb",
  "payload": {
    "responseCode": 1,
    "authCode": "PYSH10",
    "avsResponse": "Y",
    "authAmount": 300.00,  // <-- note the decimal number
    "merchantReferenceId": "123456",
    "entityName": "transaction",
    "id": "60198731341"
  },
  "notificationDate": "2022-08-19T15:35:05.07"
}

Here is my code:


app.post('/notification', (req, res) => {

  const authNetHash = req.header('X-ANET-Signature')
  // req.body.payload.authAmount = req.body.payload.authAmount.toFixed(2)
  const notificationBody = JSON.stringify(req.body);
  const notificationHash = crypto.createHmac('sha512', config.AUTHORIZENET.signature_key)
    .update(notificationBody)
    .digest('hex');

  console.log(req.body)
  console.log(notificationBody)
  console.log(`authorizenet_hash--> ${authNetHash}`)
  console.log(`my_hash--> sha512=${notificationHash.toUpperCase()}`)
    
      res.status(200).end()
    })

and the resulting console log output:

{
  notificationId: '92782537-6a15-4e8f-8e2f-0710b1e6105d',
  eventType: 'net.authorize.payment.authcapture.created',
  eventDate: '2022-08-19T15:34:56.4671136Z',
  webhookId: 'a8a17f6f-79eb-49f4-a047-436db0938bbb',
  payload: {
    responseCode: 1,
    authCode: 'PYSH10',
    avsResponse: 'Y',
    authAmount: 300,
    merchantReferenceId: '123456',
    entityName: 'transaction',
    id: '60198731341'
  }
}
{"notificationId":"92782537-6a15-4e8f-8e2f-0710b1e6105d","eventType":"net.authorize.payment.authcapture.created","eventDate":"2022-08-19T15:34:56.4671136Z","webhookId":"a8a17f6f-79eb-49f4-a047-436db0938bbb","payload":{"responseCode":1,"authCode":"PYSH10","avsResponse":"Y","authAmount":300,"merchantReferenceId":"123456","entityName":"transaction","id":"60198731341"}}
authorizenet_hash--> sha512=4ACA3F76717FBFB3E1FA38587A5057D62B2A6D70123D5B254804CBA461D1D9175CBC915B4DE6F13C43C78CF330DFFF3DC8579662C6CAFBEA7AC58D5D98E5A8DF
my_hash--> sha512=814C18B2A505E9B6385716E49D8C5AC143463741945F6E1FFE8C4450281C093C6D38308D06B1A41587D7E5EAEC1E8417BCC50D1D7039B47E3CEA2D0EE845455C

The hash mismatch is occurring as the parsed data is dropping the decimal value from "authAmount" as there is no fractional value. And of course this also occurs if a fractional value exists, but with a single decimal value (i.e. 300.10 would be parsed as 300.1). I realize this is how javascript treats decimals. Using toFixed(2) on that value doesn't help as this returns a string, in which case the quotes throw the hash off.

I'm either missing the obvious or approaching this in a very stupid way, or both. I sure of it as I can't seem to find anyone having this problem.

Thanks in advance for any help.

romeplow
  • 462
  • 1
  • 4
  • 13

1 Answers1

1

The solution I found was to get the raw request body, convert it to a string, and use that in generating the hash.

app.use(
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf.toString()
    },
  })
)
romeplow
  • 462
  • 1
  • 4
  • 13