Using the new Stripe Checkout in Gatsby + AWS Amplify: Payment confirmation (webhook)

beezfedia

Beez Fedia

Posted on June 19, 2020

Using the new Stripe Checkout in Gatsby + AWS Amplify: Payment confirmation (webhook)

This is the final post of the Using the new Stripe Checkout in Gatsby + AWS Amplify series. Previously, you completed the server-side setup by writing an AWS Lambda function that returned a new Stripe session for the checkout and you wrote the client-side CheckoutButton component that redirects your customer to Stripe Checkout. In this post, you'll implement a webhook that listens out for confirmation from Stripe that the customer's payment has been completed.

Take a look at the code from this blog post on Github.

Step 1. Update your API to include a stripeHook

If you have been following along with these posts, you will already have AWS Amplify setup within your Gatsby project. Running amplify status from the terminal will show you the status of your AWS services. Your project should include a stripeCheckout function and a stripeAPI.

Amplify table of services
To update the stripeAPI run:

amplify api update
Enter fullscreen mode Exit fullscreen mode

The Amplify CLI will walk you through a series of questions:

Please select from one of the below mentioned services:
Enter fullscreen mode Exit fullscreen mode

You want to update your REST API.

Please select the REST API you would want to update
Enter fullscreen mode Exit fullscreen mode

Select stripeAPI.

What would you like to do
Enter fullscreen mode Exit fullscreen mode

You want to add a new path to the stripeAPI.

Provide a path (e.g., /book/{isbn}):
Enter fullscreen mode Exit fullscreen mode

Enter /webhook.

Choose a Lambda source
Enter fullscreen mode Exit fullscreen mode

Select Create a new Lambda function.

Provide a friendly name for your resource to be used as a label for this category in the project:
Enter fullscreen mode Exit fullscreen mode

Enter stripeHook.

Provide the AWS Lambda function name:
Enter fullscreen mode Exit fullscreen mode

Press Enter to select the default option of stripeHook.

Choose the function runtime that you want to use:
Enter fullscreen mode Exit fullscreen mode

Select NodeJS

Choose the function template that you want to use:
Enter fullscreen mode Exit fullscreen mode

Select Serverless ExpressJS function (Integration with API Gateway)

Do you want to access other resources created in this project from your Lambda function?
Enter fullscreen mode Exit fullscreen mode

Enter n.

Do you want to invoke this function on a recurring schedule?
Enter fullscreen mode Exit fullscreen mode

Enter n.

Do you want to edit the local lambda function now?
Enter fullscreen mode Exit fullscreen mode

Enter n.

Restrict API access
Enter fullscreen mode Exit fullscreen mode

Enter n.

Do you want to add another path?
Enter fullscreen mode Exit fullscreen mode

Enter n.

If you run amplify status again you'll see that the new stripeHook function has been created and the stripeAPI has been updated.

Updated Amplify status

Step 2. Install Stripe

Now the Amplify configuration has been completed, in your terminal navigate to the amplify/backend/function/stripeHook/src folder within your Gatsby project and install Stripe:

cd amplify/backend/function/stripeHook/src
npm install stripe
Enter fullscreen mode Exit fullscreen mode

Step 3. Add your Stripe secret key and endpoint secret

Open the app.js folder within the /amplify/backend/functions/stripeHook/src/ directory.

To the top of this file add constants for your Stripe secret key and endpoint secret:

// Stripe parameters
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY)
const endpointSecret = process.env.STRIPE_ENDPOINT_SECRET
Enter fullscreen mode Exit fullscreen mode

You will add the STRIPE_SECRET_KEY and STRIPE_ENDPOINT_SECRET environment variables later via your AWS Lambda console.

Step 4. Ensure Stripe has access to the raw request body

When Stripe calls your webhook it runs identity related security checks. It expects the raw request body for the signature to match the expected signature for the payload. By default, our Lambda automatically stringifys the request body so you need to update the bodyParser to include the raw body.

Comment out the call to bodyParser.json():

// app.use(bodyParser.json()) 
Enter fullscreen mode Exit fullscreen mode

Replace this line with the following code in order to include the rawBody within the request params:

// Add raw body to req params for Stripe signature check
app.use(
  bodyParser.json({
    verify: function(req, res, buf) {
      req.rawBody = buf.toString()
    },
  })
)
Enter fullscreen mode Exit fullscreen mode

Step 4. Write the stripeHook function

Delete the example get, post, put and delete code and replace it with the following empty async post function:

app.post("/webhook", async function (req, res) {})
Enter fullscreen mode Exit fullscreen mode

Within this function you want Stripe to authenticate the request and return the event:

