Using the new Stripe Checkout in Gatsby + AWS Amplify: Payment confirmation (webhook)
Beez Fedia
Posted on June 19, 2020
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 api update
The Amplify CLI will walk you through a series of questions:
Please select from one of the below mentioned services:
You want to update your REST
API.
Please select the REST API you would want to update
Select stripeAPI
.
What would you like to do
You want to add a new path
to the stripeAPI.
Provide a path (e.g., /book/{isbn}):
Enter /webhook
.
Choose a Lambda source
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 stripeHook
.
Provide the AWS Lambda function name:
Press Enter to select the default option of stripeHook
.
Choose the function runtime that you want to use:
Select NodeJS
Choose the function template that you want to use:
Select Serverless ExpressJS function (Integration with API Gateway)
Do you want to access other resources created in this project from your Lambda function?
Enter n
.
Do you want to invoke this function on a recurring schedule?
Enter n
.
Do you want to edit the local lambda function now?
Enter n
.
Restrict API access
Enter n
.
Do you want to add another path?
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.
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
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
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 stringify
s 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())
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()
},
})
)
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) {})
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}`)
}
})
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()
}
})
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 })
})
Step 5. Publish your updates to your AWS account
From the terminal, run:
amplify push
It will show you the following table:
and it will ask:
Are you sure you want to continue? (Y/n)
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.
- 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 thestripeAPI
. From the left-hand navigation panel, selectStages
. Finally, click ondev
>/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 likehttps://4ssahjnp1d3.execute-api.eu-west-2.amazonaws.com/dev/webhook
- Log in to your Stripe account, click on the
Developers
link and navigate to Webhooks. - Click on
+ Add endpoint
and paste in your endpoint URL. - Within
Events to send
search for and select thecheckout.session.completed
event. ClickAdd endpoint
. - 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
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
.Log in to your AWS console and navigate to your Lambda page.
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)On the stripeHook function page within the AWS console, scroll down to the Environment Variables area and click
Edit
.Add
STRIPE_SECRET_KEY
andSTRIPE_ENDPOINT_SECRET
variables with the values copied from the Stripe dashboard earlier. ClickSave
.
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.
Posted on June 19, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.