Is there a way to incorporate multiple plan tiers on Stripe's platform?

//api/stripe
import { auth, currentUser } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { prismadb } from "@/lib/prismadb";
import { stripe } from "@/lib/stripe";
import { absoluteUrl } from "@/lib/utils";


const billingUrl = absoluteUrl("/billing");

export async function GET() {
  try {
    const { userId } = auth();
    const user = await currentUser();

    if (!userId || !user) {
      return new NextResponse("Unauthorized", { status: 401 });
    }

    const userSubscription = await prismadb.userSubscription.findUnique({
      where: {
        userId,
      },
    });

    if (userSubscription && userSubscription.stripeCustomerId) {
      const stripeSession = await stripe.billingPortal.sessions.create({
        customer: userSubscription.stripeCustomerId,
        return_url: billingUrl,
      });

      return new NextResponse(JSON.stringify({ url: stripeSession.url }));
    }

    const stripeSession = await stripe.checkout.sessions.create({
      success_url: billingUrl,
      cancel_url: billingUrl,
      payment_method_types: ["card", "Paypal"],
      mode: "subscription",
      billing_address_collection: "auto",
      customer_email: user.emailAddresses[0].emailAddress,
      line_items: [
        {
          price_data: {
            currency: "USD",
            product_data: {
              name: "Plume Pro",
              description: "Gain Full Access",
            },
            unit_amount: 7999,
            recurring: {
              interval: "month",
            },
          },
          quantity: 1,
        },
        {
          price_data: {
            currency: "USD",
            product_data: {
              name: "Plume Plus",
              description: "Gain Full Access",
            },
            unit_amount: 3999,
            recurring: {
              interval: "month",
            },
          },
          quantity: 1,
        },
      ],
      metadata: {
        userId,
      },
    });

    return new NextResponse(JSON.stringify({ url: stripeSession.url }));
  } catch (error) {
    console.log("[STRIPE_GET]", error);
    return new NextResponse("Internal Error", { status: 500 });
  } 
}

"use client";

import { usePlanModal } from "@/hooks/use-plan-modal";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "../ui/dialog";
import { Separator } from "../ui/separator";
import { Button } from "../ui/button";
import { useToast } from "../ui/use-toast";
import axios from "axios";
import { useState } from "react";

export const PlanModal = () => {
  const planModal = usePlanModal();
  const { toast } = useToast();
  const [loading, setLoading] = useState(false);

  const onSubscribe = async () => {
    try {
      setLoading(true);
      const response = await axios.get("/api/stripe");

      window.location.href = response.data.url;
    } catch (error) {
      toast({
        variant: "destructive",
        description: "Oops! Something went wrong.",
      });
    } finally {
      setLoading(false);
    }
  };
  return (
    <Dialog open={planModal.isOpen} onOpenChange={planModal.onClose}>
      <DialogContent>
        <DialogHeader className="space-y-4">
          <DialogTitle className="text-center">Upgrade your Plan</DialogTitle>
          <DialogDescription className="text-center space-y-2">
            Choose a plan that meets your needs.
          </DialogDescription>
        </DialogHeader>
        <Separator />
        <div className="flex items-center justify-between">
          <p className="text-2xl font-plus font-medium">
            $39
            <span className="text-sm font-normal">.99</span>
          </p>
          <Button size="md" onClick={onSubscribe}>
            Subscribe
          </Button>
        </div>
      </DialogContent>
    </Dialog>
  );
};

I'm developing an application using Next.js and Stripe, with multiple subscription plans for users to choose from. I'm facing a challenge in setting up the plans and checkout flow efficiently. Should I store all the plans in a single API folder or create separate API files for each tier? The current implementation sums up the prices of the two tiers, resulting in a total checkout amount of $119.98. Any advice on better structuring the subscription plans in the API would be greatly appreciated.

Answer №1

To ensure thoroughness:

  1. Should you wish to offer multiple subscription plans for your customers to choose from, consider exploring Pricing Table. It is recommended that each plan generates a separate Checkout Session.

  2. If you are interested in having one plan with various tiers, allowing customers to subscribe and be billed based on their usage, you may opt for tiered-pricing.

Although your inquiry is slightly ambiguous, it appears that Pricing Table would be suited for your needs.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Personalizing File Selection

What is the process for customizing file uploads? <%= f.file_field :image, class: 'inputfile' %> <label for="image">Choose an image</label> I am looking to replace "choose an image" with "choose a file" ...

What is the process behind Stackoverflow's "Congratulations, you've earned a new badge" popup window?

