Open andriusmv opened 1 year ago
Found: needs to add 'one_time' to the SQL file:
Please refer to this pull: https://github.com/vercel/nextjs-subscription-payments/pull/142
managed to do this by login to supabse, opening the SQL editor, opening a new query and writing this: ALTER TYPE pricing_plan_interval ADD VALUE 'one_time' AFTER 'year'; then hit Run. You should get a success message.
Thanks for documenting your solution. Let's keep this open as a feature request 👍
It looks like the create-checkout-session.ts also needs to be updated for this to work properly. I managed to get it working this way:
import { withApiAuth } from '@supabase/auth-helpers-nextjs';
import { createOrRetrieveCustomer } from 'controllers/supabase-admin';
import { getURL } from 'controllers/helpers';
export default withApiAuth(async function createCheckoutSession(
req,
res,
supabaseServerClient
) {
if (req.method === 'POST') {
const { price, quantity = 1, metadata = {} } = req.body;
try {
const {
data: { user }
} = await supabaseServerClient.auth.getUser();
const customer = await createOrRetrieveCustomer({
uuid: user?.id || '',
email: user?.email || ''
});
let session: Stripe.Checkout.Session;
if (price.type === 'recurring') {
session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
customer,
line_items: [
{
price: price.id,
quantity
}
],
mode: 'subscription',
allow_promotion_codes: true,
subscription_data: {
trial_from_plan: true,
metadata
},
success_url: `${getURL()}/account`,
cancel_url: `${getURL()}/`
});
} else if (price.type === 'one_time') {
session = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
billing_address_collection: 'required',
customer,
line_items: [
{
price: price.id,
quantity
}
],
mode: 'payment',
success_url: `${getURL()}/account`,
cancel_url: `${getURL()}/`
});
}
return res.status(200).json({ sessionId: session.id });
} catch (err: any) {
console.log(err);
res
.status(500)
.json({ error: { statusCode: 500, message: err.message } });
}
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Method Not Allowed');
}
});
@dalkommatt How are you storing one_time payments in tables? Did you create a new Payments table or are you using existing Subscriptions?
If possible, can you share your code snippet. Thanks
@dalkommatt How are you storing one_time payments in tables? Did you create a new Payments table or are you using existing Subscriptions?
If possible, can you share your code snippet. Thanks
I'm using the same subscriptions table. For one-time payments the subscription status becomes 'succeeded' so when I query subscriptions I include that in the query.
const { data, error } = await supabase
.from("subscriptions")
.select("*, prices(*, products(*))")
.in("status", ["trialing", "active", "succeeded"])
.single()
@dalkommatt Thanks for getting back so quickly.
I don't see any case which handles one_time
payments in this repo's webhook.
app/api/webhooks/route.ts
case 'checkout.session.completed':
const checkoutSession = event.data.object as Stripe.Checkout.Session;
if (checkoutSession.mode === 'subscription') {
const subscriptionId = checkoutSession.subscription;
await manageSubscriptionStatusChange(
subscriptionId as string,
checkoutSession.customer as string,
true
);
}
break;
I tried writing my own logic, but I was struggling to get price_id, product_id from payment intent. Appreciate your help.
@anup-a this is how I implemented it
case "checkout.session.completed":
const checkoutSession = event.data.object as Stripe.Checkout.Session
if (checkoutSession.mode === "subscription") {
const subscriptionId = checkoutSession.subscription
await manageSubscriptionStatusChange(
subscriptionId as string,
checkoutSession.customer as string,
true
)
}
if (checkoutSession.mode === "payment") {
const paymentIntentId = checkoutSession.payment_intent
await manageSubscriptionStatusChange(
paymentIntentId as string,
checkoutSession.customer as string,
true
)
}
break
Hey @dalkommatt, did you make any change to manageSubscriptionStatusChange
in supabase-admin.ts
(mine looks like the on currently in this repo's main
)?
Using your code, I get a Stripe error:
StripeInvalidRequestError: No such subscription: 'pi_3Oxxxxxxxxxxxxxxxx'
That's probably coming from this piece in manageSubscriptionStatusChange
:
const subscription = await stripe.subscriptions.retrieve(subscriptionId, {
expand: ["default_payment_method"]
});
Either that or my Stripe product is incorrectly setup. I have one product with 2 prices, one of them being a one time payment like this:
Hey @dalkommatt, did you make any change to
manageSubscriptionStatusChange
insupabase-admin.ts
(mine looks like the on currently in this repo'smain
)?Using your code, I get a Stripe error:
StripeInvalidRequestError: No such subscription: 'pi_3Oxxxxxxxxxxxxxxxx'
That's probably coming from this piece in
manageSubscriptionStatusChange
:const subscription = await stripe.subscriptions.retrieve(subscriptionId, { expand: ["default_payment_method"] });
Either that or my Stripe product is incorrectly setup. I have one product with 2 prices, one of them being a one time payment like this:
manageSubscriptionStatusChange
Any updates?
When mapping products, those set as one-time payment (in Stripe) are not rendered. I guess it has something to do with line 107 of the Prixing.tsx component?
{products.map((product) => { const price = product?.prices?.find( (price) => price.interval === billingInterval ); if (!price) return null;