PAYATHON 2026

印度银行 API 如何自动处理 NEFT 付款?

支付小周

结论

可以自动处理,但普通银行账户加上 API token、customer id 和金额,并不足以直接发起 NEFT 转账。

你通常需要开通银行的企业支付 API、Host-to-Host 服务,或者接入具备相应资质的支付服务商。银行可能还会要求预先登记收款人、设置固定出口 IP、使用数字签名、执行双人审批、遵守交易限额,并对回调进行验签。

customer id 只是自定义应用中的内部标识。转账时,系统仍要根据它读取收款人姓名、银行账号、IFSC、金额、业务用途等银行要求的字段。

为什么普通网银接口不够

NEFT 是银行间资金转账网络,并不是任意应用都能直接调用的公共 API。应用必须通过开户银行或合规服务商提供的接口提交付款指令。

具体能做什么,取决于银行和账户类型:

  • 个人账户一般没有自动批量付款 API。
  • 企业往来账户通常可以申请 Corporate Banking API 或 Host-to-Host 接入。
  • 某些银行要求先通过 API 或网银登记 beneficiary,登记完成后才能付款。
  • 有些接口可以全自动执行,有些仍需授权人登录网银审批。
  • 银行可能对单笔金额、单日累计金额及收款人启用时间作出限制。

选择银行或服务商前,需要确认其接口是否支持 NEFT payout,而不是只支持余额和交易流水查询。

推荐的系统流程

1. 开通企业付款能力

向开户银行申请企业 API,并确认:

  • 是否支持 NEFT 转账;
  • 是否支持批量付款;
  • 是否必须提前登记收款人;
  • 是否需要 maker-checker 审批;
  • 认证方式是 OAuth 2.0、API key、mTLS 还是请求签名;
  • 单笔和每日交易限额;
  • 是否提供状态查询和 webhook;
  • 测试环境及生产环境有哪些接入要求。

如果银行没有合适的 API,可以评估提供 payout API 的服务商,但要核实其监管资质、资金流向、结算模式和数据存储要求。

2. 保存并验证客户银行资料

客户资料至少需要包含:

customer_id
account_holder_name
account_number
ifsc
bank_name
verification_status

问题中的 “NEFT code” 通常是指 IFSC,例如 HDFC0001234。实际参数名称应以银行 API 文档为准。

只验证 IFSC 格式还不够。正式付款前,还应通过银行提供的账户验证功能、微额验证或其他合规方式,核对账号和收款人信息。验证通过后保存验证状态,避免后续付款继续使用未经核验的数据。

3. 在服务端创建付款指令

前端只提交 customer_id、金额和业务订单号。其余工作由后端完成:

  1. 验证调用者权限;
  2. 查询客户银行资料;
  3. 确认客户资料已经验证;
  4. 校验金额、币种和付款规则;
  5. 创建唯一的幂等键;
  6. 调用银行付款 API;
  7. 保存银行返回的交易编号和初始状态;
  8. 通过 webhook 或查询接口更新最终结果。

银行 API 凭证不能直接保存在浏览器或移动端。

示例代码

下面的 Node.js 示例只用于说明调用结构。URL、字段名、签名方法和状态值都是占位内容,必须按照所选银行或服务商的真实规范替换。

import crypto from "node:crypto";

