Stripe Webhook联动AWS服务更新DynamoDB付费状态失败排查
问题排查:Stripe Webhook触发404导致DynamoDB支付状态未更新
背景情况
网站表单提交后会写入DynamoDB表,默认paid字段设为false,前端核心状态管理代码片段:
const Home: React.FC = () => { const [file, setFile] = useState<File | null>(null); const [email, setEmail] = useState<string>(''); const [petName, setPetName] = useState<string>(''); const [videoKey, setVideoKey] = useState<string>(''); const [isUploading, setIsUploading] = useState<boolean>(false);
实现逻辑:Stripe Webhook + API Gateway + Lambda
通过监听Stripe支付成功事件,将DynamoDB中对应条目的paid字段更新为true,Lambda代码如下:
// index.mjs import AWS from 'aws-sdk'; import Stripe from 'stripe'; // Initialize Stripe with your secret key const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2020-08-27' }); // Initialize AWS SDK const dynamoDB = new AWS.DynamoDB.DocumentClient(); // Helper function to update the 'paid' status in DynamoDB const updatePaymentStatus = async (userId, videoId, paidStatus) => { const params = { TableName: 'petsafaritable1', // Your DynamoDB table name Key: { userId, videoId }, UpdateExpression: 'set paid = :p', ExpressionAttributeValues: { ':p': paidStatus, }, }; return dynamoDB.update(params).promise(); }; export const handler = async (event) => { let stripeEvent; try { // Verify the event with Stripe to make sure it's legitimate const sig = event.headers['Stripe-Signature']; stripeEvent = stripe.webhooks.constructEvent(event.body, sig, process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { console.error(`Webhook Error: ${err.message}`); return { statusCode: 400, body: `Webhook Error: ${err.message}`, }; } // Handle the event if (stripeEvent.type === 'payment_intent.succeeded') { const paymentIntent = stripeEvent.data.object; const userId = paymentIntent.metadata.userId; const videoId = paymentIntent.metadata.videoId; try { // Update the 'paid' status in DynamoDB to true await updatePaymentStatus(userId, videoId, true); return { statusCode: 200, body: JSON.stringify({ received: true }), }; } catch (updateError) { console.error(`Error updating payment status in DynamoDB: ${updateError.message}`); return { statusCode: 500, body: `Error updating payment status in DynamoDB: ${updateError.message}`, }; } } else { // Ignore other event types return { statusCode: 200, body: `Received unhandled event type: ${stripeEvent.type}`, }; } };
问题现象
- 已完成Lambda环境变量配置、API Gateway搭建(自定义阶段为
/prod),并将Stripe Webhook URL设置为https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod - 使用测试卡(4242...)完成结账后,Stripe控制台显示Webhook交付失败,返回HTTP 404状态码,响应内容为
{"message": "Not Found"} - 前端表单已成功写入DynamoDB初始条目,但
paid字段未更新为true - Stripe触发的事件详情:
{ "id": "evt_1OFml3Ej3GI2oYw2vCQ7n6RW", "object": "event", "api_version": "2023-10-16", "created": 1700783793, "data": { "object": { "id": "cs_test_a17a7e6ZPoUum328HbhqKEiecNa6WcRiHi1TEubvfK00n9bih2mninhSLe", "object": "checkout.session", "after_expiration": null, "allow_promotion_codes": null, "amount_subtotal": 2000, "amount_total": 2000, "automatic_tax": { "enabled": false, "status": null }, "billing_address_collection": null, "cancel_url": "https://www.petsafari.tv/payment-cancelled", "client_reference_id": null, "client_secret": null, "consent": null, "consent_collection": null, "created": 1700783771, "currency": "usd", "currency_conversion": null, "custom_fields": [], "custom_text": { "shipping_address": null, "submit": null, "terms_of_service_acceptance": null }, "customer": null, "customer_creation": "if_required", "customer_details": { "address": { "city": null, "country": "US", "line1": null, "line2": null, "postal_code": "12345", "state": null }, "email": "this@that.com", "name": "chri sdes", "phone": null, "tax_exempt": "none", "tax_ids": [] }, "customer_email": null, "expires_at": 1700870171, "invoice": null, "invoice_creation": { "enabled": false, "invoice_data": { "account_tax_ids": null, "custom_fields": null, "description": null, "footer": null, "metadata": {}, "rendering_options": null } }, "livemode": false, "locale": null, "metadata": {}, "mode": "payment", "payment_intent": "pi_3OFml2Ej3GI2oYw212TKJpIv", "payment_link": null, "payment_method_collection": "if_required", "payment_method_configuration_details": null, "payment_method_options": {}, "payment_method_types": [ "card" ], "payment_status": "paid", "phone_number_collection": { "enabled": false }, "recovered_from": null, "setup_intent": null, "shipping_address_collection": null, "shipping_cost": null, "shipping_details": null, "shipping_options": [], "status": "complete", "submit_type": null, "subscription": null, "success_url": "https://www.petsafari.tv/payment-success?session_id={CHECKOUT_SESSION_ID}", "total_details": { "amount_discount": 0, "amount_shipping": 0, "amount_tax": 0 }, "ui_mode": "hosted", "url": null } }, "livemode": false, "pending_webhooks": 3, "request": { "id": null, "idempotency_key": null }, "type": "checkout.session.completed" }
补充配置信息
API Gateway路由配置:
- 配置了路径为
/{proxy+}的路由,集成目标为对应Lambda函数 - 已启用Lambda代理集成模式
排查步骤
1. 确认API Gateway路由与Webhook URL匹配
- 检查Stripe配置的Webhook URL是否包含完整的路由路径:若API Gateway路由为
/{proxy+},但仅配置根路径/prod,Stripe请求会因找不到对应路由返回404。需确认API Gateway部署的完整路径,例如若Lambda挂载在/webhook路径下,Webhook URL应为https://xxx.execute-api.us-east-1.amazonaws.com/prod/webhook - 登录AWS控制台,查看API Gateway的部署阶段,确认实际可访问路径与Stripe配置一致
2. 验证API Gateway集成配置
- 确认路由的集成请求已启用Lambda代理集成:未启用的话,API Gateway无法正确转发请求到Lambda,会返回404
- 检查路由是否允许
POST方法:Stripe Webhook仅通过POST请求触发,需确保API Gateway路由开放POST方法
3. 修正Stripe事件类型处理逻辑
当前Lambda仅处理payment_intent.succeeded事件,但Stripe实际触发的是checkout.session.completed,即使路径问题解决,该事件也会被标记为未处理。需添加对应事件的处理分支:
// 在handler函数的事件判断中添加分支 } else if (stripeEvent.type === 'checkout.session.completed') { const checkoutSession = stripeEvent.data.object; const paymentIntentId = checkoutSession.payment_intent; // 调用Stripe API获取payment_intent的metadata const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId); const userId = paymentIntent.metadata.userId; const videoId = paymentIntent.metadata.videoId; await updatePaymentStatus(userId, videoId, true); return { statusCode: 200, body: JSON.stringify({ received: true }), }; }
另外,当前Stripe事件的metadata为空,需确保创建Checkout Session时,将userId和videoId写入metadata,否则无法匹配DynamoDB条目:
// 创建Checkout Session时添加metadata const session = await stripe.checkout.sessions.create({ // 其他必填配置 metadata: { userId: "用户唯一ID", videoId: "对应视频ID" } });
4. 测试API Gateway端点可用性
使用curl或Postman发送POST请求到Webhook URL,验证端点是否可访问:
curl -X POST https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/prod \ -H "Content-Type: application/json" \ -d '{"test": "data"}'
- 返回404:说明API Gateway的部署或路由配置存在问题
- 返回其他响应:进一步排查Lambda逻辑问题
5. 查看AWS CloudWatch日志
登录AWS CloudWatch,查看对应Lambda的日志流:
- 无日志记录:说明请求未到达Lambda,问题出在API Gateway的路由或部署环节
- 有日志记录:查看日志中的错误信息,例如签名验证失败、DynamoDB权限不足等
内容的提问来源于stack exchange,提问作者reallymemorable
相关产品推荐
相关产品推荐

