跳至内容
文档
本页内容

集成指南

开发者文档

创建账单,将客户引导至付款页面,付款完成后接收已签名的 webhook。请从以下四个步骤开始。

  1. 复制项目的 API 密钥。
  2. 为写请求签名。
  3. 创建第一张账单。
  4. 验证付款 webhook。

支持范围

网关接受 USDT 和 USDC。此表与 GET /v1/services 一致,核对日期为 2026年11月4日。构建支付方式选择器前,请先读取实时目录。

资产网络确认数
USDTTron20
USDTEthereum12
USDTSolana32
USDTPolygon8
USDTArbitrum12
USDTTON5
USDCEthereum12
USDCSolana32
USDCPolygon8
USDCBase12
USDCArbitrum12

付款为托管模式:资金进入平台控制的地址。您的商户余额是应付给您的金额。独立的 Telegram 钱包为非托管模式。

限制

  • 出款和退款尚未上线:GET /v1/services 将 boundaries.livePayouts 和 boundaries.liveRefunds 报告为 false。
  • 入账付款未经过 KYT 服务商筛查。
  • 费用超过金额 20% 的账单会被拒绝,并返回 AMOUNT_BELOW_MINIMUM。
  • 不提供合同约定的 SLA。

起始费率为 0.40%,另加网络费用。请申请费用报价以获取总额和最低金额;参见价格。

身份验证

服务器间调用使用 x-api-key。项目密钥仅限单个商户项目使用。请将其保存在您的服务器上。

测试密钥以 ncp_test_… 开头;正式密钥以 ncp_live_… 开头。较早签发的密钥以 ncp_sandbox_ 开头,仍可继续使用。

curl -sS "https://api.veilnebu.la/v1/balances" \
  -H "x-api-key: $PROJECT_API_KEY"

为写请求签名

GET 请求只需 x-api-key。每个使用项目密钥认证的 POST、PUT、PATCH 和 DELETE 请求还需要两个请求头,否则 API 会返回 401 Invalid API signature:

  • x-ncp-timestamp:ISO-8601 格式,与当前时间相差不超过 5 分钟。
  • x-ncp-signature:hex(HMAC-SHA256(your API key, "<timestamp>.<METHOD>.<path incl. query>.<raw body>"))。

方法名使用大写。对实际发送的字节进行签名,包括路径中的查询字符串。没有请求体的请求对空字符串签名。下面的辅助工具需要您的项目密钥;shell 辅助脚本需要 Bash、curl 和 OpenSSL,Node.js 辅助脚本使用原生 fetch。

export API_BASE_URL="https://api.veilnebu.la"
export PROJECT_API_KEY="ncp_test_…"

ncp() {
  local method="$1" path="$2" body="" ts sig
  shift 2
  if [ "$#" -gt 0 ]; then body="$1"; shift; fi
  ts=$(date -u +%Y-%m-%dT%H:%M:%S.000Z) || return
  sig=$(printf '%s.%s.%s.%s' "$ts" "$method" "$path" "$body" |
    openssl dgst -sha256 -hmac "$PROJECT_API_KEY" -hex) || return
  sig=${sig##* }
  if [ -n "$body" ]; then
    curl -sS --request "$method" "$API_BASE_URL$path" \
      -H "x-api-key: $PROJECT_API_KEY" \
      -H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" \
      -H "content-type: application/json" --data-binary "$body" "$@"
  else
    curl -sS --request "$method" "$API_BASE_URL$path" \
      -H "x-api-key: $PROJECT_API_KEY" \
      -H "x-ncp-timestamp: $ts" -H "x-ncp-signature: $sig" "$@"
  fi
}

创建第一张账单

在控制台中创建项目,复制其 API 密钥,然后创建账单。响应中包含 checkoutUrl:将客户引导至该地址。

ncp POST /v1/payments '{
  "amountMinor":"10000",
  "currencyCode":"USD",
  "settlementAssetCode":"USDT",
  "externalId":"order-1001"
}' -H "idempotency-key: order-1001"

响应节选。每张账单的 ID 都会单独生成。

{
  "id": "cm3k9x2f40001qz8e7h5v1abc",
  "status": "new",
  "amountMinor": "10000",
  "currencyCode": "USD",
  "settlementAssetCode": "USDT",
  "checkoutUrl": "https://app.veilnebu.la/checkout/cm3k9x2f40001qz8e7h5v1abc"
}

amountMinor 是以该币种最小单位表示的整数字符串:10000 = 100.00 USD。十进制的 amount 字段仅被浏览器端接口 /v1/public/payments 接受。

