如何在Node.js中实现基于Stripe Checkout的多关联账户付款?
多关联账户的Stripe Checkout Session付款改造方案
问题背景
购物车包含来自不同卖家的商品,需向多个已关联主账户的卖家付款。此前单卖家场景通过Checkout Session结合payment_intent_data.transfer_data实现付款,但该方式无法直接支持多卖家,需要调整逻辑适配多账户转账需求。
核心思路
Stripe Checkout Session的payment_intent_data.transfer_data仅支持指定单个收款账户,因此需结合Transfer Group和Webhook异步处理:
- 创建Checkout Session时指定统一的
transfer_group,不设置单个收款账户; - 监听付款成功的Webhook事件(
checkout.session.completed),在事件触发后批量向每个卖家发起转账,所有转账关联同一个transfer_group。
改造后的代码实现
1. 创建支持多卖家的Checkout Session
const cart = await Cart.findById(record.cartId); const currency = 'usd'; // 计算购物车总金额 const totalAmount = cart.prices.reduce((sum, price) => sum + price, 0); // 计算平台总手续费(示例为总金额的10%) const totalPlatformFee = Math.ceil(totalAmount * 10); const session = await stripe.checkout.sessions.create({ mode: "payment", // 生成购物车所有商品的line_items,显示卖家和商品信息 line_items: await Promise.all(cart.items.map(async (item, index) => { const partner = await Partner.findById(item); const seller = await Seller.findOne({ user: partner.user }); return { price_data: { currency: currency, product_data: { name: `${seller.shopName} - ${item.productName}`, }, unit_amount: Math.ceil(cart.prices[index] * 100), }, quantity: 1, }; })), // 设置统一转账组ID,关联后续所有转账操作 transfer_group: record._id, payment_intent_data: { application_fee_amount: totalPlatformFee, // 不设置transfer_data.destination,避免指定单一收款方 }, success_url: `${domainURL}/payment/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${domainURL}/payment/cancelled`, }); return res.json({ url: session.url });
2. 监听付款成功事件并批量转账
通过Stripe Webhook监听checkout.session.completed事件,确保仅在付款成功后执行转账:
app.post('/webhook/stripe', express.raw({ type: 'application/json' }), async (req, res) => { const sig = req.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET); } catch (err) { return res.status(400).send(`Webhook Error: ${err.message}`); } if (event.type === 'checkout.session.completed') { const session = event.data.object; const transferGroup = session.transfer_group; // 根据转账组ID获取对应的订单和购物车信息 const orderRecord = await YourOrderModel.findOne({ _id: transferGroup }); const cart = await Cart.findById(orderRecord.cartId); const currency = 'usd'; const transfers = []; for (let i = 0; i < cart.items.length; i++) { const item = cart.items[i]; const partner = await Partner.findById(item); const seller = await Seller.findOne({ user: partner.user }); const stripeDestination = seller.stripe_account.id; const itemPrice = cart.prices[i]; // 向当前卖家发起转账 const transfer = await stripe.transfers.create({ amount: Math.ceil(itemPrice * 100), currency: currency, destination: stripeDestination, transfer_group: transferGroup, }); transfers.push(transfer.id); } // 将转账记录保存到订单中,便于后续对账 await YourOrderModel.findByIdAndUpdate(transferGroup, { completedTransfers: transfers }); } res.json({ received: true }); });
关键注意事项
- Transfer Group的作用:将Payment Intent与后续所有转账绑定,Stripe会确保只有当付款成功后,关联的转账才会被执行,避免付款失败却发起转账的风险。
- 异步转账的必要性:Checkout Session仅负责发起付款流程,实际付款成功是异步事件,必须通过Webhook触发转账,不能在创建Session后立即执行。
- 手续费灵活配置:示例中设置了总平台手续费,若需按单个商品抽成,可在创建每个
transfer时单独指定application_fee_amount,替代Payment Intent中的总手续费设置。
内容的提问来源于stack exchange,提问作者Mad Devil Coder
相关产品推荐
相关产品推荐

