Stripe
The Stripe integration is designed to be as lightweight as possible, accommodating one-off payment and subscriptions.
One-off payment
Ideal if your product involves one-time payments or is credit-based.
Activation
Steps to activate:
- Sign up with Stripe.
- Create a Product.
- Set up a webhook listener.
- For the URL, use your API’s address followed by
/v1/billing/stripe/webhook/raw
. - Choose
checkout.session.completed
as the event to monitor. - Head to the developers section.
- Uncover the secret key.
- Add your environment variables to the apps/server/.env file:
SERVER_PAYMENT_STRIPE_SECRET_KEY=
SERVER_PAYMENT_STRIPE_WEBHOOK_SECRET=
Payment flow
Assuming you have a page on your front-end to purchase your product:
1). Pull the product listings from Stripe.
You can either fetch all your products directly from Stripe, or predefine/store a specific product ID in your environment variable.
const [products, setProducts] = useState([])
const fetchProducts = async () => {
const response = await Api.Billing.findManyProducts()
setProducts(response)
}
2). Create a payment URL for the customer to complete their purchase:
const handlePayment = async () => {
const paymentLink = await Api.Billing.createPaymentLink(
products[0].id,
order.id,
)
window.open(paymentLink, undefined, 'noopener,noreferrer')
}
return (
<div>
<Button onClick={handlePayment}>Buy</Button>
</div>
)
3). Receive notifications via your server-side webhook when a payment succeeds.
/apps/server/src/modules/billing/application/billing.controller.ts
@Authentication.Public()
@Post('/stripe/webhook/raw')
async handleStripeWebhook(@Req() request: Request) {
const body = RequestHelper.getRawBody(request)
const sig = request.headers['stripe-signature'] as string
const data = await this.paymentService.onPayment(body, sig)
// data: {
// userId: string
// stripeCustomerId: string
// metadata: Record<string, string>
// customerDetails?: StripeSDK.Checkout.Session.CustomerDetails
// }
// send a confirmation email or add credits to the user
if (!data) {
return
}
}
Subscription
Activation
- Sign up with Stripe.
- Create a Product (recurring).
- Set up a webhook listener.
- For the URL, use your API’s address followed by
/v1/billing/stripe/webhook/raw
. - Choose
checkout.session.completed
as the event to monitor. - Go to the developers section.
- Uncover the secret key.
- Add your environment variables to the apps/server/.env file:
SERVER_PAYMENT_STRIPE_SECRET_KEY=
SERVER_PAYMENT_STRIPE_WEBHOOK_SECRET=
Subscription flow
Assuming you have a page on your front-end for subscriptions:
1). Fetch the products from Stripe.
You can either fetch all your products directly from Stripe, or predefine/store a specific product ID in your environment variable.
const [products, setProducts] = useState([])
const fetchProducts = async () => {
the response = await Api.Billing.findManyProducts()
setProducts(response)
}
2). Create a payment URL for the customer to subscribe to your plan:
const handleSubscription = async () => {
const paymentLink = await Api.Billing.createPaymentLink(
products[0].id,
order.id,
)
window.open(paymentLink, undefined, 'noopener,noreferrer')
}
return (
<div>
<Button onClick={handleSubscription}>Subscribe</Button>
</div>
)
When a user subscribes, it will trigger a webhook to your server /apps/server/src/modules/billing/application/billing.controller.ts
and will assign the stripeCustomerId to your user table.
3). Check at any point if a user has an active subscription:
const [activeSubscription, setActiveSubscription] = useState(null)
const fetchSubscriptions = async () => {
const response = await Api.Billing.findManySubscriptions()
const subscription = response.find(
subscription => subscription.status === 'active',
)
setActiveSubscription(subscription)
}
return (
<>
{activeSubscription && (
<Card title="Active Subscription" style={{ marginBottom: 16 }}>
<Typography.Paragraph>
You have an active subscription.
</Typography.Paragraph>
<Typography.Paragraph>
Your subscription will expire on{' '}
{dayjs(activeSubscription.dateExpired).format('MMMM D, YYYY')}.
</Typography.Paragraph>
</Card>
)}
</>