I’m trying to integrate Stripe subscriptions billing in my app with a tiered pricing model and based on my understanding, I need to do two things:
- Allow my new users to also create a stripe customer account (via the integration)
- Monitor Stripe webhook ‘events’ to provision access while customer subscription payments are active
My userflow is as follows:
- create profile in my app (saved to database) -> redirected to stripe checkout portal for billing info (saved to stripe database) -> attempt to save stripe customerId to my database so I can monitor subscription status
However, I can’t figure out how to save the customerId info in my app because req.user
and req.params
are empty as the users are sent back to my app from the stripe billing portal
Controller function
module.exports.stripeWebhook = async (req, res) => { let data; const webhookSecret = stripeWebhookSecret; if (webhookSecret) { let event; let signature = req.headers["stripe-signature"]; try { event = stripe.webhooks.constructEvent( req.body, signature, webhookSecret ); } catch (err) { console.log(`⚠️ Webhook signature verification failed.`); return res.sendStatus(400); } data = event.data; eventType = event.type; } else { // retrieve the event data directly from the request body. data = req.body.data; eventType = req.body.type; } switch (eventType) { case 'payment_intent.succeeded': { console.log('PaymentIntent was successful!'); break; } case 'checkout.session.completed': // Payment is successful and the subscription is created. // You should provision the subscription and save the customer ID to your database. console.log(data.object.customer); <- works const user = await User.findById(req.user.id); <- comes back empty so my next two lines of code don't work user.stripeId.push(data.object.customer); await user.save(); break; default: } res.sendStatus(200); };
App.js
app.use(bodyParser.raw({type: "application/json"})); app.use(express.json({ limit: '1mb' })); app.use(express.urlencoded({ extended: true }));
I included the app.js code because the bodyparser.raw
has an impact on how the body comes through in my controller function.
I was counting on the req.user or req.params to find the user in my database but it’s not working. How do I save the customerId to my database like the stripe comments suggest?
Advertisement
Answer
You should create Stripe customer account before creating checkout session for the customer.
Check if customer already have
stripe_customer
account (Stripe customer account). If yes, use that one. If not, create one for him and save it in database.Set
stripe_customer
for the checkout session, so customer will be automatically authenticated in the Stripe checkoutYou can optionally put user’s
_id
in the metadata of the Stripe checkout session, so you access that data later in the webhook.
Note: You should create one stripe_customer
account for each currency. So one user can have multiple stripe_customer
accounts, one for each currency.
router.post('/create-checkout-session', authenticate.verifyUser, async (req, res) => { const { currency } = req.body; ... // If user does not have stripe customer for order currency, create a new one. if (!(req.user.stripe_customer && req.user.stripe_customer[currency])) { const new_stripe_customer = await stripe.customers.create({ email: req.user.email, metadata: { user_id: req.user._id.toString(), }, }); let update = {}; update[`stripe_customer.${currency}`] = new_stripe_customer.id; await Users.findByIdAndUpdate(req.user._id, update); if (!req.user.stripe_customer) { req.user.stripe_customer = {}; req.user.stripe_customer[currency] = new_stripe_customer.id; } else { req.user.stripe_customer[currency] = new_stripe_customer.id; } } ... // Set `stripe_customer` for checkout session. const session = await stripe.checkout.sessions.create({ customer: req.user.stripe_customer[currency], ... }); ... }