This Stripe billing portal is a Next.js/Tailwind app. On the back-end, it is powered by a Postgres replica containing Stripe data (via Sync Inc).
This repo uses plain node-postgres
(pg
) for all SQL queries.
You can view the app live here:
https://stripe-billing-hio0e7ngh-syncinc.vercel.app/
The only feature that does not work is the "Update" link for Payment Method.
To use for yourself:
1. Copy .env.example
→ .env.local
Run the following:
cp .env.example .env.local
You'll see in .env.local
that there are two env variables you need to set:
DATABASE_URL=
STRIPE_KEY=
For STRIPE_KEY
, grab a key from your Stripe dashboard.
For DATABASE_URL
, use the Postgres connection URL provided by Sync Inc in the following step.
2. Setup your Sync Inc database
With Sync Inc, you can provision a real-time Postgres replica in AWS with all your Stripe data.
Sign-up for an account here (free to start).
At the end of the on-boarding process, Sync Inc displays your PG connection URL. Copy and paste this URL as your DATABASE_URL
:
That's it! Now, on /
, you'll see a list of customers. Click on one to see their billing portal:
There is no login or authentication scheme for the API endpoints in this example. You must be sure to wrap the API in authentication before pushing to production.
Data
The React front-end uses Next's /api
features to query a Postgres database containing Stripe data. There are three API endpoints:
GET /customers
: For populating the list of customers on/
GET /customer/[id]
: Hydrates the billing portal with the customer details, subscription data, receipts, etc.POST /customer/billingPortal
: If a customer wants to change their payment method, this generates a link to Stripe's billing portal to redirect them to
To get a sense of how it all works: in ./pages/api/customer/[id].ts
, you'll see an API handler function that first grabs the id from the path:
let { id } = req.query;
Then composes a response object with a number of SQL queries:
let customer = await getOne(`select * from customer where id = $1`, [id]);
let subscription = await getOne(
`select * from subscription where customer_id = $1`,
[id]
);
Note: to optimize performance/round-trips to the database, these queries can be combined into one super query.
Styles
Styles are all done with Tailwind.