跳至正文

Webhook

资金一动,即刻知晓

收款、付款、虚拟账户、订阅、换汇和电汇的实时通知,每一条都以 HMAC-SHA256 签名,让您在处理前验证每一个请求。

  • HMAC-SHA256 签名
  • 指数退避重试
  • 按国家和账户配置

配置

在 Business Portal 中配置通知

登记端点无需编写代码。每个条目都可以按国家和账户限定范围。

  1. 01

    打开通知设置

    在 Business Portal 中进入 Configurations → Clients → Notifications。

  2. 02

    添加 HTTPS URL

    登记您服务器上用于接收事件的公网 HTTPS 端点。

  3. 03

    限定范围

    选择国家(或 All)以及通知所属的账户。

  4. 04

    添加请求头并测试

    可按需为您的网关添加自定义请求头,然后在 Stage 发起一笔测试交易。

载荷

所有事件共用一个信封结构

所有通知都共享相同的根字段,一个处理程序即可完成路由。请求体为 JSON,以 Content-Type text/plain 发送。

  • transactionType:PayIn、PayOut、VirtualAccount、Subscription、CurrencyExchange、WireIn 或 WireOut
  • transactionFlow:处理流程,便于路由
  • data:交易本身,包含 externalId、internalId 和 status

事件

您会收到哪些通知

  • 银行卡收款

    transactionType
    PayIn
    通知内容
    授权成功、完成、拒绝、退款或拒付
  • 银行转账及本地支付方式收款

    transactionType
    PayIn
    通知内容
    付款人在银行或钱包应用中确认后的最终状态
  • 虚拟账户

    transactionType
    VirtualAccount
    通知内容
    账户创建,以及每笔到账
  • 订阅

    transactionType
    Subscription
    通知内容
    订阅生命周期;每次扣款还会以 PayIn 事件单独通知
  • 付款

    transactionType
    PayOut
    通知内容
    付款流程中直至完成的各个状态变化
  • 换汇

    transactionType
    CurrencyExchange
    通知内容
    您的余额之间换汇的结果
  • 电汇入账(Wire In)

    transactionType
    WireIn
    通知内容
    资金已入账,可为法币或 USDC/USDT
  • 电汇出账(Wire Out)

    transactionType
    WireOut
    通知内容
    提现已发出;加密货币出账包含区块链交易哈希

各事件的完整结构和状态码见 docs.localpayment.com 的 Webhook 参考。

安全

验证每一个签名

每个请求都带有 x-Signature: HMAC-SHA256 加原始请求体的十六进制摘要,由您的 Webhook 密钥计算得出。请重新计算并比对,然后再信任载荷中的任何内容。

  • 读取原始请求体:它是以 Content-Type text/plain 发送的 JSON
  • 去掉 “HMAC-SHA256 ” 前缀,再用恒定时间函数比较
  • 签名不符时返回 401,切勿记录完整载荷
webhook.js
// 用 Express 接收 Localpayment Webhook(Node.js 18+)。
import crypto from 'node:crypto';
import express from 'express';
import { jobQueue } from './queue.js'; // 您自己的异步任务队列

const app = express();

// 启动时从密钥管理服务读取 Webhook 密钥,切勿硬编码。
const WEBHOOK_SECRET = process.env.LP_WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) throw new Error('LP_WEBHOOK_SECRET is not configured');

function hasValidSignature(rawBody, header) {
  if (typeof rawBody !== 'string') return false;
  // 请求头格式:"x-Signature: HMAC-SHA256 <hex>",先去掉前缀。
  const received = String(header ?? '').replace(/^HMAC-SHA256\s+/i, '').trim();
  if (!/^[0-9a-f]{64}$/i.test(received)) return false;

  const expected = crypto.createHmac('sha256', WEBHOOK_SECRET).update(rawBody, 'utf8').digest();
  return crypto.timingSafeEqual(Buffer.from(received, 'hex'), expected);
}

// 请求体是以 Content-Type text/plain 发送的 JSON:计算 HMAC 时请使用原始文本。
app.post('/webhooks/localpayment', express.text({ type: '*/*', limit: '1mb' }), (req, res) => {
  if (!hasValidSignature(req.body, req.get('x-signature'))) return res.sendStatus(401);

  let event;
  try {
    event = JSON.parse(req.body);
  } catch {
    return res.sendStatus(400);
  }

  res.sendStatus(200); // 先快速确认,再做耗时处理
  // 异步处理。按 internalId/externalId + 状态去重,
  // 确保重试投递只生效一次。切勿记录完整载荷。
  jobQueue.enqueue(event);
});

可靠处理

为重试和真实网络环境而设计

  • 快速确认

    签名验证通过后立即返回 200,耗时处理放到后台任务中完成。

  • 预期会有重试

    如果没有收到 200,我们会按指数退避重试。您的处理程序必须能安全地多次执行。

  • 去重

    以 internalId 或 externalId 加状态作为处理键,确保重试投递只生效一次。

  • 不要假设顺序

    应用前先将收到的状态与已存储的状态比较,绝不让交易状态倒退。

  • 通过 API 确认

    如有疑问,调用 GET /api/transactions/{externalId}/status 查询交易的当前状态。

  • 安全记录日志

    只记录事件类型、ID 和状态,绝不记录完整载荷、令牌或个人数据。

运营

在 Business Portal 中查看每次投递

通知报告会显示向您的端点发送了哪些内容,支持和工程团队无需翻查服务器日志即可定位问题。

  • 发送到您端点的通知投递记录
  • 交易搜索和详细的活动报告
  • Webhook、IP 白名单和用户集中管理

Webhook 常见问题

如何确认 Webhook 确实来自 Localpayment?

验证 x-Signature 请求头:用您的 Webhook 密钥重新计算原始请求体的 HMAC-SHA256,并用恒定时间函数进行比较。您还可以为每个通知条目添加自定义请求头,供网关使用。

投递失败后多久重试一次?

未收到 200 的投递会按指数退避重试。请按多次投递来设计处理程序,并在 Business Portal 的通知报告中查看投递历史。

为什么 Content-Type 是 text/plain?

请求体是以 text/plain 发送的 JSON。请按原始文本读取,基于这些原始字节验证签名,然后再解析。

可以把不同国家的通知发到不同端点吗?

可以。每个通知条目都按国家(或 All)和账户限定范围,您可以把不同市场或业务线路由到不同的 URL。

我的端点应该返回什么?

尽快返回 200 状态,并在任何耗时处理之前返回。未收到 200 时,该次投递视为失败并会重试。

接入您的第一个 Webhook

配置一个 Stage 端点,发起一笔测试收款,端到端验证签名。

我们能为您做些什么?

下载完整版

留下您的信息,即可立即获取完整版。