Accepting Apple Pay and Google on the web with Stripe and Firebase

Accepting Apple Pay and Google on the web with Stripe and Firebase

Stripe and Firebase both offer some guidance in implementing Apple / Google Pay with Stripe and Firebase. I found those tutorials to jump a few steps at times and that makes them harder to follow.

This tutorial should serve as an easy to follow guide to implement the most basic flow of accepting payments using Stripe and Firebase Functions.

💡 Prerequisites

Firebase project and Stripe account.

insert GIF of end result

Before you begin

Let’s make sure you have everything you need to get through this tutorial: [] Firebase Project on premium plan to be able to deploy Functions
[] Stripe API keys (found in Stripe Dashboard under Developer -> API Keys)
[] Download the starter project to follow along

🗺 overview

insert Stripe flow image


In our case the Server is going to be the Firebase back-end. So to accept the a Stripe payment using Apple Pay or Google Pay our flow will be:

  1. Customer taps ‘Add to Cart’ button, which indicates the intention to puchase.
  2. We’ll create a PaymentIntent both on the client and on the server. On the client we create it in order to show the pay with Apple/Google button and on the server we create it in order to communicate with Stripe and get the ‘client_secret’.
  3. Next we present the user with the checkout section.
  4. When the user taps pay with Apple button the browser is going to handle the user interaction and retun a ‘payment method’ to us
  5. Now that we have the ‘client_secret’ from Stripe and the ‘payment method’ from the user we can send both to Stripe to process the payment.
  6. The only thing left to do is to listen for the result of that transaction processing and handle either the success or error cases.

☁️ server-side setup

As you can see from the diagram above we only need the server to take an order information and then call Stripe to create a PaymentIntent from which we’ll send back to the client just the ‘client_secret’ value.

If you are unfamiliar with Firebase Functions, check out this short tutorial on writing your first Firebase Function.

Note: For simplicity we’ll create our Firebase Functions project within the current folder. In production you should have different folder for each as they are different projects (i.e. Client and Server).

To setup the Firebase Functions project, go ahead and run the following commands in your terminal, inside the project folder

firebase init functions
cd functions
npm install stripe

To finish up the Firebase Functions setup you’ll also need to set up your Stripe API secret key as an environment variable. To do so, get your secret key from Stripe → Dashboard → Developers → API Keys and then run the following in terminal:

firebase functions:config:set stripe.secret="YOUR_SECRET_KEY"

You need to do this in order to keep your secret key secret. Otherwise you would have to keep it in a source code file which is more exposed.

Once we have that we can write our Firebase function that we can call and that will respond with the client_secret attribute from the PaymentIntent object.

// functions/src/index.ts

import * as functions from 'firebase-functions';
import Stripe from 'stripe';

const stripeSecret = functions.config().stripe.secret // 1.
const stripe = new Stripe( stripeSecret, {
    apiVersion: '2020-08-27'

export const getClientSecret = functions.https.onRequest( async (req, res) => {
    const amount = 500; // 2.
    const paymentIntent = await stripe.paymentIntents.create({ // 3.
        currency: 'usd',
        amount: amount,
        metadata: { integration_check: 'accept_a_payment' }

    res.setHeader('Access-Control-Allow-Origin', '*'); // 4.
    res.setHeader("Vary", "Origin");
    res.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET");

    res.status(200).json({client_secret: paymentIntent.client_secret}); // 5.

Let’s look over what the above code is doing:

  1. Reads the Stripe secret API key we previously defined as an environment variable.
  2. Set the amount you want to charge. It’s important to remember to always set this vale on the server side, because any data coming from the client might be changed by a bad actor.
  3. Makes an asynchronous call to stripe to create the PaymentIntent object
  4. It’s best practice to define the headers for your Response object. In production you might be a bit more restrictive, but in our case we’re going for simplicity.
  5. Sends a JSON response containing the client_secret attribute of the recently created PaymentIntent

At this point you could also test your function locally before deploying with:

npm run deploy

When the operation completes you’ll have a live ‘back-end’ endpoint that can communicate with Stripe, create a PaymentIntent and send back a JSON response containing the client_secret attribute. Awesome!

testing your serverless back-end

⚠️ Even in testing you need to use HTTPS on the client, even when testing locally. An easy way to do that is with the help of a service like ngrok.

Now that we have our serverless back-end up and running let’s go ahead and test it from our front-end.

Add the following script in your index.html after the closing body tag.

<!-- index.html -->
  (async () => {
    // Go to -> Your Project -> Functions -> Dashboard
    const endpointURL = "YOUR_ENDPOINT_URL";
    const serverResponse = await fetch(endpointURL);
    const { client_secret: clientSecret } = serverResponse.json()

    console.log(`👀 client secret is: ${clientSecret}`);

Ready to get started?
Contact minima today and become more data driven in your business.

  • firebase functions
  • stripe
  • web