forked from clerk/use-stripe-subscription
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
119 additions
and
118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,138 +1,139 @@ | ||
import Stripe from "stripe"; | ||
import Stripe from "stripe" | ||
|
||
export const stripeApiClient = process.env.STRIPE_SECRET_KEY | ||
? new Stripe(process.env.STRIPE_SECRET_KEY, { | ||
apiVersion: null, | ||
}) | ||
: null; | ||
? new Stripe(process.env.STRIPE_SECRET_KEY, { | ||
apiVersion: null, | ||
}) | ||
: null | ||
|
||
export interface CustomerHasFeatureArgs { | ||
customerId: string; | ||
feature: string; | ||
customerId: string | ||
feature: string | ||
} | ||
export const customerHasFeature = async ({ customerId, feature }) => { | ||
const customer = (await stripeApiClient.customers.retrieve(customerId, { | ||
expand: ["subscriptions"], | ||
})) as Stripe.Customer; | ||
let subscription = customer.subscriptions.data[0] || null; | ||
if (subscription) { | ||
subscription = await stripeApiClient.subscriptions.retrieve( | ||
subscription.id, | ||
{ expand: ["items.data.price.product"] } | ||
); | ||
const features = (subscription.items.data[0].price | ||
.product as Stripe.Product).metadata.features; | ||
return features?.includes(feature); | ||
} | ||
return false; | ||
}; | ||
const customer = (await stripeApiClient.customers.retrieve(customerId, { | ||
expand: ["subscriptions"], | ||
})) as Stripe.Customer | ||
let subscription = customer.subscriptions.data[0] || null | ||
if (subscription) { | ||
subscription = await stripeApiClient.subscriptions.retrieve( | ||
subscription.id, | ||
{ expand: ["items.data.price.product"] } | ||
) | ||
const features = ( | ||
subscription.items.data[0].price.product as Stripe.Product | ||
).metadata.features | ||
return features?.includes(feature) | ||
} | ||
return false | ||
} | ||
|
||
export const subscriptionHandler = async ({ customerId, query, body }) => { | ||
if (query.action === "useSubscription") { | ||
return await useSubscription({ customerId }); | ||
} | ||
if (query.action === "useSubscription") { | ||
return await useSubscription({ customerId }) | ||
} | ||
|
||
if (query.action === "redirectToCheckout") { | ||
return await redirectToCheckout({ customerId, body }); | ||
} | ||
if (query.action === "redirectToCheckout") { | ||
return await redirectToCheckout({ customerId, body }) | ||
} | ||
|
||
if (query.action === "redirectToCustomerPortal") { | ||
return await redirectToCustomerPortal({ customerId, body }); | ||
} | ||
if (query.action === "redirectToCustomerPortal") { | ||
return await redirectToCustomerPortal({ customerId, body }) | ||
} | ||
|
||
return { error: "Action not found" }; | ||
}; | ||
return { error: "Action not found" } | ||
} | ||
|
||
async function useSubscription({ customerId }) { | ||
// Retrieve products based on default billing portal config | ||
|
||
// First, retrieve the configuration | ||
const configurations = await stripeApiClient.billingPortal.configurations.list( | ||
{ | ||
is_default: true, | ||
expand: ["data.features.subscription_update.products"], | ||
} | ||
); | ||
|
||
// Stripe doesn't let us expand as much as we'd like. | ||
// Run this big mess to manually expand | ||
|
||
// We preserve the order stripe returns things in | ||
const products = new Array( | ||
configurations.data[0].features.subscription_update.products.length | ||
); | ||
const pricePromises = configurations.data[0].features.subscription_update.products | ||
.map((product, i) => | ||
product.prices.map(async (price, j) => { | ||
const priceData = await stripeApiClient.prices.retrieve(price, { | ||
expand: ["product"], | ||
}); | ||
const cleanPriceData = { | ||
...priceData, | ||
product: (priceData.product as Stripe.Product).id, | ||
}; | ||
if (!products[i]) { | ||
products[i] = { | ||
product: priceData.product, | ||
prices: new Array(product.prices.length), | ||
}; | ||
products[i].prices[j] = cleanPriceData; | ||
} else { | ||
products[i].prices[j] = cleanPriceData; | ||
} | ||
}) | ||
) | ||
.flat(); | ||
|
||
let subscription; | ||
const subscriptionPromise = stripeApiClient.customers | ||
.retrieve(customerId, { expand: ["subscriptions"] }) | ||
.then((customer) => { | ||
// This package is limited to one subscription at a time | ||
// @ts-ignore | ||
subscription = customer.subscriptions.data[0] || null; | ||
}); | ||
|
||
await Promise.all([...pricePromises, subscriptionPromise]); | ||
|
||
return { products, subscription }; | ||
// Retrieve products based on default billing portal config | ||
|
||
// First, retrieve the configuration | ||
const configurations = | ||
await stripeApiClient.billingPortal.configurations.list({ | ||
is_default: true, | ||
expand: ["data.features.subscription_update.products"], | ||
}) | ||
|
||
// Stripe doesn't let us expand as much as we'd like. | ||
// Run this big mess to manually expand | ||
|
||
// We preserve the order stripe returns things in | ||
const products = new Array( | ||
configurations.data[0].features.subscription_update.products.length | ||
) | ||
const pricePromises = | ||
configurations.data[0].features.subscription_update.products | ||
.map((product, i) => | ||
product.prices.map(async (price, j) => { | ||
const priceData = await stripeApiClient.prices.retrieve(price, { | ||
expand: ["product"], | ||
}) | ||
const cleanPriceData = { | ||
...priceData, | ||
product: (priceData.product as Stripe.Product).id, | ||
} | ||
if (!products[i]) { | ||
products[i] = { | ||
product: priceData.product, | ||
prices: new Array(product.prices.length), | ||
} | ||
products[i].prices[j] = cleanPriceData | ||
} else { | ||
products[i].prices[j] = cleanPriceData | ||
} | ||
}) | ||
) | ||
.flat() | ||
|
||
let subscription | ||
const subscriptionPromise = stripeApiClient.customers | ||
.retrieve(customerId, { expand: ["subscriptions"] }) | ||
.then((customer) => { | ||
// This package is limited to one subscription at a time | ||
// @ts-ignore | ||
subscription = customer.subscriptions.data[0] || null | ||
}) | ||
|
||
await Promise.all([...pricePromises, subscriptionPromise]) | ||
|
||
return { products, subscription } | ||
} | ||
|
||
async function redirectToCustomerPortal({ customerId, body }) { | ||
return await stripeApiClient.billingPortal.sessions.create({ | ||
customer: customerId, | ||
return_url: body.returnUrl, | ||
}); | ||
return await stripeApiClient.billingPortal.sessions.create({ | ||
customer: customerId, | ||
return_url: body.returnUrl, | ||
}) | ||
} | ||
|
||
async function redirectToCheckout({ customerId, body }) { | ||
const configurations = await stripeApiClient.billingPortal.configurations.list( | ||
{ | ||
is_default: true, | ||
expand: ["data.features.subscription_update.products"], | ||
} | ||
); | ||
|
||
// Make sure the price ID is in here somewhere | ||
let go = false; | ||
for (let product of configurations.data[0].features.subscription_update | ||
.products) { | ||
for (let price of product.prices) { | ||
if (price === body.price) { | ||
go = true; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if (go) { | ||
return await stripeApiClient.checkout.sessions.create({ | ||
customer: customerId, | ||
success_url: body.successUrl, | ||
cancel_url: body.cancelUrl, | ||
line_items: [{ price: body.price, quantity: 1 }], | ||
mode: "subscription", | ||
}); | ||
} | ||
return { error: "Error" }; | ||
const configurations = | ||
await stripeApiClient.billingPortal.configurations.list({ | ||
is_default: true, | ||
expand: ["data.features.subscription_update.products"], | ||
}) | ||
|
||
// Make sure the price ID is in here somewhere | ||
let go = false | ||
for (let product of configurations.data[0].features.subscription_update | ||
.products) { | ||
for (let price of product.prices) { | ||
if (price === body.price) { | ||
go = true | ||
break | ||
} | ||
} | ||
} | ||
|
||
if (go) { | ||
return await stripeApiClient.checkout.sessions.create({ | ||
customer: customerId, | ||
success_url: body.successUrl, | ||
cancel_url: body.cancelUrl, | ||
line_items: [{ price: body.price, quantity: 1 }], | ||
mode: "subscription", | ||
allow_promotion_codes: true, | ||
}) | ||
} | ||
return { error: "Error" } | ||
} |