Managing payments in your app: Check out the checkout — the code

Jen Person
Google Cloud - Community
6 min readAug 5, 2020

See how to create a Stripe Checkout intent in Cloud Run and pass the Session ID to the client.

Time to check out! Source

Are you new to this series? Check out the first blog for an introduction and a table of contents!

Get caught up

With our inventory for American Sock Market up and running, it’s time to implement the server-side components of Stripe Checkout by creating a payment intent.

If you’re interested in the rationale behind this code, then check out this blog!

Assuming you already have read about why I’m including this code, I’m going to focus on the execution. To briefly summarize, the Stripe Checkout API enables you to implement purchasing functionality in a way that is secure and compliant with current regulations. This process involves creating an intent to purchase in a server environment, and passing that intent to the client to complete the purchase.

This post details a way to implement the server-side component of Checkout.

Set up

With that in mind, let’s explore the checkout function. Navigate to the part-2-stripe-checkout folder and take a look at the contents.

  • checkout.js
  • package.json
  • Dockerfile
  • README.md

So this tells you off the bat that this is also a Node.js app, thanks to the package. json. But, there’s an additional file here: the Dockerfile. This is required when packaging an app in a container and deploying it to Cloud Run.

The code

Source

Before deploying to Cloud Run, let’s take a look at the code in checkout.js.

First, there are some imports and initializers.

const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const admin = require('firebase-admin');
const express = require('express');
const app = express();
admin.initializeApp();
const db = admin.firestore()
const DOMAIN = process.env.DOMAIN;

Notice that there are a couple of environment variables here. You can declare values locally like we did when adding inventory items to Cloud Firestore and Stripe for local testing purposes, but once the code is deployed to Cloud Run, you will give them values in that environment as well. There are a couple ways to do this, which I’ll explain later in the post.

Then, there is the endpoint definition. This function uses the session extension, referring to the current Stripe Checkout session being created.

app.get('/session', async (req, res) => {

Next is some code addressing CORS and implementing a response for query options for requests. I’m not going to get into this code because it’s primarily about HTTP requests in general.

res.set('Access-Control-Allow-Origin', '*');if (req.method === 'OPTIONS') {// Send response to OPTIONS requestsres.set('Access-Control-Allow-Methods', 'GET');res.set('Access-Control-Allow-Headers', 'Content-Type');res.set('Access-Control-Max-Age', '3600');res.status(204).send('');

Get the name of the product, which is passed as a query parameter, and then use it to reference the product’s document in Firestore.

let product = req.query.product;
let productRef = db.collection('socks').doc(product);
productRef
.get()
.then(async doc => {
// Get inventory data from Firestore
const data = doc.data();

Once the document is retrieved, get information about the product from the document data.

Why not pass all data as query parameters?

As the heading suggests, you may be wondering why the code involves getting the product data from Firestore when it could have been passed from the client as query parameters. If the data was passed directly from the client, then users could potentially name their own price.

Create the Stripe CheckoutSession

Using the information provided by Cloud Firestore, create a CheckoutSession. You’ll notice that a success URL and cancel URL are provided using the DOMAIN environment variable. This variable will be the URL of your web application. Since we have not made that app yet, we’ll use a stand-in.

// Create a CheckoutSession on Stripe with the order information
const session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
name: data.name,
description: data.description,
amount: data.price,
currency: 'usd',
quantity: 1,
images: [data.image]
}
],
success_url: DOMAIN + '/completed.html',
cancel_url: DOMAIN + '/canceled.html',
metadata: { file: data.file }
});

Once created, pass this session in the response object.

// Send the session to the client
res.send(session);

Deploy to Cloud Run

Now that we’ve explored the code, let’s deploy! The following command does a lot under the hood. First, it uses Cloud Build to build the container image, then it stores the image in Container Registry, and finally deploys it to Cloud Run. Change SERVICE to the name you want to give the service, PROJECT-ID to your Cloud project ID, and REGION to the region in which you want to deploy (us-central1, for example).

gcloud run deploy SERVICE --image gcr.io/PROJECT-ID/checkout-session --platform managed --allow-unauthenticated --region REGION

After running this command, the terminal will display the URL to access your app. If you follow the link now, you’ll get an error, as the environment variables do not yet have values. Let’s get that fixed!

Configuring Environment Variables in Cloud Run

Nope, not that kind of environment! Source

The Cloud Run doc about environment variables showcases several ways that you can instantiate values: via the console, via gcloud, or via .yaml file. All of these methods are useful in their own ways. If you have a preference for one over the other, feel free to use it. As I’ve said before, I usually lean toward the CLI given the option. In this case, however, I prefer the .yaml. When I have a whole bunch of variables to set, this just feels easier. I can see them all and check for errors, and then quickly make changes. While we only have two environment variables here, chances are your projects are going to be much more complex with many more components and variables. It’s nice to be familiar with this system before the project grows.

gcloud run services describe SERVICE --format export > service.yaml

Now you can add the environment variables to the doc. Be sure to use your Stripe secret key, which is accessed via the Stripe console. Once your storefront web app is deployed, you will replace https://www.google.com with your actual web URL.

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: SERVICE-abc
spec:
template:
spec:
containers:
- env:
- name: STRIPE_SECRET_KEY
value: sk_test_abcd1234
- name: DOMAIN
value: https://www.google.com

One more important thing to note: the name under metadata refers to the specific configuration version. If you upload this .yaml as is, you’ll get an error because that name is already taken. Delete this name field in order to upload the file. This will assign a new randomly generated version name.

Save this file and you’re ready to upload a new configuration.

Note that this config doc will replace all of the current config, so if you have variables you set in the console and you give them a different value in the .yaml or remove them entirely, this will be reflected in the console once the config is deployed.

With that in mind, when you’re ready, run the following command:

gcloud run services replace service.yaml

Run Cloud Run

Source

With a new configuration in place, you can now run the application. Your request will be in the following format:

https://[your-run-app/session?product=[product]

For example, a request to create a checkout session for the blue sock pattern could look like this:

https://checkout-a1bcdef98.a.run.app/session?product=blue

Next Steps

You now have a serverless backend that creates a Stripe Checkout session! Next, we will create the storefront so our customers can finally buy our amazing sock patterns!

Here are some next steps to take:

--

--

Jen Person
Google Cloud - Community

Developer Relations Engineer for Google Cloud. Pun connoisseur.