async function payCustomer({ customerId, amountPaise, orderId }) {
  const customer = await db.customers.findById(customerId);

  if (!customer) {
    throw new Error("Customer not found");
  }

  if (customer.verificationStatus !== "verified") {
    throw new Error("Beneficiary bank account is not verified");
  }

  if (!Number.isInteger(amountPaise) || amountPaise <= 0) {
    throw new Error("Invalid amount");
  }

  const idempotencyKey = crypto
    .createHash("sha256")
    .update(`neft:${orderId}`)
    .digest("hex");

  const existingPayment = await db.payments.findByIdempotencyKey(
    idempotencyKey
  );

  if (existingPayment) {
    return existingPayment;
  }

  const payment = await db.payments.create({
    customerId,
    orderId,
    amountPaise,
    idempotencyKey,
    status: "created"
  });

  const requestBody = {
    reference_id: payment.id,
    transfer_mode: "NEFT",
    amount: (amountPaise / 100).toFixed(2),
    currency: "INR",
    beneficiary: {
      name: customer.accountHolderName,
      account_number: customer.accountNumber,
      ifsc: customer.ifsc
    }
  };

  const response = await fetch(
    "https://api.example-bank.in/v1/transfers",
    {
      method: "POST",
      headers: {
        "Authorization": `Bearer ${process.env.BANK_API_TOKEN}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey
      },
      body: JSON.stringify(requestBody)
    }
  );

  const result = await response.json();

  if (!response.ok) {
    await db.payments.update(payment.id, {
      status: "submission_failed",
      errorCode: result.code
    });

    throw new Error(`Bank rejected transfer: ${result.code ?? "UNKNOWN"}`);
  }

  return db.payments.update(payment.id, {
    status: "submitted",
    bankReference: result.transfer_id
  });
}

这不是任何银行的官方 SDK 示例。部分银行要求对请求体进行 HMAC 签名、使用客户端证书或加密敏感字段,遇到这些要求时,应严格按照银行文档实现。

正确处理异步状态

API 返回成功,通常只说明银行已经接收请求,并不表示客户已经收到资金。NEFT 付款可能经历以下状态:

created → submitted → processing → successful
                              └──→ failed
                              └──→ returned

应用需要保存银行交易编号,并通过 webhook 或状态查询接口确认最终结果。

Webhook 处理程序必须验证签名,同时能正确处理重复通知:

app.post("/webhooks/bank", async (req, res) => {
  if (!verifyBankSignature(req)) {
    return res.status(401).send("Invalid signature");
  }

  const event = req.body;

  await db.transaction(async (tx) => {
    const alreadyProcessed = await tx.webhookEvents.exists(event.event_id);

    if (alreadyProcessed) {
      return;
    }

    await tx.webhookEvents.insert(event.event_id);

    await tx.payments.updateByBankReference(event.transfer_id, {
      status: mapBankStatus(event.status)
    });
  });

  res.sendStatus(200);
});

发生网络超时后,不要马上创建一笔新转账。应先使用原来的 reference_id 或幂等键查询状态,否则可能造成重复付款。

批量支付给所有客户

批量付款不能只是遍历所有客户,再无限并发调用接口。更稳妥的流程是先创建付款批次,然后将每笔付款放入队列:

创建付款批次
    ↓
检查余额和总金额
    ↓
冻结批次中的客户与金额快照
    ↓
逐笔或按银行批量格式提交
    ↓
接收状态回调
    ↓
对账并处理失败、退回和重试

创建批次前,需要确认每位客户获得相应金额的依据。客户资料中有银行账号,并不代表系统可以自动向其付款。

如果银行支持 batch payout API,可以优先使用批量接口。如果只支持单笔接口,则必须遵守并发数和速率限制。只有已经确认未被受理的交易才能重试。交易状态不明确时,应先查询状态。

安全与合规注意事项

  • API token、客户端私钥和证书只能存放在服务端的密钥管理系统中。
  • 银行账号属于敏感数据,需要加密存储,并限制日志、客服及运营人员的访问权限。
  • 日志不得记录完整账号、token、签名或未脱敏的请求体。
  • 修改收款账户后需要重新验证,也可以设置冷静期或人工复核。
  • 对大额交易、频率异常的交易及新收款账户设置风险控制。
  • 保存业务订单号、内部付款编号、银行交易编号和状态变更记录,以便审计和对账。
  • 根据业务性质确认 KYC、AML、税务、退款和客户授权要求。
  • 不要假设所有 API 都能全天实时到账。具体处理时间、节假日规则和服务窗口,应以开户银行的当前规则为准。

NEFT 并不适用于所有付款场景。对于小额且要求即时到账的付款,可以询问银行是否支持 IMPS 或 UPI。大额交易则可能需要考虑 RTGS。选择哪种方式,要结合金额、到账时效、手续费、限额和银行 API 的实际能力。

自定义应用可以实现这套功能,但重点不是寻找一个通用的 “NEFT API token”,而是取得银行或合规服务商的企业付款接入资格,并处理好收款人验证、幂等控制、异步状态、风险审核和对账。

备注:内容仅供参考。