app.post("/webhook", async function (req, res) {
  // Check Stripe signature
  const sig = req.headers['stripe-signature']
  let event
  try {
    event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret)
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`)
  }
})
Enter fullscreen mode Exit fullscreen mode

Next, you want to handle the event. Modify the POST function as follows:

app.post("/webhook", async function (req, res) {
  // Check Stripe signature
  const sig = req.headers['stripe-signature']
  let event
  try {
    event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret)
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`)
  }

  switch (event.type) {
    case 'checkout.session.completed':
      console.log(`Payment checkout session for ${req.body.data.object.client_reference_id} was successful!`)

      break
    default:
      // Unexpected event type
      return res.status(400).end()
  }
})
Enter fullscreen mode Exit fullscreen mode

You are checking for the checkout.session.completed event and you are accessing the unique client_reference_id you sent to Stripe when creating the original Stripe session via your stripeCheckout lambda function in Part 1. Having access to this allows you to match up payments to unique customer checkouts.

To complete the stripeHook function, Stripe requires that you acknowledge receipt of the event. Add res.json({ received: true }) to the end of the function.

Your POST function should look like:

app.post("/webhook", async function (req, res) {
  // Check Stripe signature
  const sig = req.headers['stripe-signature']
  let event
  try {
    event = stripe.webhooks.constructEvent(req.rawBody, sig, endpointSecret)
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`)
  }

  switch (event.type) {
    case 'checkout.session.completed':
      console.log(`Payment checkout session for ${req.body.data.object.client_reference_id} was successful!`)

      break
    default:
      // Unexpected event type
      return res.status(400).end()
  }

  // Return a response to acknowledge receipt of the event
  res.json({ received: true })
})
Enter fullscreen mode Exit fullscreen mode

Step 5. Publish your updates to your AWS account

From the terminal, run:

amplify push
Enter fullscreen mode Exit fullscreen mode

It will show you the following table:
Amplify push table
and it will ask:

Are you sure you want to continue? (Y/n)
Enter fullscreen mode Exit fullscreen mode

Enter y.

The amplify push command takes a few minutes to complete.

Step 6. Add the webhook to Stripe

In order for Stripe to send out the event you need to add the URL the webhook must call within the Stripe dashboard.

  1. To find the URL of the stripeHook lambda you created, log into your AWS Console and navigate to the API Gateway page. From here, select the stripeAPI. From the left-hand navigation panel, select Stages. Finally, click on dev > /webhook > POST. The Invoke URL will be shown in a blue panel at the top of the main area. Copy the URL. It will look something like https://4ssahjnp1d3.execute-api.eu-west-2.amazonaws.com/dev/webhook
  2. Log in to your Stripe account, click on the Developers link and navigate to Webhooks.
  3. Click on + Add endpoint and paste in your endpoint URL.
  4. Within Events to send search for and select the checkout.session.completed event. Click Add endpoint.
  5. Reveal, copy and save the signing secret which you will add as an AWS Lambda environment variable in the next step.

Step 7. Add your Stripe secret key and endpoint secret as environment variables to your Lambda

  1. Remain in your Stripe account and navigate to your API keys. Reveal and copy the secret key token, it will look something like sk_test_LE4KxozC6O8d3krb3FEjbBp00erufO2Bm.

  2. Log in to your AWS console and navigate to your Lambda page.

  3. Find your stripeHook-dev function on the Lambda > Functions list (If you can't find it, check you are in the correct region within the AWS console)

  4. On the stripeHook function page within the AWS console, scroll down to the Environment Variables area and click Edit.

  5. Add STRIPE_SECRET_KEY and STRIPE_ENDPOINT_SECRET variables with the values copied from the Stripe dashboard earlier. Click Save.

AWS Lambda environment variables

You've completed setting up the new Stripe Checkout in Gatsby using AWS Amplify.

Extra info.

When testing the Stripe Checkout use the card number 4242 4242 4242 4242 with an expiry date in the future and any 3 digits for the CVC.

To see that the webhook has been sent after payment, log into Stripe, navigate to your webhook and scroll down to Webhook attempts. You can resend webhooks from here too.

To observe that the Lambda function fired on receipt of the webhook, log into AWS Console and navigate to the stripeHook lambda function. Click on the monitoring tab and click View logs on Cloudwatch. Scroll down to Log events and you'll find the console.log we wrote earlier that logged Payment checkout session for UniqueString was successful!.

To delete the resources pushed to AWS, run amplify delete from terminal.

💖 💪 🙅 🚩
beezfedia
Beez Fedia

Posted on June 19, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related