Invoice Preview
The "Cart" here is shown as a result of generating an invoice preview when a new item is added to the cart
servertypescript
export const POST = async (req: Request) => {
const { cart } = await req.json()
const invoicePreview = await stripe.invoices.createPreview({
subscription_details: {
items: cart
}
})
return Response.json({invoicePreview});
}
Update Elements
When the invoice preview is returned to the client, ensure that you call elements.update() setting the new amount. This is crucial to ensure the correct amount is displayed for wallet payments (Google Pay and Apple Pay)
clienttypescript
// page.tsx
const handleAddToCart = async (id: string) => {
setCart(newCart);
// ...
const { invoice_preview } = await post(/* update cart */);
setAmount(invoicePreview.amount_due);
};
// CheckoutForm.tsx
// (receives amountDue as a prop)
useEffect(() => {
elements.update({ // update the amount for digital wallets
amount: props.amountDue,
});
}, [props.amountDue]);
}
Payment Element
The payment element is loaded here in a deferred configuration, with intial values provided for rendering but then ignored. Clicking on the submit button triggers a call to the server to create the subscription and receive the
client_secret for the payment intent tied to the first invoice.
clienttsx
<Elements stripe={loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY as string)} options={{
mode: 'subscription',
amount: 50,
currency: 'usd',
appearance: {
theme: "night"
}
}}>
{typeof window != "undefined" && <CheckoutForm
returnUrl={window.location.origin + "/stripe/confirmation"}
submitButtonText={`Subscribe for ${formatPrice(previewInvoice?.total || 0)}`}
submitButtonActive={previewInvoice ? previewInvoice.total > 0 : false}
paymentIntentGetter={getPaymentIntent}
/>}
</Elements>
Create Subscription
Create a customer (since we need one to create the subscription) and the create the subscription in a default_incomplete status. Expand the latest invoice to get the confirmation secret and return it to the client for confirmation in the payment element.
servertypescript
export const POST = async (req: Request) => {
const { cart } = await req.json()
const customer = await stripe.customers.create({
name: "Customer"
})
const susbcription = await stripe.subscriptions.create({
customer: customer.id,
items: cart,
payment_behavior: "default_incomplete",
payment_settings: {
save_default_payment_method: "on_subscription"
},
expand: ["latest_invoice.confirmation_secret"]
})
return Response.json({
susbcription,
clientSecret: susbcription
.latest_invoice!
//@ts-ignore
.confirmation_secret!
.client_secret || null
})
}