💸 Pay Me: Quickstart for creating a SaaS pt.2 — Stripe Payments

Creating a SaaS solution is fun, but creating a payment system can be a minefield. In this article, I demonstrate how to create a serverless SaaS on AWS. It uses the Stripe API and Vue-Checkout library to perform payments and manages users with Cognito, building on the architecture from part 1. Check out the live demo 💽.

Image for post
Image for post

Payments systems are a complex but necessary part of running a SaaS. Turning to a payment service side-steps most of the complexity and responsibility, but usually costs a percentage or flat fee for each transaction. Stripe offers payment system solution and charges 1.4%+20p for European cards and 2.9%+20p for for non-European cards per transaction. Stripe also has other useful features such as:

  • customer management, including storing payment details
  • manage products and prices
  • create payments and subscriptions
  • dashboards and insights

In this article, I will demonstrate integrating Stripe’s payment solution and how to store user details, make subscriptions and instant payments. In a previous article, I demonstrated how to create a SaaS user management application which allows users to manage their API key and monitor usage.

I want to limit the number of requests a user can make per month and scale that with an amount of money that would make the SaaS profitable. I have chosen to use credits instead of number of requests to open up the possibility of having multiple services which might be more costly to run. This also allows you to charge multiple credits.

I have divided plans up into three tiers which can be subscribed to or as pay-as-you-go:

  • Free — 50 Credits per week
  • Basic —1,000 Credits per month
  • Power — 100,000 Credits per month

In a future article, I hope to investigate the cost of the architecture per user to determine a fair price for the service.

Stripe offers both the ability to make instant payments and subscriptions. Your products and prices must be stored on the Stripe service and when a purchase is successfully completed, Stripe will notify you using a web hook that you define. This web hook is called on subscription payment being successful too.

The architecture extends upon the previous User Manager App with another DynamoDB table to store products, a checkout function on the User API, a Stripe web hook API, and a State machine to manage users’ credit.

Image for post
Image for post
SaaS Architecture Diagram

The full code for this project can be found here ☁️

The live demo can be found here 💽

Image for post
Image for post
Video of User buying subscription with SaaS App

Let’s Build! 🔩

Backend

Credit

User credit is managed using a State Machine. The Cognito post-confirmation hook trigger a Lambda to create a user and begin an execution of the State Machine with the Free plan.

Image for post
Image for post
User Management State Machine

State Machines offer the ability to wait a specific amount of time and this does not cost. This allows us to periodically add credits to users on the Free plan. This is a requirement as this plan is not purchased and therefore cannot use the Stripe web hook as a trigger to add credits (see later).

Using a State Machines to construct flows means that extensions are easily added, such as sending email alerts. The cost of a standard State Machine is $0.025 per thousand state transitions. In the above State Machine, a free user would cost $0.025/1000 * 3 = $0.000075 per User per week — not including the Lambda fees. There is also a limit of 25,000 executions at a time.

Product Table

Stripe allows you to create products which can have multiple prices. This is particularly useful for SaaS where you may offer the same product as an instant buy or recurring subscription.

I have defined the products in a JSON file and attempted to sync this details with both Stripe and a DynamoDB table using the following script:

User API

The previous User Management system from part 1 has:

/generate-key — this regenerates the User’s API key

/user — returns details like API key, last Update time, and subscription Plan

This is extended with:

/create-session

/cancel-subscriptions

Create Session

The Stripe-checkout frontend component requires a session-Id to initiate the payment sequence.

When the user selects a product on the frontend, a request is sent to the /create-session endpoint where following function is run:

When creating a session, you can also attach meta-data. This data is accessible on the resultant success object of the payment sent by Stripe to your web hook.

Cancel Subscriptions

Users should be able to cancel their subscription to the SaaS at any time. In this example, selecting the Free plan in the frontend cancels all other subscriptions. This is achieved simply by referencing a customer id on a cancel call to Stipe.

WebHook API

Stripe sends transaction events to a web hook which you must create and define using the Stripe API:

Once you have defined the web hook, the secret is used in the following Lambda function which processes the successful charge events:

Again, I have added open CORS whilst developing. For security, these should been locked down so that only Stripe can call this API.

Test API

The test service API still uses the user’s API key for a query on a GSI of the User table to get the username and credit count. The credit field is decremented every time a request is made to the service.

Frontend

The frontend is created using Vue JS with the AWS Amplify package and components — this is described in the pevious User Manager App from part 1. Here, we add the ability to make payments.

Stripe Checkout

Stripe Checkout is a payment page hosted by Stripe that users are redirected to in order to complete their purchase. Vue-Stripe-Checkout is a plugin that means Stripe Checkout can be added in just a few lines:

<stripe-checkout
ref="sessionRef"
:pk="publishableKey"
:session-id="sessionId"
successUrl="http://localhost:8080#success"
cancelUrl="http://localhost:8080#cancel"
>
<template slot="checkout-button">
<v-btn @click="$refs.sessionRef.redirectToCheckout()" v- show="!isLoadingSession">Pay</v-btn>
</template>
</stripe-checkout>

The pricing page for this site looks like this:

Image for post
Image for post
SaaS Frontend Vue Checkout Component

When the user clicks pay they are redirected to the Stripe page which looks like this:

Image for post
Image for post
Stripe Checkout Page for Power Subscription

After Thoughts

This Architecture is a basis for creating your own SaaS. It would be great to extend this SaaS to cover:

  • processing unsuccessful subscription payments
  • connecting to social identity providers

Thanks for reading

I hope you have enjoyed this article. If you like the style, check out T3chFlicks.org for more tech-focused educational content (YouTube, Instagram, Facebook, Twitter).

The full code for this project can be found here ☁️

The live demo can be found here ⚡

Resources:

Written by

A tech focused education and services company. Find us at https://t3chflicks.org.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store