Payment Processing and Subscription with Stripe

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 to true 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 a SetupIntent is created, and a clientSecret is returned. The clientSecret is then used to confirm and securely save the payment details.
How createStripeSetupIntent Works:
  1. Frontend Request: When the user is ready to submit their payment details, createStripeSetupIntent is invoked.
  2. Backend Route: The backend receives the request, creates a SetupIntent via Stripe’s API, and returns the clientSecret to the frontend.
  3. 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: The clientSecret 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:
  1. Request to Create SetupIntent: The frontend sends a request to the /setup-intents route (typically via axios or fetch), passing any necessary details (e.g., user email).
  2. Stripe API Interaction: The backend uses Stripe’s API to create a SetupIntent. This step generates a clientSecret that is necessary for confirming the payment method.
  3. Send clientSecret Back: After the SetupIntent is successfully created, the backend returns the clientSecret to the frontend.
  4. 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:
  1. 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.
  2. Backend Route: The backend processes the request and interacts with Stripe's API to either create or update the subscription.
  3. 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:
  1. Request to Create or Update Subscription: The frontend sends a request to the /subscriptions route with details such as email, organizationId, priceId, and paymentMethodId.
  2. 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.
  3. 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:

  1. upgradeStripeSubscription and downgradeStripeSubscription:
  • 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 Routes

    These routes are used by the upgradeStripeSubscription and downgradeStripeSubscription 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 the downgrade 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:
    1. Request to Upgrade/Downgrade Subscription: The frontend calls either the /subscriptions/{subscriptionId}/upgrade or /subscriptions/{subscriptionId}/downgrade route, passing the necessary details such as subscriptionId, priceId, organizationId, email, paymentMethodId, and metadata.

    2. 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.

    1. 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.

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:

  1. Update the Configuration:

    • Open the /src/config/features file and set useStripeCheckoutPage to true. This tells the application to use Stripe's hosted checkout page instead of the custom flow.
    // /src/config/features
    {
      useStripeCheckoutPage: true;
    }

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:

  1. Update the Configuration:

    • Open the /src/config/features file and set useStripeCheckoutPage to false. This will disable Stripe Checkout and activate the custom payment flow instead.
    // /src/config/features
    {
      useStripeCheckoutPage: false;
    }

Summary of Differences

FeatureStripe Checkout FlowCustom Flow
CustomizationLimited (uses Stripe’s hosted page)Full control over UI and logic
User ExperienceRedirects to external pageStays within your app
Security & ComplianceHandled by Stripe (PCI compliant)Handled by you, using Stripe’s APIs
Payment Method SetupStripe handles itYou handle it via SetupIntent
Subscription ManagementStripe handles subscription creationYou handle subscription logic
Business LogicLimited to what Stripe offersFully 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.