Payment Processing and Subscriptions with Stripe
Key Features
Payment Processing
With Stripe integrated into ShipFast, payment processing is secure and reliable. The boilerplate offers two options for integrating Stripe:
1. Stripe Checkout Page
A secure, pre-built, hosted payment page provided by Stripe. This option simplifies the payment process by handling PCI compliance and security, so you don’t have to worry about building a payment page from scratch.
-
In ShipFast, this integration is already set up and ready to use as long as you set
useStripeCheckoutPage
totrue
in the/src/config/features
file. -
The Stripe Checkout URL generation is already configured in the Pricing section of the boilerplate, so you do not need to write custom code for this part. However, if there are any specific requirements or customizations, you can easily update this functionality by modifying the existing code.
Here's an example of how it's set up:
import { initiateStripeCheckout } from "@/services/stripe";
const handleCheckout = async () => {
try {
// Initiating Stripe checkout with the necessary parameters
const { url } = await initiateStripeCheckout({
priceId: "your-price-id", // Replace with your Stripe price ID
credits: 100, // Replace with the number of credits
email: "user@example.com", // Replace with the user's email
organizationId: "your-org-id", // Replace with the user's organization ID
userId: "your-user-id", // Replace with the user's ID
source: "checkout-page", // Identifying the source as "checkout-page"
freeCredits: 10 // Replace with the number of free credits if applicable
});
// Redirect the user to the Stripe Checkout Page
window.location.href = url;
} catch (error) {
console.error("Error initiating Stripe checkout:", error);
}
};
File Path: src/common/sections/PricingSection/index.js
How It Works:
The initiateStripeCheckout
function is responsible for calling the backend /checkout-sessions
API route. This API route handles the creation of a Stripe Checkout session based on the provided parameters, such as the price ID, credits, user details, and organization information.
When the initiateStripeCheckout
function is invoked:
- It sends a POST request to the
/checkout-sessions
API route with all the necessary data. - The API route interacts with Stripe’s API to create a checkout session, returning the session URL.
- The user is then redirected to the Stripe Checkout Page where they can complete their payment.
You can customize this functionality by adjusting the parameters or modifying the API route if you have specific requirements.
/checkout-sessions
API Route
The /checkout-sessions
API route is essential for initiating a Stripe Checkout session. It handles the backend logic of creating a session with Stripe based on the parameters provided during the initiateStripeCheckout
function call.
How it works:
- When the
initiateStripeCheckout
function is triggered, it sends a request to this API route to create a new Stripe Checkout session. - This route interacts with Stripe's API to create the session, passing along important details such as the price ID, credits, email, user information, and more.
- Once the session is successfully created, the route returns the URL to the Stripe Checkout Page where the user will be redirected.
Parameters of initiateStripeCheckout
The initiateStripeCheckout
function requires the following parameters to initiate the Stripe Checkout process:
-
priceId
:
The Stripe price ID for the product or service the user is purchasing. This is required to associate the checkout session with a specific pricing tier. -
credits
:
The number of credits the user is purchasing. This can be used to track or allocate credits based on the payment. -
email
:
The user's email address. This will be associated with the Stripe Checkout session and is important for sending receipts or notifications. -
organizationId
:
The ID of the user's organization. This helps to associate the payment with the specific organization in the system. -
userId
:
The ID of the user making the purchase. This is used to associate the transaction with the correct user in the system. -
freeCredits
:
The number of free credits being offered to the user. This could be used as part of a promotion or discount for the user during the checkout process. -
source
(optional):
A string to identify the source of the checkout. In most cases, this would be set to"checkout-page"
to indicate that the checkout is being initiated from a page on your website. This parameter is optional and can be omitted if not needed.
2. Custom Flow with ShipFast
ShipFast already provides a subscription flow setup for Stripe, ready to be used with minimal configuration. However, to disable the default Stripe checkout page, you need to ensure that useStripeCheckoutPage
is set to false
in the /src/config/features
file. Once this is configured, the boilerplate facilitates a smooth subscription experience tailored to your needs.
The custom flow offers several advantages over the default Stripe checkout page:
- Customizability: You have full control over the user interface, allowing you to design the subscription process to match your application's branding and user experience.
- Flexibility: The custom flow enables you to integrate additional business logic, such as discounts, free trials, or custom metadata that might not be possible with the default Stripe checkout.
- Seamless User Experience: Users are kept within your app's interface, preventing them from being redirected to an external page, which can enhance engagement and reduce drop-off rates.
- Complete Control: You maintain control over the entire flow, from payment method setup to subscription management, making it easier to implement custom features like subscription upgrades, downgrades, and cancellations.
The flow can be divided into two main scenarios: subscribing to a paid plan for the first time and switching between plans. Each scenario is designed to ensure the user is seamlessly guided through the subscription process while giving you the flexibility to integrate custom business logic.
Scenario 1: Subscribing to a Paid Plan for the First Time (No Payment Method Yet)
In this case, the user has not provided any payment method yet. To begin the subscription process, the following functions are used:
1. createStripeSetupIntent
:
- Purpose: This function handles the setup of the user's payment method by creating a
SetupIntent
using Stripe’s API. It ensures that the user's payment details are securely collected, validated, and stored for future use (e.g., subscriptions or one-time payments). - Flow: When the user is ready to subscribe for the first time or set up a recurring payment, the frontend calls the
createStripeSetupIntent
function. This function sends a request to the backend, where aSetupIntent
is created, and aclientSecret
is returned. TheclientSecret
is then used to confirm and securely save the payment details.
How createStripeSetupIntent
Works:
- Frontend Request: When the user is ready to submit their payment details,
createStripeSetupIntent
is invoked. - Backend Route: The backend receives the request, creates a
SetupIntent
via Stripe’s API, and returns theclientSecret
to the frontend. - Confirmation: On the frontend, the
clientSecret
is used to confirm the payment method and securely save it for future transactions.
Here’s an updated example of how the function is set up:
import { createStripeSetupIntent } from "@/services/stripe";
const initializeSetupIntent = async () => {
try {
// Send the request to the backend to create the SetupIntent
const { clientSecret } = await createStripeSetupIntent({ email });
// Save the clientSecret for later use to confirm the payment method
setClientSecret(clientSecret);
} catch (error) {
// Handle errors
console.error("Failed to setup intent:", error);
showNotification(error?.message || "Something went wrong.", "error");
} finally {
setIsLoading(false); // Stop loading state
}
};
File Path: src/common/forms/SubscriptionModalForm/index.js
/setup-intents
API Route
The /setup-intents
API route plays a key role in securely interacting with Stripe’s API. It is the route being called by the createStripeSetupIntent
function to create a SetupIntent
and return the associated clientSecret
to the frontend.
Purpose of /setup-intents
Route
- Creating the SetupIntent: The server-side logic calls the Stripe API to create a
SetupIntent
, which prepares Stripe to securely collect payment details (e.g., credit card information). - Returning the
clientSecret
: TheclientSecret
is sent back to the frontend, which will use it to confirm and securely save the user's payment method for future transactions.
How the /setup-intents
Route Works:
- Request to Create SetupIntent: The frontend sends a request to the
/setup-intents
route (typically viaaxios
orfetch
), passing any necessary details (e.g., user email). - Stripe API Interaction: The backend uses Stripe’s API to create a
SetupIntent
. This step generates aclientSecret
that is necessary for confirming the payment method. - Send
clientSecret
Back: After theSetupIntent
is successfully created, the backend returns theclientSecret
to the frontend. - Frontend Confirmation: The frontend uses this
clientSecret
to confirm and save the user’s payment method securely.
5. createOrUpdateStripeSubscription
:
- Once the payment method is set up, this function is used to create or update the user's subscription based on their selected plan.
- Purpose: This function links the user’s payment method to their chosen plan and finalizes the subscription in Stripe.
- Flow: When the user subscribes to a plan, createOrUpdateStripeSubscription is called with the necessary information (email, organizationId, priceId, paymentMethodId). The function ensures the subscription is created or updated in Stripe.
How createOrUpdateStripeSubscription
Works:
- Frontend Request: Once the payment method is confirmed (via the
SetupIntent
), the frontend sends a request to the/subscriptions
route to create or update the subscription with the necessary details. - Backend Route: The backend processes the request and interacts with Stripe's API to either create or update the subscription.
- Confirmation: After the subscription is successfully created or updated, the backend returns a success response, confirming that the user is now subscribed to their chosen plan.
Here’s an example demonstrating how the function is used in the subscription flow:
First, confirm the SetupIntent, which securely collects the user's card details:
const cardElement = elements.getElement(CardElement);
const { error, setupIntent } = await stripe.confirmCardSetup(clientSecret, {
payment_method: {
card: cardElement
}
});
File Path: src/common/forms/SubscriptionModalForm/index.js
After confirming the card setup and receiving the payment_method from the setupIntent, use it to create or update the subscription:
import { createOrUpdateStripeSubscription } from "@/services/stripe";
const paymentMethodId = setupIntent.payment_method;
await createOrUpdateStripeSubscription(
email,
organizationId,
priceId,
paymentMethodId,
{
credits: 100,
freeCredits,
action: "subscribed"
}
);
File Path: src/common/forms/SubscriptionModalForm/index.js
/subscriptions
API Route
The /subscriptions
API route is crucial for creating or updating the user's subscription in Stripe. This is the API route being called within the createOrUpdateStripeSubscription
function to ensure that the user is subscribed to the correct plan with the provided payment method.
Purpose of /subscriptions
Route:
- Creating or Updating Subscription: The backend logic calls Stripe’s API to create or update the subscription, based on the user’s selected plan and payment method.
- Subscription Confirmation: Once the subscription is successfully created or updated, the backend confirms the action with a success response.
How the /subscriptions
Route Works:
- Request to Create or Update Subscription: The frontend sends a request to the
/subscriptions
route with details such asemail
,organizationId
,priceId
, andpaymentMethodId
. - Stripe API Interaction: The backend uses Stripe's API to create or update the subscription, linking the user's payment method with their chosen plan.
- Subscription Confirmation: After the subscription is successfully created or updated, the backend sends a confirmation response to the frontend, signaling that the user is now subscribed to the correct plan.
Scenario 2: Switching Plans (Upgrading or Downgrading an Existing Subscription)
In this scenario, the user already has an active subscription and payment method. The user is either upgrading or downgrading their plan. The following functions are used to modify their subscription:
upgradeStripeSubscription
anddowngradeStripeSubscription
:
-
Purpose: These functions ensure that the user’s plan is updated in Stripe, either increasing or decreasing the subscription's benefits.
-
Flow: Depending on whether the user is upgrading or downgrading, either upgradeStripeSubscription or downgradeStripeSubscription is called with the necessary data, ensuring the subscription is adjusted accordingly.
Here’s an example demonstrating how the function is used in the subscription flow:
import { upgradeStripeSubscription, downgradeStripeSubscription } from "@/services/stripe"; const handleChangePlan = () => { if (isUpgrade) { await upgradeStripeSubscription( subscriptionId, priceId, organizationId, email, paymentMethodId, metadata ); } else { await downgradeStripeSubscription( subscriptionId, priceId, organizationId, email, paymentMethodId, metadata ); } }
File Path:
src/common/forms/SubscriptionModalForm/index.js
/subscriptions/{subscriptionId}/upgrade
and/subscriptions/{subscriptionId}/downgrade
API RoutesThese routes are used by the
upgradeStripeSubscription
anddowngradeStripeSubscription
functions to interact with Stripe's API for upgrading or downgrading a subscription.Purpose of
/subscriptions/{subscriptionId}/upgrade
and/subscriptions/{subscriptionId}/downgrade
Routes:- Upgrading/Downgrading the Subscription: These routes handle the logic of adjusting the user's subscription in Stripe. The
upgrade
route increases the subscription's benefits, while thedowngrade
route reduces them. - Updating the Subscription in Stripe: The backend uses Stripe’s API to modify the subscription with the new plan details, ensuring the user’s subscription reflects their selected tier.
How the
/subscriptions/{subscriptionId}/upgrade
and/subscriptions/{subscriptionId}/downgrade
Routes Work:-
Request to Upgrade/Downgrade Subscription: The frontend calls either the
/subscriptions/{subscriptionId}/upgrade
or/subscriptions/{subscriptionId}/downgrade
route, passing the necessary details such assubscriptionId
,priceId
,organizationId
,email
,paymentMethodId
, andmetadata
. -
Stripe API Interaction:
-
For Upgrades: The backend uses Stripe’s API to immediately upgrade the subscription and apply the new plan's benefits.
-
For Downgrades: When downgrading, the backend creates or updates a Stripe subscription schedule. This ensures that the downgrade doesn't take effect immediately but instead at the end of the current billing cycle. The subscription continues with the current plan until the next renewal, at which point the downgrade takes effect.
- Confirmation of Subscription Change: Once the subscription is successfully updated, the backend confirms the change. The user will see the new plan at the next billing cycle if it is a downgrade.
- Upgrading/Downgrading the Subscription: These routes handle the logic of adjusting the user's subscription in Stripe. The
Switching Between Stripe Checkout and Custom Flow
ShipFast allows you to easily switch between two distinct payment flows: Stripe Checkout and Custom Flow. Both offer unique advantages, but understanding how to seamlessly switch between them is essential for adapting to different business needs. Below, we'll explain the steps required to switch between the two flows in your application.
Switching to Stripe Checkout Flow
The Stripe Checkout Flow is a pre-built, hosted solution by Stripe, designed to simplify the payment experience. Switching to this flow is useful when you want a quick and secure payment method without needing to handle custom UI or complex subscription management.
Steps to Switch to Stripe Checkout:
-
Update the Configuration:
- Open the
/src/config/features
file and setuseStripeCheckoutPage
totrue
. This tells the application to use Stripe's hosted checkout page instead of the custom flow.
// /src/config/features { useStripeCheckoutPage: true; }
- Open the
Switching to Custom Flow
The Custom Flow gives you complete control over the payment experience, allowing you to build a tailored subscription process. If you're looking for more flexibility and customization options, switching to this flow might be the best solution.
Steps to Switch to Custom Flow:
-
Update the Configuration:
- Open the
/src/config/features
file and setuseStripeCheckoutPage
tofalse
. This will disable Stripe Checkout and activate the custom payment flow instead.
// /src/config/features { useStripeCheckoutPage: false; }
- Open the
Summary of Differences
Feature | Stripe Checkout Flow | Custom Flow |
---|---|---|
Customization | Limited (uses Stripe’s hosted page) | Full control over UI and logic |
User Experience | Redirects to external page | Stays within your app |
Security & Compliance | Handled by Stripe (PCI compliant) | Handled by you, using Stripe’s APIs |
Payment Method Setup | Stripe handles it | You handle it via SetupIntent |
Subscription Management | Stripe handles subscription creation | You handle subscription logic |
Business Logic | Limited to what Stripe offers | Fully customizable |
When to Use Each Flow
- Stripe Checkout Flow: Use this flow if you want a quick, secure, and hassle-free integration with Stripe. It’s ideal for simpler subscription models where you don’t need much customization.
- Custom Flow: Use this flow if you need more control over the user experience, want to handle complex business logic, or need a fully customized payment process.
By understanding the key differences between these two flows, you can easily choose the one that best suits your application’s needs and switch between them as necessary.