Reputation: 2890
I'm encountering an issue with a Stripe webhook handler in my SvelteKit project. The handler processes a checkout session and should store the customer ID in Supabase, but it appears the operation is being terminated before completion. Here's the relevant code:
export const storeStripeCustomerId = async (session: Stripe.Checkout.Session) => {
let customerId: string = '';
if (typeof session.customer === 'string') {
customerId = session.customer;
} else if (session.customer !== null) {
customerId = session.customer.id;
}
console.log("Storing Stripe customer ID", customerId, "for session", session.id);
if (customerId) {
// This being logged
console.log("session.client_reference_id", session.client_reference_id);
const {error} = await SUPABASE_SERVICE_ROLE.schema('stripe').from('user_id_table').upsert({
id: session.client_reference_id,
stripe_customerid: customerId,
stripe_email: session.customer_details?.email
});
// This is not logged anymore
console.log("Test");
console.log(error);
if (error) {
console.error(error);
errorReturn(400, "Supabase: " + error.message);
}
}
}
// /api/stripe/webhook
...
case 'checkout.session.completed': {
const session: Stripe.Checkout.Session = event.data.object
storeStripeCustomerId(session);
break;
}
The problem is that the logs after the Supabase upsert
operation don't appear, suggesting that the function execution is being cut off. However, I can't await it as I need to send a quick response back to Stripe.
I've verified that the environment variables for Supabase are correct and the webhook itself works properly (gets called by stripe). How can I ensure that the storeStripeCustomerId
function completes its execution, even if the main handler has already responded?
Additional context:
Any suggestions on how to properly handle this asynchronous operation in a SvelteKit context would be greatly appreciated.
Upvotes: 1
Views: 101
Reputation: 2890
Turns out Cloudflare is terminating the worker for the webhook before the async function finishes, since the webhook already returned a 200 answer. This also explains why it worked locally.
I resolved it by using the waitUntil
function from Cloudflare. This function prevents the worker from terminating prematurely, allowing asynchronous operations to complete even after sending the HTTP response (note that it's still async and not awaiting anything). Here’s how I integrated waitUntil into my webhook handler:
// Webhook
const processEvent = async () => {
switch (event.type) {
case 'entitlements.active_entitlement_summary.updated':
await handleEntitlement(event.data.object.customer);
console.log('Entitlements updated successfully');
break;
case 'checkout.session.completed':
await storeStripeCustomerId(event.data.object);
console.log('Customer ID stored successfully');
break;
}
};
// We cannot await processEvent() directly, because we need to return a quick response to Stripe
if (platform && platform.context && platform.context.waitUntil) {
// On Cloudflare, using waitUntil to ensure the Cloudflare worker isn't killed
platform.context.waitUntil(processEvent());
} else {
// Locally or on platforms without waitUntil, running the async function directly
processEvent().catch(error => {
console.error('Failed to process event in background:', error);
});
}
You have to extend your app configuration like this to make the platform available.
interface Platform {
env: {
COUNTER: DurableObjectNamespace;
};
context: {
waitUntil(promise: Promise<any>): void;
};
caches: CacheStorage & { default: Cache }
}
Here is the official Cloudflare documentation that talks about how to access waitUntil
in Svelte.
Upvotes: 0
Reputation: 7459
I can't speak to the issues with the Supabase upsert, but in terms of this:
However, I can't await it as I need to send a quick response back to Stripe.
You should either:
Note the Stripe docs say:
Your endpoint must quickly return a successful status code (2xx) prior to any complex logic that could cause a timeout. For example, you must return a 200 response before updating a customer’s invoice as paid in your accounting system.
The 200/success response is to indicate successful delivery only, and should not be used as a proxy for successful processing.
Upvotes: 1