node.jsamazon-web-servicesexpressauthorize.net

Node/Express API runs locally but hangs up on API call in AWS Lambda


I have an Express API setup which works fine locally. However, when deployed to AWS Lambda it chokes/hangs on a call to an Authorize.net API and the Lambda function closes. Lambda/Cloudwatch logs don't indicate an error of any kind.

Completely at a loss.

Any help is appreciated.

authnet-api.js

const axios = require('../config/axios')
const loginId = process.env.AUTHORIZENET_LOGIN_ID
const transactionKey = process.env.AUTHORIZENET_TRANSACTION_KEY

class AuthNetAPI {
  constructor() {
    this.headers = { headers: { 'Content-Type': 'application/json' } }
  }
  async getTransactionDetails(transactionId) {
    return axios.authNetAxios.post(
      '/',
      {
        getTransactionDetailsRequest: {
          merchantAuthentication: {
            name: loginId,
            transactionKey: transactionKey,
          },
          transId: transactionId,
        },
      },
      this.headers
    )
  }
}
module.exports = new AuthNetAPI()

webhook controller

const moment = require('moment')
const AuthNetAPI = require('../lib/authnet-api')
const helpers = require('../utils/helpers')


module.exports.makeWebhookPayment = async (req, res, next) => {
  try {
    const rawBodyString = req.rawBody
    const notificationBody = req.body
    const authNetHash = req.header('X-ANET-Signature')
    const clientHash = await helpers.generateHash(rawBodyString)
    const timestamp = Date.now()
    const batch = `AuthorizeNet_Phone ${moment(timestamp).format('M/D/YYYY')}`
    const documentDate = moment(timestamp).format()

    if (authNetHash === clientHash) {
      res.sendStatus(200)
      const transactionId = req.body.payload.id

      console.log(transactionId) // this logs out.

      const transactionResult = await AuthNetAPI.getTransactionDetails(
        transactionId
      ) // this API call closes the Lambda function with no error

      const transaction = transactionResult.data.transaction
      // console.log(transaction) // this does not log out, obviously.

     ...// remainder of code
 
  } catch (error) {
    next(error)
  }
}

index.js (express app wrapped with serverless-http handler)

const serverless = require('serverless-http')
const app = require('./src/config/express')

process.env.ENVIRONMENT === 'production'
  ? (module.exports.handler = serverless(app))
  : app.listen(3000, () => {
      console.log('listening on port 3000')
    })

module.exports.handler = serverless(app)

Solution

  • The Authorize.net Webhooks API requires a response status of 200 when the webhook notification is received. Failure to do so may result in repeated notification retries and webhook inactivation.

    My particular problem had to do with res.sendStatus(200). This was returning the response to API Gateway and effectively closing the Lambda function. The solution was to call res.sendStatus(200) after the API call.

    const moment = require('moment')
    const AuthNetAPI = require('../lib/authnet-api')
    const helpers = require('../utils/helpers')
    
    
    module.exports.makeWebhookPayment = async (req, res, next) => {
      try {
        const rawBodyString = req.rawBody
        const notificationBody = req.body
        const authNetHash = req.header('X-ANET-Signature')
        const clientHash = await helpers.generateHash(rawBodyString)
        const timestamp = Date.now()
        const batch = `AuthorizeNet_Phone ${moment(timestamp).format('M/D/YYYY')}`
        const documentDate = moment(timestamp).format()
    
        if (authNetHash === clientHash) {
          const transactionId = req.body.payload.id
    
          console.log(transactionId)
    
          const transactionResult = await AuthNetAPI.getTransactionDetails(
            transactionId
          ) 
    
          const transaction = transactionResult.data.transaction
    
         ...// remainder of code
    
         res.sendStatus(200)
     
      } catch (error) {
        next(error)
      }
    }