如何配置Swell自定义结账对接Flutterwave支付网关
Swell 自定义结账对接 Flutterwave 支付网关实现方案
前置准备
- 提前从两个平台后台拿到对应密钥:Swell侧的
Store ID、前端公钥、服务端Secret Key(在Swell后台Settings > API页面生成,记得给密钥开订单、购物车相关的读写权限);Flutterwave侧的公钥、Secret Key、Encryption Key(在Flutterwave后台Settings > API Keys页面获取,测试环境和生产环境的密钥分开存,别混用) - 先在Flutterwave后台配置好两个地址:支付完成后的前端跳转地址、异步回调Webhook地址,Webhook验签密钥单独存好,后面回调校验必须用。
步骤1:开启Swell自定义结账权限
先到Swell后台Settings > Checkout页面,开启Custom checkout开关,允许注入自定义支付逻辑,不要用Swell默认的托管结账页,不然没法加自定义支付网关的逻辑。
前端初始化SDK的时候注意,所有带Secret Key的逻辑全放服务端,前端绝对不能存任何服务端密钥,避免泄露。
// 前端初始化Swell JS SDK示例 import swell from '@swell/js'; swell.init('你的Swell Store ID', '你的Swell前端公钥'); // Flutterwave前端直接用官方Inline JS引入即可,不用装npm包,减少密钥泄露风险
步骤2:支付单生成逻辑对接
用户填完收货地址、选完配送方式确认结账时,先把当前购物车转为Swell侧的待支付订单:
// 前端提交购物车生成待支付订单 const submitCart = async () => { // 先校验购物车商品库存、配送信息是否完整 await swell.cart.validate(); // 提交购物车生成待支付订单,订单状态默认是payment_pending const pendingOrder = await swell.cart.submitOrder({ $extra: { payment_channel: 'flutterwave' // 加自定义字段标记支付渠道,方便后续对账 } }); return pendingOrder; }
拿到Swell返回的待支付订单数据(订单ID、总金额、结算币种、用户收货信息)之后,调用你自己的服务端接口,生成Flutterwave支付链接。
关键注意点:生成Flutterwave支付单的时候,直接把Swell的订单ID存在
tx_ref字段里,不要自己生成随机交易号再做映射,后续回调对账的时候直接拿这个字段找对应Swell订单就行,能少写很多关联逻辑,也不容易出错。
服务端生成Flutterwave支付链接的示例代码:
// 服务端Node.js逻辑示例 const Flutterwave = require('flutterwave-node-v3'); const flw = new Flutterwave( process.env.FLW_PUBLIC_KEY, process.env.FLW_SECRET_KEY ); const generateFlwPayLink = async (swellOrder) => { const payPayload = { tx_ref: swellOrder.id, // 直接绑定Swell订单ID amount: swellOrder.amount_total, currency: swellOrder.currency, redirect_url: "你的店铺结账结果页地址", customer: { email: swellOrder.account?.email || swellOrder.checkout_email, name: `${swellOrder.shipping.first_name} ${swellOrder.shipping.last_name}`, phonenumber: swellOrder.shipping.phone }, customizations: { title: "你的店铺名称", description: "商品订单支付" } } const flwRes = await flw.Charge.card(payPayload); return flwRes.data.link; // 返回给前端,让用户跳转到Flutterwave支付页 }
步骤3:支付结果回调与订单状态同步
这块必须做双层校验,不能只信前端跳转带回来的结果,必须结合Flutterwave的异步Webhook做最终状态确认,避免掉单。
- 前端跳转回调处理:用户完成支付从Flutterwave跳回你的结果页时,拿URL里的
transaction_id传给服务端,调用Flutterwave的交易查询接口,确认这笔交易的实际状态 - Webhook异步回调处理:Flutterwave会在支付状态变更时给你提前配置的Webhook地址发POST请求,收到请求第一步先用存好的Encryption Key验签,确认请求是Flutterwave官方发的,避免恶意请求伪造支付成功状态。验签通过后,从请求体里取
tx_ref(也就是之前绑定的Swell订单ID),再调用Swell服务端API更新订单状态:
// 服务端更新Swell订单为已支付示例 const swell = require('swell-node'); swell.init(process.env.SWELL_STORE_ID, process.env.SWELL_SECRET_KEY); const markOrderPaid = async (swellOrderId, flwTransactionId) => { // 先查订单当前状态,避免重复更新 const existOrder = await swell.get('/orders/{id}', { id: swellOrderId }); if (existOrder.status !== 'payment_pending') return; // 已经处理过的订单直接跳过 // 更新订单状态为已支付,记录Flutterwave交易号 await swell.put(`/orders/${swellOrderId}`, { status: 'paid', paid: true, payment_transaction_id: flwTransactionId, date_paid: new Date().toISOString() }) // 触发Swell默认的订单履约、下单成功邮件发送、库存扣减流程 await swell.post('/orders/{id}/authorize', { id: swellOrderId }) }
注意:Flutterwave的Webhook可能会重复推送同一条交易结果,所以必须加订单状态判断,重复处理会导致重复发货、重复发邮件的问题。另外Webhook接口必须在10秒内返回200状态码,不然Flutterwave会持续重试推送,如果后续逻辑耗时长,可以先返回200,把逻辑丢到异步队列里慢慢处理
常见问题排查
- 支付成功但订单没更新:先查Webhook日志,看是不是验签失败,或者你的服务端接口超时没返回200导致Flutterwave没推送成功
- 金额不匹配报错:确认Swell后台设置的结算币种和传给Flutterwave的currency参数完全一致,包含大小写,比如
USD不能写成usd - 测试流程:上线前先用Flutterwave提供的测试卡号跑通全流程,确认库存扣减、订单状态流转、邮件通知都正常之后,再切换成生产密钥
- 退款对接:后续要做退款的话,服务端直接调用Flutterwave的退款接口,退款成功后同步调用Swell的订单退款接口更新状态即可,不需要额外改前端逻辑
内容的提问来源于stack exchange,提问作者Dev Star
相关产品推荐
相关产品推荐

