如何通过 Flutterwave API 使用客户银行卡支付账单?
结论
可以,但通常无法通过一次 Flutterwave API 请求同时完成”从客户银行卡扣款”和”支付账单”。
一般按以下流程处理:
- 创建支付,让客户在 Flutterwave 托管的支付页面完成银行卡付款。
- 在服务端验证交易是否成功。
- 调用 Bills API 支付话费、电费或其他受支持的账单。
Bills API 使用商户的 Flutterwave 可用余额支付账单,并不会直接从客户银行卡扣款。因此,调用 Bills API 前,需要确认收款已经成功,且资金符合账单支付条件。
推荐流程
1. 在服务端创建银行卡支付
通过 POST /v3/payments 创建 Flutterwave 托管支付页面。密钥只能保存在服务端,不应写入 Flutter、网页或其他客户端代码。
const response = await fetch("https://api.flutterwave.com/v3/payments", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FLW_SECRET_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
tx_ref: `bill-${Date.now()}`,
amount: 5000,
currency: "NGN",
redirect_url: "https://example.com/payments/callback",
payment_options: "card",
customer: {
email: "customer@example.com",
name: "Example Customer",
phonenumber: "08012345678"
},
customizations: {
title: "Bill payment",
description: "Payment for customer bill"
}
})
});
const result = await response.json();
if (result.status !== "success" || !result.data?.link) {
throw new Error(result.message || "Unable to create payment");
}
console.log(result.data.link);
客户端打开响应中的 data.link 后,客户可以在 Flutterwave 页面填写银行卡信息,并完成可能需要的 OTP、3-D Secure 或银行授权。
不能只根据跳转页面中的查询参数判定支付成功。
2. 在服务端验证交易
支付完成后,使用 Flutterwave 返回的交易 ID,在服务端调用交易验证接口:
async function verifyTransaction(transactionId, expectedReference) {
const response = await fetch(
`https://api.flutterwave.com/v3/transactions/${encodeURIComponent(transactionId)}/verify`,
{
headers: {
Authorization: `Bearer ${process.env.FLW_SECRET_KEY}`
}
}
);
const result = await response.json();
const transaction = result.data;
const valid =
result.status === "success" &&
transaction?.status === "successful" &&
transaction?.tx_ref === expectedReference &&
transaction?.currency === "NGN" &&
Number(transaction?.amount) >= 5000;
if (!valid) {
throw new Error("Transaction verification failed");
}
return transaction;
}
至少需要核对以下内容:
status是否为successfultx_ref是否与本地订单一致currency是否正确- 实收金额是否达到订单金额
- 该交易是否已经处理过
生产环境还需要接收并验证 Flutterwave webhook。跳转回调可以改善用户体验,但不能代替服务端验证。
3. 调用 Bills API 支付账单
确认收款成功后,再从服务端调用 Bills API。以下代码以尼日利亚的一次性话费充值为例:
async function payAirtimeBill({ phoneNumber, amount, reference }) {
const response = await fetch("https://api.flutterwave.com/v3/bills", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FLW_SECRET_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
country: "NG",
customer: phoneNumber,
amount,
recurrence: "ONCE",
type: "AIRTIME",
reference
})
});
const result = await response.json();
if (!response.ok || result.status !== "success") {
throw new Error(result.message || "Bill payment request failed");
}
return result.data;
}
不同账单类型对 type、客户标识和其他字段的要求可能不同。例如,电费通常需要电表号或账单账户标识。具体参数应以对应国家、账单服务商和当前 Flutterwave Bills API 文档为准,不能将话费充值参数直接用于所有账单类型。
业务代码需要防止重复支付
支付回调和 webhook 都可能重复发送,因此账单支付必须有幂等控制。可以在数据库中维护类似的状态:
PENDING_PAYMENT
PAYMENT_CONFIRMED
BILL_PROCESSING
BILL_SUCCESS
BILL_FAILED
请求只有在成功地将状态从 PAYMENT_CONFIRMED 更新为 BILL_PROCESSING 后,才能调用 Bills API。tx_ref 和账单 reference 都应保持唯一,同时记录 Flutterwave 返回的交易 ID 和账单请求结果。
能否直接提交银行卡号扣款
Flutterwave 也可能提供直接卡片扣款或 tokenized charge 能力,但这类接入通常涉及 PCI DSS、卡数据加密、3-D Secure、OTP,以及不同国家和账户权限的限制。
除非已经确认商户账户具备相应能力并满足合规要求,否则不要让自己的服务器接收或保存以下信息:
- 卡号
- CVV
- PIN
- OTP
- 完整银行卡有效期数据
多数情况下,应优先使用 Flutterwave 托管支付页面,以减少敏感卡数据流经业务系统带来的安全和合规风险。
需要特别注意
- 所有使用 Secret Key 的请求都必须从服务端发起。
- 测试环境和生产环境的密钥、交易记录及余额彼此独立。
- 客户付款成功后,资金不一定会立即成为可用于支付账单的余额,具体取决于商户配置、结算规则和风险审核。
- 账单请求返回”已接收”并不一定代表最终履约成功。如果对应产品提供状态查询接口,还需要继续查询最终状态。
- 如果支付成功但账单支付失败,需要提前设计重试、人工处理或退款机制。
- 不要直接自动重试超时请求。应先查询原请求的状态,以免重复支付同一账单。
- 支持的国家、币种、账单类型和请求字段可能发生调整。上线前,需要根据当前 Flutterwave 官方文档和商户后台权限进行确认。
备注:内容仅供参考。