Unable to handle Stripe Webhooks when client and server on different domains

Hi, Firstly, thanks for an awesome library. I've stumbled across an issue and hoping if someone had any pointers. Currently trying to integrate Stripe with Organizations. From a setup POV, we have the backend running Hono on CF Workers and for the frontend, we have a React + Vite (separate client and server) For local testing, client: localhost:5173, server localhost:8787 When calling the client.subscription.upgrade in the frontend, the Stripe checkout page is shown, we are able to do a checkout and the subscription is created successfully. We can see the subscription in Stripe and we can also see the webhooks being sent to the backend. But, the subscription status is not getting updated in the database. It is still incomplete and the subscription_id is NULL. We also do not see any errors in the backend log, just one entry with HTTP 302 with the onSuccess callback URL pointing to the client origin
--> GET /auth/subscription/success?callbackURL=http%3A%2F%2Flocalhost%3A5173%2Fassessments&subscriptionId=fQdAgAVQRyIwVQwuA4pi9eVDvz9lLBJG 302 288ms
[wrangler:info] GET /auth/subscription/success 302 FOUND
--> GET /auth/subscription/success?callbackURL=http%3A%2F%2Flocalhost%3A5173%2Fassessments&subscriptionId=fQdAgAVQRyIwVQwuA4pi9eVDvz9lLBJG 302 288ms
[wrangler:info] GET /auth/subscription/success 302 FOUND
onSubscriptionComplete is also not being triggered in the plugin. When we test cancelling a subscription directly from Stripe, we see Stripe webhook error because the subscription id is not updated and is still NULL
WARN [Better Auth]: Stripe webhook error: Subscription not found for subscriptionId: sub_1RnLptBUHBWZLcxoU6f2N9X2
WARN [Better Auth]: Stripe webhook error: Subscription not found for subscriptionId: sub_1RnLptBUHBWZLcxoU6f2N9X2
1 Reply
__maxom__
__maxom__OP4mo ago
Below is my plugin config on the backend:
stripe({
stripeClient,
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET!,
createCustomerOnSignUp: false,
subscription: {
enabled: true,
plans: async () => {
return stripePricingPlans.map((plan) => mapPlanForStripe(plan));
},
authorizeReference: async ({ user, session, referenceId, action }) => {
if (
action === "upgrade-subscription" ||
action === "cancel-subscription" ||
action === "restore-subscription"
) {
const org = await db.query.members.findFirst({
where: and(
eq(schema.members.organizationId, referenceId),
eq(schema.members.userId, user.id)
),
});
return org?.role === "owner";
}
return true;
},
getCheckoutSessionParams: async (
{ user, session, plan, subscription },
request
) => {
return {
params: {
allow_promotion_codes: true,
tax_id_collection: {
enabled: true,
},
},
options: {
idempotencyKey: `sub_${user.id}_${plan.name}_${Date.now()}`,
},
};
},
onSubscriptionComplete: async ({
event,
subscription,
stripeSubscription,
plan,
}) => {
// Called when a subscription is successfully created
console.log(
`Event: ${event} Subscription ${subscription.id} created for plan ${plan.name}, subscription: ${stripeSubscription}`
);
},
},
}),
stripe({
stripeClient,
stripeWebhookSecret: env.STRIPE_WEBHOOK_SECRET!,
createCustomerOnSignUp: false,
subscription: {
enabled: true,
plans: async () => {
return stripePricingPlans.map((plan) => mapPlanForStripe(plan));
},
authorizeReference: async ({ user, session, referenceId, action }) => {
if (
action === "upgrade-subscription" ||
action === "cancel-subscription" ||
action === "restore-subscription"
) {
const org = await db.query.members.findFirst({
where: and(
eq(schema.members.organizationId, referenceId),
eq(schema.members.userId, user.id)
),
});
return org?.role === "owner";
}
return true;
},
getCheckoutSessionParams: async (
{ user, session, plan, subscription },
request
) => {
return {
params: {
allow_promotion_codes: true,
tax_id_collection: {
enabled: true,
},
},
options: {
idempotencyKey: `sub_${user.id}_${plan.name}_${Date.now()}`,
},
};
},
onSubscriptionComplete: async ({
event,
subscription,
stripeSubscription,
plan,
}) => {
// Called when a subscription is successfully created
console.log(
`Event: ${event} Subscription ${subscription.id} created for plan ${plan.name}, subscription: ${stripeSubscription}`
);
},
},
}),

Did you find this page helpful?