Skip to content

Commit aadd7cd

Browse files
authored
api: billing hotfixes (#2347)
1 parent 87374c1 commit aadd7cd

2 files changed

Lines changed: 72 additions & 8 deletions

File tree

packages/api/src/controllers/user.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,6 +1378,7 @@ app.post(
13781378
price: item.id,
13791379
})),
13801380
],
1381+
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
13811382
},
13821383
);
13831384
} else {
@@ -1413,10 +1414,37 @@ app.post(
14131414
})),
14141415
...payAsYouGoItems,
14151416
],
1417+
expand: ["latest_invoice.payment_intent", "pending_setup_intent"],
14161418
},
14171419
);
14181420
}
14191421

1422+
// Check if payment failed before updating user's plan
1423+
const latestInvoice = updatedSubscription.latest_invoice as any;
1424+
if (latestInvoice?.payment_intent) {
1425+
const paymentIntent = latestInvoice.payment_intent;
1426+
if (
1427+
paymentIntent.status === "requires_payment_method" ||
1428+
paymentIntent.status === "canceled"
1429+
) {
1430+
return res.status(402).send({
1431+
error: {
1432+
message: "Payment failed. Please update your payment method.",
1433+
},
1434+
});
1435+
}
1436+
}
1437+
1438+
// Check subscription status for payment issues
1439+
if (
1440+
updatedSubscription.status === "past_due" ||
1441+
updatedSubscription.status === "unpaid"
1442+
) {
1443+
return res.status(402).send({
1444+
error: { message: "Subscription payment failed." },
1445+
});
1446+
}
1447+
14201448
if (
14211449
products[payload.stripeProductId].name == "Growth" ||
14221450
products[payload.stripeProductId].name == "Scale"

packages/www/components/PlanForm/index.tsx

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,14 +157,50 @@ const PlanForm = ({
157157

158158
// If user already submitted payment, don't ask for payment information again
159159
if (user.stripeCustomerPaymentMethodId) {
160-
await updateSubscription({
161-
stripeCustomerId: user.stripeCustomerId,
162-
stripeCustomerPaymentMethodId: user.stripeCustomerPaymentMethodId,
163-
stripeCustomerSubscriptionId: user.stripeCustomerSubscriptionId,
164-
stripeProductId,
165-
});
166-
setStatus("succeeded");
167-
setOpen(false);
160+
try {
161+
const result: any = await updateSubscription({
162+
stripeCustomerId: user.stripeCustomerId,
163+
stripeCustomerPaymentMethodId: user.stripeCustomerPaymentMethodId,
164+
stripeCustomerSubscriptionId: user.stripeCustomerSubscriptionId,
165+
stripeProductId,
166+
});
167+
168+
if (result?.error || result?.errors) {
169+
console.error(result.error || result.errors);
170+
setStatus("error");
171+
openSnackbar("Payment failed. Please update your payment method.");
172+
return;
173+
}
174+
175+
const pendingSetupIntent = result?.pending_setup_intent;
176+
if (pendingSetupIntent?.status === "requires_action") {
177+
const confirmResult = await stripe.confirmCardSetup(
178+
pendingSetupIntent.client_secret,
179+
{
180+
payment_method: user.stripeCustomerPaymentMethodId,
181+
},
182+
);
183+
if (confirmResult.error) {
184+
console.error(confirmResult.error);
185+
setStatus("error");
186+
openSnackbar("Payment verification failed.");
187+
return;
188+
}
189+
}
190+
191+
if (result?.status === "past_due" || result?.status === "unpaid") {
192+
setStatus("error");
193+
openSnackbar("Payment failed. Please update your payment method.");
194+
return;
195+
}
196+
197+
setStatus("succeeded");
198+
setOpen(false);
199+
} catch (error) {
200+
console.error(error);
201+
setStatus("error");
202+
openSnackbar("An error occurred. Please try again.");
203+
}
168204
} else {
169205
const cardElement = elements!.getElement(CardElement);
170206
createPaymentMethod({

0 commit comments

Comments
 (0)