Possible Duplicates: Custom notify box similar to StackOverflow Creating popup message like StackOverflow How does StackOverflow implement the "You earned a new badge" window? (the orange one that pops up at the top of the screen, I believe it&ap ...

Why does the method Array.concat on an extended class remove empty values in Node.js version 8.9.3?

I am experimenting with adding new functionality to Arrays in node.js without altering the original Array class to avoid any unintended consequences in other modules. During testing, I encountered some failures which led me to discover that the behavior o ...

Switch content based on value with Javascript

One of my elements is: <a href="#" type="link" class="button send" classAct="button send" classSel="button send pressed" label="encrypt" title="sendmessage" onclick="add_encryption();">Encrypt</a> When toggled via the JavaScript below, I want ...

Mastering the use of npm and sails to create HTML-PDF files effortlessly

UPDATE: I am simplifying my question and will address any other concerns in separate posts if necessary. The initial post was too lengthy, hoping for a straightforward guide on utilizing sails to its fullest potential. Apologies. To begin with, my knowled ...

Nested Ajax request fails and triggers a full page reload

My goal is to search for product information and images using a product code input on index.php. The query runs in open_first.php via an ajax post request, which works perfectly. open_first.php displays images that can be selected by clicking on them. How ...

Leveraging Enjoyhint within nextJS

I am attempting to create a code tour using EnjoyHint, but encountered an error after installing the xbs-enjoyhint library. The error reads: Server Error - ReferenceError: CanvasRenderingContext2D is not defined. This issue is within the jquery.enjoyhint ...

What steps should I follow to create an automatic image slider using Fancybox that transitions to the next slide seamlessly?

*I've recently ventured into web design and am currently experimenting with Fancybox. I'm interested in creating a slider with 3 images that automatically transitions to the next image, similar to what is seen on this website: Is it feasible to ...

What is the best way to compare an attribute value with a JSON value in JavaScript?

I have a JSON string that looks like this: { "DocID": "NA2", "DocType": "Phase1.1 - Visa Documents (This section is applicable for HK work location only)", "DocSubType": "New Application", "DocName": "Passport / Travel Document (Soft copy only) ...

Ways to switch text on and off smoothly using transitions

I have created a webpage where text starts off hidden but has a visible heading. When you click on the heading, the text is revealed below. I am putting the final touches and aiming to make the text slide in smoothly. I am using Javascript for toggling ins ...

Check to see if two sets of coordinates fall within the specified radius

I'm currently working on analyzing the collision data for major intersections in my city by aggregating it with the location information. My main goal is to determine the number of accidents that occurred within a 20-meter radius of each intersection. ...

Guide for inserting personalized buttons onto a map with the Bing Maps 6.3 Ajax Control

Looking to enhance a Bing Map with custom buttons for more interactive functionality? I'm interested in creating a custom dashboard on the map that would allow users to toggle different information or colors displayed on specific pins or polygons by s ...

Updating the $_GET parameter using JQuery when a button is clicked

I'm working on implementing a feature where a series of buttons can update the $_GET['year'] variable on my website. These buttons should function across different pages within my website, such as: example.com example.com/?search=foo exa ...

Tips for finding information within a table using HTML

My task involves creating a table with the option for users to search data within it. However, I have encountered an issue where I am only able to search for data in the first row of the table. <table style="width:100%" id="table"> <tr> ...

Exploring the best practices for loading state from local storage in VueJS/NuxtJS by leveraging the "useLocalStorage" utility

When attempting to utilize useLocalStorage from pinia, I am encountering an issue where the data in the store's state is not fetched from local storage and instead defaults to the default value. Below is the code snippet from my store method: import ...

Ways to activate a special function after clicking a social media button?

My goal is to implement a social media div locker on my website. The concept involves hiding a piece of text behind a div locker that prompts the user to share the content on Facebook, Google+, or Twitter in order to unlock it. After the visitor clicks on ...

Error in React UseTable: Attempting to read properties of an undefined object (specifically trying to use a forEach loop)

https://i.stack.imgur.com/ZSLSN.pngHaving an issue here that I need some quick help with! Whenever I attempt to render data into a React table, I encounter the error mentioned above. The data is fetched from an API using Axios. Let's take a look at t ...

Confusion between modules and classes in Node.js when using CoffeeScript

I'm struggling to understand how to use Protoype in CoffeeScript, even though I am familiar with using it in standard Javascript with Node.js and modules. Imagine I have a file named mymodule.coffee: Module = {} class MyModule constructor: (para ...

Tips for converting asynchronous function calls into synchronous functions in Node.js or JavaScript?

Imagine you are managing a library that provides access to a function called getData. Users utilize this function to retrieve real data: var output = getData(); In the background, the data is stored in a file, so you have implemented the getData functi ...

Tips for substituting commas and slashes within an input text box

For instance, if the input is "1,23/456", the output should be "123456". When "1,23/456" is entered into the input field and "enter" is pressed, it should automatically convert to "123456". <input id="Id" ng-model="Id" name="searchInput" type="text"&g ...