我在 SvelteKit 项目中遇到了 Stripe webhook 处理程序的问题。该处理程序处理结账会话,并应将客户 ID 存储在 Supabase 中,但该操作似乎在完成之前就被终止了。这是相关代码:
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;
}
问题是Supabase
upsert
操作后的日志没有出现,提示函数执行被切断。但是,我等不及了,因为我需要向 Stripe 发送快速回复。
我已验证 Supabase 的环境变量是否正确,并且 webhook 本身工作正常(由 stripe 调用)。即使主处理程序已经响应,如何确保
storeStripeCustomerId
函数完成执行?
附加背景:
任何有关如何在 SvelteKit 上下文中正确处理此异步操作的建议将不胜感激。
我无法谈论 Supabase upsert 的问题,但就这一点而言:
但是,我等不及了,因为我需要向 Stripe 发送快速回复。
您应该:
注意条纹文档说:
您的端点必须在任何可能导致超时的复杂逻辑之前快速返回成功状态代码 (2xx)。例如,在更新会计系统中已付款的客户发票之前,您必须返回 200 响应。
200/成功响应仅表示成功交付,不应用作成功处理的代理。
事实证明,Cloudflare 在异步函数完成之前终止了 webhook 的工作线程,因为 webhook 已经返回了 200 答案。这也解释了为什么它在本地有效。
我通过使用 Cloudflare 的
waitUntil
函数解决了这个问题。此函数可防止工作进程过早终止,从而允许异步操作即使在发送 HTTP 响应后也能完成(请注意,它仍然是异步的并且不等待任何内容)。以下是我如何将 waitUntil 集成到我的 webhook 处理程序中:
// 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);
});
}
您必须像这样扩展应用程序配置才能使平台可用。
interface Platform {
env: {
COUNTER: DurableObjectNamespace;
};
context: {
waitUntil(promise: Promise<any>): void;
};
caches: CacheStorage & { default: Cache }
}
这里是官方 Cloudflare 文档,讨论如何在 Svelte 中访问
waitUntil
。