浏览器端付款页面与允许的来源

在控制台中创建可公开密钥,并列出您店铺实际使用的来源。浏览器请求将其作为 x-publishable-key 发送到 POST /v1/public/payments。

Origin、successUrl 和 cancelUrl 都必须在该密钥的允许来源列表中。来源由协议、主机和可选端口组成,不含路径或通配符。缺失或未列出的来源会被拒绝。

十进制金额对法币最多接受两位小数,对加密货币最多接受该资产自身的小数位数(USDT 和 USDC:在我们目前支持的所有网络上均为 6 位)。

const response = await fetch('https://api.veilnebu.la/v1/public/payments', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'x-publishable-key': 'pk_test_…',
  },
  body: JSON.stringify({
    amount: '100.00',
    currencyCode: 'USD',
    settlementAssetCode: 'USDT',
    successUrl: 'https://shop.example/thanks',
    cancelUrl: 'https://shop.example/cart',
  }),
});
if (!response.ok) throw new Error(await response.text());
const invoice = await response.json();
window.location.assign(invoice.checkoutUrl);

付款状态

状态下一步操作
new分享付款链接
pending等待付款
confirming等待确认
paid可以结算
partially_paid请客户完成付款
overpaid核查多入账的金额
expired创建新账单
cancelled无需操作
failed查看详情
refunded无需操作

幂等键

创建账单时,请发送由订单 ID 生成的固定 Idempotency-Key。使用相同的键和请求体重试会返回原账单。用同一个键发送不同的请求体会返回 409。超时后重试时,请保持键和请求体不变。

请求示例请参见第一张账单示例。

错误

错误以 JSON 格式返回。message 是一个字符串;对于校验错误,则是字符串数组。

{
  "statusCode": 401,
  "error": "Unauthorized",
  "message": "Invalid API key"
}
HTTP 状态码含义与处理
400字段无效;message 会指出具体字段。请先修正再重试。
401Invalid API key 或 Invalid API signature。请检查密钥、签名的字节和时间戳。
404Payment invoice not found。请检查 ID 和项目。
409相同的 Idempotency-Key 使用了不同的请求体,或 Insufficient merchant payable balance。
429请求过于频繁,已被限流。请稍候再重试。
5xx使用相同的 Idempotency-Key 和请求体重试。

Webhooks(HMAC-SHA256)

每次投递都带有 x-nebula-signature: t=<unix>,v1=<hex>。请使用创建端点时返回的端点密钥验证 HMAC。拒绝与当前时间相差超过 300 秒的时间戳。

在 Node.js 中验证

