PAYATHON 2026

如何通过 Flutterwave API 使用客户银行卡支付账单?

支付小周

结论

可以,但通常无法通过一次 Flutterwave API 请求同时完成”从客户银行卡扣款”和”支付账单”。

一般按以下流程处理:

  1. 创建支付,让客户在 Flutterwave 托管的支付页面完成银行卡付款。
  2. 在服务端验证交易是否成功。
  3. 调用 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 是否为 successful
  • tx_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 官方文档和商户后台权限进行确认。

备注:内容仅供参考。