Node.js 验证器仅使用内置的 crypto 模块。将签名请求头、原始请求体字节和端点密钥传入即可。

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(header, rawBody, secret) {
  const parts = {};
  for (const piece of (header ?? '').split(',')) {
    const separator = piece.indexOf('=');
    if (separator <= 0) continue;
    parts[piece.slice(0, separator).trim()] = piece.slice(separator + 1).trim();
  }
  if (!/^[0-9]{1,16}$/.test(parts.t ?? '')) return false;
  if (!/^[0-9a-f]{64}$/.test(parts.v1 ?? '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = createHmac('sha256', secret)
    .update(parts.t + '.')
    .update(rawBody)
    .digest('hex');
  if (expected.length !== parts.v1.length) return false;
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

请在解析 JSON 之前,基于原始请求体进行验证。投递至少发生一次:请按 x-nebula-event-id 去重,它与载荷中的 eventId 一致。自动重试保持投递 ID 不变;手动重新发送会生成新的投递 ID,但事件 ID 相同。请在返回 2xx 之前持久化该事件。

在本地测试 webhook

Webhook URL 必须使用公网主机。本地和私有地址会被拒绝。请通过公网 HTTPS 隧道暴露您的接收端,然后使用签名辅助工具注册其 URL。

ncp POST /v1/webhooks/endpoints '{
  "url":"https://receiver.example/webhook",
  "events":["payment.paid","payment.reorged"]
}'

将 URL 替换为您的接收端地址。请保存创建时返回的端点密钥。

事件类型

出款和退款尚未上线(GET /v1/services 将 boundaries.livePayouts 报告为 false)。在此之前,出款和批量出款仅在您的余额中记录转账;payout.completed 和 refund.completed 并不表示资金已转出。

下方目录与 GET /v1/services 一致。目录中有某个条目,并不表示相应的资金流动已上线。

事件含义
webhook.test为某个 webhook 端点请求的测试投递。
payment.pending客户已选择支付方式,等待首笔入账。
payment.confirming已检测到入账,正在等待所需的确认数。
payment.paid账单已全额付清,并已在账本中结算。
payment.partially_paid收到的金额少于请求金额,等待剩余部分。
payment.overpaid收到的金额多于请求金额,已连同超付部分在账本中结算。
payment.expired账单在资金不足的情况下已过期。
payment.cancelled账单在付款前已取消。
payment.failed由于链上、费用策略或观测错误,付款失败。
payment.risk_flagged针对已入账存款的提示性风险事件。入账 KYT 筛查尚未上线。
payment.reorged一笔已确认的存款因链重组而被移除;账单不再处于已付状态,请暂停或撤销履约。
payment.unsupported_asset存入了不受支持的代币,未予入账。
payout.processing出款处理中状态。实际转账尚未开放。
payout.completed出款已记录在余额中;这并不表示资金已转出。
payout.failed出款失败。
payout.cancelled出款在完成前已取消。
refund.processing退款处理中状态。实际退款尚未开放。
refund.completed退款已记录在余额中;这并不表示资金已转出。
refund.failed退款失败。
internal_transfer.sent商户间余额转账已为发送方提交。
internal_transfer.received商户间余额转账已计入接收方。
static_wallet.created已为客户创建一个长期有效的充值地址。
static_wallet.blocked某个静态钱包已被冻结。
static_wallet.unblocked某个静态钱包已解除冻结。
static_wallet.topup.completed已检测到一笔静态钱包存款,并已在账本中结算。
dispute.opened针对某张账单发起了争议。
dispute.resolved争议已解决。仅凭解决结果并不能证明已发生链上退款。
dispute.rejected争议已被驳回;商户无需采取任何操作。
subscription.invoice_created一个计费周期生成了新账单。

重试策略

如果您的端点未返回 2xx,我们总共会尝试 8 次:首次尝试,之后分别在 1 分钟、5 分钟、30 分钟、2 小时、6 小时、12 小时和 24 小时后重试。每次延迟从上一次失败的尝试之后开始计算。网络错误和超时也计为失败。测试投递只尝试一次。

最后一次失败后,该投递被标记为失败;可使用 POST /v1/webhooks/deliveries/{deliveryId}/resend 重新发送。作为备用方案,可通过 GET /v1/payments/{id} 进行核对。

出款和退款尚未上线(GET /v1/services 将 boundaries.livePayouts 报告为 false)。在此之前,出款和批量出款仅在您的余额中记录转账;payout.completed 和 refund.completed 并不表示资金已转出。

批量出款

批次用于对转账记录进行分组。其审批策略和所需审批数分别以 approvalPolicy 和 approvalsRequired 返回。存在多名审批人时,创建者不能审批自己的批次。唯一符合条件的操作员可以审批自己在会话中创建的批次。请在控制台中使用 WebAuthn 审批批次。

export PROJECT_API_KEY="ncp_test_…"
ncp POST /v1/payout-batches '{
  "assetCode":"USDT",
  "networkCode":"TRON",
  "externalId":"batch-1001",
  "items":[{
    "destinationAddress":"T…",
    "amountMinor":"15000000",
    "externalRef":"recipient-1001"
  }]
}' -H "idempotency-key: batch-1001"

请将收款地址替换为您自己测试项目中的有效地址。

周期性计费

订阅会在每个计费周期创建一张新账单,并通过 subscription.invoice_created 通知。它不会自动从客户钱包扣款。请将新的付款页面 URL 发送给客户。

interval:DAY、WEEK、MONTH 或 YEAR。

ncp POST /v1/subscriptions '{
  "customerRef":"customer-42",
  "assetCode":"USDT",
  "networkCode":"TRON",
  "amountMinor":"9000000",
  "interval":"MONTH",
  "intervalCount":1
}'

可使用API 参考中的生命周期操作来暂停、恢复或取消订阅。

风险筛查

出款和退款的收款地址会在创建转账前与公开的制裁名单进行比对;一旦匹配,请求将被拒绝。入账付款不经过筛查。

反洗钱与制裁政策

Telegram 商户机器人

@veilnebula_bot 一次只能关联一个项目。请在私聊中使用该项目的 API 密钥完成关联。机器人会尝试删除您的消息,并对存储的密钥进行加密。如果删除失败,请吊销并轮换该密钥。切勿在群聊中发布密钥。

通知仅限于已关联的项目和当前有效的密钥。如需审批出款,请打开控制台。

/link <API_KEY>
/balance
/invoices
/notify on
/unlink
集成指南 · VeilNebula