关于 inflow-payments
inflow-payments 是将 InFlow 稳定币支付接入后端服务、应用、CLI、Agent 或工作进程的技能,不绑定特定框架或语言。它涵盖消费者识别、付款请求和状态确认,并要求 API 密钥仅保存在服务器端。
主要功能
- 01搜索、注册和识别消费者
- 02创建稳定币付款请求
- 03配置预授权支付策略
- 04通过 Webhook 或轮询确认状态
- 05读取余额、交易和钱包地址
- 06限定 API 密钥仅用于后端
适用场景
- 01为应用或服务添加 InFlow 结账
- 02为 Agent 设置无交互策略支付
- 03处理支付状态通知并关联业务订单
- 04在批处理任务中轮询付款状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nodeops-app/ skills --skill inflow-payments安装后在 Claude Code 中输入/inflow-payments确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api-reference.mdflows.mdoverview.md详细介绍
InFlow 支付集成
InFlow 是一个稳定币支付平台。卖家(商户、Agent)向消费者发起付款请求;消费者通过手机、电子邮件或短信批准;结算在链上进行。消费者可以预先配置策略,在预算范围内自动批准请求,从而让自主 Agent 实现零点击流程。
此技能可将 InFlow 集成到任何项目中,包括后端服务、全栈 Web 应用、CLI、自主 Agent 或工作进程,不强制使用特定框架或语言。**重要:**集成本身始终是后端工作。浏览器代码只用于可选的 inflow.js 弹窗(仅用于 UX);所有对 InFlow 的 REST 调用以及 INFLOW_API_KEY 都只能放在服务器上。具体规则以及如何处理尚无后端的项目,请参阅「硬性后端门槛」部分。
InFlow 支持的功能
| 功能 | 端点 | 用例 |
|---|---|---|
| 用户搜索 | POST /v1/users/search | 通过电子邮件、手机号码或用户名查找消费者 |
| 用户注册 | POST /v1/requests/register | 为新消费者办理注册(需线下批准) |
| 登录 | POST /v1/requests/login | 验证消费者身份(需线下批准) |
| 支付 | POST /v1/requests/payment | 请求消费者支付稳定币 |
| 策略 | POST /v1/policies、POST /v1/requests/policy | 为无头/零点击流程预先批准支出规则 |
| Webhooks | 向你的端点发送入站 POST 请求 | 接收交易/批准状态变更 |
| 轮询备用方案 | GET /v1/requests/{requestId} | 在没有 Webhooks 时检查状态 |
| 钱包读取 | GET /v1/balances、GET /v1/transactions | 读取余额和交易历史 |
| 加密钱包 | GET/POST /v1/deposit-addresses、/v1/withdrawal-addresses | 管理链上钱包地址 |
| Agentic 用户 | POST /v1/users/agentic | 创建程序化账户并获取 API 密钥 |
货币:USDC、USDT、EURC、PYUSD(仅限稳定币,不支持法币)
区块链:APTOS、BASE、SOLANA、WORLD
此技能不支持的功能
以下功能未通过 InFlow 公共 API 提供。如果用户提出相关需求,请停止并告知他们暂不支持,不要尝试变通方案:
- 市场分账——
PaymentRequest没有splits[]字段。向多个收款方付款需要分别顺序发起多个请求,且无法保证原子性。 - 创建退款——交易状态可以是
REFUNDED,但没有用于发起退款的 API。退款只能通过控制台或线下方式处理。 - 通过 API 注册 Webhook URL——Webhook URL 需通过 InFlow 控制台配置。没有可供程序化注册的
POST /v1/webhooks。 - 卖家的 OAuth 流程——尽管部分旧版文档提及「OAuth」,消费者身份验证完全由 SDK 处理。集成卖家无需执行 OAuth 流程。
默认值与决策树
用户未指定时,使用以下默认值:
| 问题 | 默认值 | 原因 |
|---|---|---|
| 货币 | USDC | 持有者最多的稳定币 |
userDetails 字段 | ["EMAIL"] | 必填字段;电子邮件是最低限度且有用的标识符 |
| 显示模式 | 见下方决策 | 取决于是否涉及浏览器 |
| 生产环境还是沙盒 | 用户确认前使用沙盒 SDK URL | 生产环境 SDK URL 尚未记录 |
显示模式: 如果付款时消费者正在与浏览器交互(且页面可以加载 inflow.js),则使用 FULL。其他情况均使用 HEADLESS:服务器到服务器、自主 Agent、定时任务、AI 工作进程。
Webhook 还是轮询: 生产环境使用 Webhook。仅在本地开发、脚本运行或没有公共 HTTPS 端点的情况下使用轮询。
用户识别: 付款必须关联到已知的 userId。Agent 必须执行以下操作之一:
- 向用户获取
userId(如果用户已知晓该 ID),或者 - 通过
POST /v1/users/search按电子邮件/手机号码/用户名搜索,或者 - 如果搜索返回 404,则注册新的消费者
始终先识别用户,再创建付款请求。
🛑 后端硬性门槛(编写任何代码前请阅读)
InFlow 集成始终属于后端工作。INFLOW_API_KEY 是商户密钥,绝不能泄露到浏览器。不存在例外、演示模式或“暂时这样做”的变通方案。
强制门槛
在编写任何 InFlow HTTP 调用前,你必须能够指出唯一一个仅供服务器使用的接口,并在其中设置 X-API-Key。可接受的接口包括:
- Express / Fastify / Koa / Hono / Hapi 路由处理程序
- Next.js Route Handler(
app/api/.../route.ts)或 Server Action——不能是 Client Component - SvelteKit
+server.ts/+page.server.ts——不能是+page.svelte组件 - Nuxt
server/api/*.ts/server/routes/*.ts——不能是 Vue 组件或组合式函数 - Remix
loader/action——不能是路由组件主体 - 无服务器函数:Vercel Function、Netlify Function、Cloudflare Worker、AWS Lambda 等
- Python / Go / Rust / .NET / Java HTTP 服务
- 独立后端脚本或工作进程(不涉及浏览器)
如果项目没有此类接口——例如纯 Vite + React SPA、静态网站、Storybook 配置或 Chrome 扩展弹出窗口——请停止。告知用户他们需要一个后端接口来存放 API 密钥,即使它只是单文件无服务器函数或 30 行的 Express 服务器。提出为其搭建脚手架。在用户同意且后端接口就绪之前,不要继续。
特定框架中的陷阱(密钥泄露机制)
以下是商家密钥泄露到浏览器中的真实常见方式。全部拒绝。
Next.js:
- 在
'use client'组件中引用process.env.INFLOW_API_KEY,运行时会得到undefined——Next.js 不会将非NEXT_PUBLIC_环境变量打包到客户端代码中。这个问题会表现为请求失败且没有认证标头。 - 灾难发生在 Agent 试图通过将变量重命名为
NEXT_PUBLIC_INFLOW_API_KEY来“修复”这个undefined时。这个前缀会让打包工具将值内联到客户端包中 → 密钥现在会发送给每一位访客。 - 绝不要给
INFLOW_API_KEY加上NEXT_PUBLIC_前缀。 正确的修复方法是将调用移到服务端组件、路由处理程序(app/api/.../route.ts)或服务端操作中——绝不能公开密钥。
Vite(React、Vue、Svelte、Solid 等):
import.meta.env.INFLOW_API_KEY会得到undefined,除非添加VITE_前缀。有些 Agent 会试图通过将其重命名为VITE_INFLOW_API_KEY来“修复”问题 → Vite 随后会将密钥内联到客户端包中,并发送给每一位访客。- 绝不要给
INFLOW_API_KEY加上VITE_前缀。 Vite 本身没有服务端运行时。如果需要后端,用户必须自行添加(Express、Fastify,或搭配无服务器函数)。
SvelteKit:
- 组件和
+page.svelte文件会在浏览器中运行。请使用+server.ts、+page.server.ts或$lib/server/*(打包工具会强制将server目录限定为仅在服务端使用)。
Nuxt:
- Vue 组件和组合式函数会在浏览器中运行。请使用
server/api/*路由处理程序或server/utils/*。绝不要在.vue文件的<script setup>块中读取INFLOW_API_KEY。
Astro / Remix / Qwik / Solid Start:
- 这些框架都有服务端/客户端分界。规则相同:只能在框架的服务端边界(loader、action、服务端路由等)中读取
INFLOW_API_KEY,绝不能在组件代码中读取。
“只是演示”的陷阱
如果用户说 “这只是演示”、“只是测试用”、“我们之后再添加后端”、“暂时先这样” 或类似的话,规则仍然适用。嵌入的商家密钥会通过以下途径泄露:
- Git 历史记录(尤其是在通过
git push --force“修复”之后) - 浏览器开发者工具(任何人都能打开)
- 浏览器扩展(它们会读取你的脚本)
- 在工单中分享的错误报告/崩溃转储
- 发布在 Slack / GitHub / Stack Overflow 上的截图
- 上传到 CDN 的构建产物
- 被爬取的部署预览 URL
浏览器中的“临时”密钥,就是互联网上的永久密钥。不存在安全的短期嵌入方式。
快速演示的正确做法仍然是使用后端——只是做一个小型后端。一个 20 行的 Express 脚本、一个 Vercel Function 或一个 Cloudflare Worker,花费的时间都比密钥泄露后写道歉邮件更少。
后端专用快速路径(无需浏览器,无需前端)
如果用户要将 InFlow 集成到纯后端、API、Agent、worker 或服务中,请完全跳过浏览器/SDK 相关内容,并遵循以下简短路径。这是最常见的后端集成方式。
操作顺序:
- 设置环境变量(步骤 1)—
INFLOW_API_KEY,以及可选的INFLOW_WEBHOOK_SECRET - 识别消费者(步骤 2)— 使用电子邮件/手机/用户名调用
POST /v1/users/search→ 获取userId - 使用
display: HEADLESS创建付款(步骤 3)— 如果有用于免点击支付的policyId,请传入;否则消费者会收到手机/电子邮件审批提示 - 持久化你的关联键 — 存储
{ yourOrderId, requestId, transactionId? },以便 webhook 处理程序查找对应订单。webhook 负载中只包含 InFlow 的 ID;你必须将其映射回自己的业务对象。 - 等待确认 — 任选其一:
- webhook 处理程序(步骤 5)— 推荐用于生产环境。验证 HMAC,使用
eventId实现幂等性,并在event.data.status === "PAID"时履行订单(同时检查判别字段) - 或轮询(步骤 6)— 适用于脚本、批处理作业和本地开发。调用
GET /v1/requests/{requestId},直到状态不再是PENDING
- webhook 处理程序(步骤 5)— 推荐用于生产环境。验证 HMAC,使用
**跳过:**步骤 4(前端 SDK)、下方的全栈顺利流程图,以及任何提到 inflow.js 或浏览器的内容。
对于自主 Agent,如果需要实现无需消费者逐笔交互的免点击无头付款,还要实现步骤 7(策略),并在每个付款请求中附加 policyId。
订单关联(后端模式)
付款集成中常见的一个问题是:webhook 到达后,处理程序却无法判断它属于哪个订单。InFlow webhook 负载包含 InFlow 的 ID(requestId、transactionId、approvalId),但不包含任何你的业务 ID。你必须在创建付款时自行存储映射关系。
最简映射表(存储在你的数据库/存储/缓存中):
| 你的列 | 来源 |
|---|---|
your_order_id | 你的业务对象,也就是被购买的商品 |
inflow_request_id | 从 POST /v1/requests/payment 返回 |
inflow_transaction_id | 稍后在 webhook 负载中返回(event.data.transactionId)——初始设为 NULL,在收到第一个事件时填入 |
status | 你的本地状态机:pending / paid / failed |
created_at | 审计记录 |
**创建付款时:**插入一行,包含 your_order_id 和 inflow_request_id,状态设为 pending。
**收到 webhook 时:**通过 inflow_request_id 查找该行(如果 data 是 ApprovalResponse,则使用 event.data.approvalId 或通过 requestId 关联;一旦出现 TransactionResponse,则使用 event.data.transactionId)。更新状态。履行订单。
**幂等性:**还要单独存储已处理的 event.eventId,以免重试时重复履行订单(InFlow 最多可能在 72 小时内重发同一事件)。
顺利流程:为 Web 应用添加结账功能
这是最常见的需求。以下是“我想在结账时接受 InFlow 付款”的端到端操作指南:
┌──────────────────────────────────────────────────────────────────┐
│ Frontend (browser) │
├──────────────────────────────────────────────────────────────────┤
│ 1. Collect consumer email at checkout │
│ 2. Click "Pay with InFlow" → POST to YOUR /api/inflow/checkout │
│ 3. Receive { requestId } from your backend │
│ 4. Render popup: new InFlow.Request(requestId).render({...}) │
│ 5. statusCallback fires when consumer approves │
│ 6. Show "processing..." (DO NOT fulfill yet) │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Backend (your /api/inflow/checkout endpoint) │
├──────────────────────────────────────────────────────────────────┤
│ 1. Receive { email, amount } from frontend │
│ 2. POST /v1/users/search { email } → get userId │
│ (if 404 → POST /v1/requests/register first) │
│ 3. POST /v1/requests/payment { │
│ userId, amount, currency: "USDC", │
│ display: "FULL", userDetails: ["EMAIL"] │
│ } │
│ 4. Return { requestId } to frontend │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ Backend (your /api/webhooks/inflow endpoint) │
├──────────────────────────────────────────────────────────────────┤
│ 1. Receive POST from InFlow with x-inflow-signature header │
│ 2. Verify HMAC-SHA256 signature → 401 if invalid │
│ 3. Check eventId against idempotency store → skip if seen │
│ 4. Fulfill ONLY if ALL three are true: │
│ a. event.type === "TRANSACTION_UPDATED" │
│ b. event.data.transactionId is present │
│ (i.e. data is a TransactionResponse, not Approval) │
│ c. event.data.status === "PAID" │
│ (use INNER data.status, NOT outer event.status) │
│ 5. Return HTTP 200 │
└──────────────────────────────────────────────────────────────────┘
两个
status字段,两种含义。 webhook 信封中有外层event.status(DELIVERED/FAILED/PENDING/DISABLED——InFlow 的投递状态),以及内层event.data.status(实际交易状态,例如PAID/PENDING/INSUFFICIENT_FUNDS)。**始终根据event.data.status做出履行订单的决定。**详情请参阅步骤 5。
用户必须手动完成的三件事:
- 获取
INFLOW_API_KEY(从 InFlow 仪表板获取,或创建 Agent 用户——请参阅下方的“Agent 用户设置”) - 在 InFlow 仪表板中注册 webhook URL
https://yourdomain.com/api/webhooks/inflow(此操作没有 API) - 在注册 URL 后,从 InFlow 仪表板获取
INFLOW_WEBHOOK_SECRET
实现集成后,请醒目地总结这些手动步骤。
参考资料
此技能随附 references/ 中的三份参考文档。需要了解本文件未涵盖的详细信息时,请阅读这些文档:
references/overview.md— 平台概念、审批模型、FULL 与 HEADLESS 显示模式references/flows.md— 8 个完整的集成流程,包含 HTTP 示例references/api-reference.md— 所有端点、所有架构、所有枚举值
用户提出具体问题时(例如“如何列出策略?”、“Webhook 载荷是什么样的?”),请查阅参考资料,不要猜测。
实施指南
前提条件
编写任何代码之前:
- 阅读项目 — 通过
package.json、requirements.txt、go.mod、Cargo.toml等文件识别语言和框架。使用项目中已有的 HTTP 客户端和 Web 框架。不要强行使用 Express、Next.js、React 或任何特定框架。 - 通过严格的后端门槛检查(见本文件前面的“🛑 严格的后端门槛”部分)——指出 API 密钥将存放于哪个仅服务端可访问的区域。如果不存在,则需征得用户同意后创建,再继续。
- 确认用户持有 API 密钥 — 如果没有,请按照本文件底部的 Agent 用户设置流程引导他们。
- 确认项目类型 — 纯后端 / 全栈 Web 应用 / CLI / Agent / worker。纯前端项目不是有效类型 — 请参阅“严格的后端门槛”。
- 纯后端 → 服务端支付 + Webhook(或轮询)
- 全栈 / Web 应用 → 服务端支付 + 前端 SDK + Webhook
- CLI / Agent / worker → 无头支付 + Webhook(或轮询),并可选择使用策略实现零点击
步骤 1:设置环境变量
将以下变量添加到项目现有的环境变量文件(.env、.env.local 等——遵循项目惯例)。也添加到 .env.example。
# Required for any InFlow integration
INFLOW_API_KEY=<your-private-key>
# Required if you handle webhooks
INFLOW_WEBHOOK_SECRET=<your-webhook-secret>
# Optional: defaults to https://api.inflowpay.ai
# INFLOW_API_BASE_URL=https://api.inflowpay.ai
确保 .env 和 .env.local 已加入 .gitignore。
重要: INFLOW_API_KEY 仅限服务端使用。切勿将其嵌入前端代码、通过公共路由暴露或记录到日志中。
步骤 2:查找或创建消费者
支付必须指向一个 userId。如果用户不知道消费者的 userId,必须先查找。
按电子邮件(或手机号码 / 用户名)搜索:
POST https://api.inflowpay.ai/v1/users/search
X-API-Key: <INFLOW_API_KEY>
Content-Type: application/json
{ "email": "[email protected]" }
匹配时(200):
{ "userId": "8a5c2948-9e08-439e-a7a6-9f0ee335f566" }
未匹配时(404): 发起注册:
POST https://api.inflowpay.ai/v1/requests/register
X-API-Key: <INFLOW_API_KEY>
Content-Type: application/json
{
"display": "FULL",
"userDetails": ["EMAIL", "NAME"]
}
⚠️ API 特殊之处:
RegisterRequest将userId列为可选项(根据 OpenAPI 规范,只有display和userDetails是必填项),但其在真正的新用户注册流程中的语义并未说明。不要自行生成 UUID。 先尝试不传userId调用;如果 API 拒绝,则用户必须向 InFlow 支持团队获取临时userId,或查阅供应商文档。请将此情况视为供应商说明不明确,并向用户明确说明,不要悄悄绕过这一问题。
此调用会返回包含 requestId 的 ApprovalResponse。将 requestId 交给前端 SDK,以呈现注册弹窗。用户批准后,消费者即完成注册,之后可以再次搜索。
搜索字段规则:
email— RFC 电子邮件格式mobile— E.164 格式,例如+15551234567username— 3-16 个字符,由字母数字字符及-_组成
userDetails 枚举值: BIRTHDATE、DEPOSIT_ADDRESSES、EMAIL、MOBILE、NAME、NATIONAL_ID、PHYSICAL_ADDRESS、USERNAME。始终至少包含 EMAIL。
步骤 3:创建支付请求
POST https://api.inflowpay.ai/v1/requests/payment
X-API-Key: <INFLOW_API_KEY>
Content-Type: application/json
{
"userId": "<consumer-uuid-from-Step-2>",
"amount": 99.99,
"currency": "USDC",
"display": "FULL",
"userDetails": ["EMAIL"]
}
响应(200):
{
"requestId": "<uuid>",
"type": "PAYMENT",
"status": "PENDING"
}
必填字段: amount(≥ 0.01)、currency、display、userDetails。
可选字段: userId、policyId(通过现有策略自动批准)。
金额格式: 使用所选稳定币的主要单位,以普通小数表示。99.99 表示 99.99 USDC(≈ $99.99)。不要使用分或其他最小单位。
根据用户的技术栈调整 HTTP 调用——查看项目,并使用已有的 HTTP 客户端。具体代码形式取决于语言(Node fetch、Python requests、Go net/http 等)。
错误处理:
- 4xx → 用户错误(缺少字段、格式无效、找不到用户)。将错误返回给调用方,不要重试。
- 5xx / 网络错误 → 暂时性错误。在结账失败前,使用指数退避重试(最多 3 次)。
获取 requestId 后,下一步取决于显示模式:
FULL→ 将requestId交给前端 SDK(步骤 4)HEADLESS→ 等待 webhook(步骤 5),或轮询GET /v1/requests/{requestId}(步骤 6)
步骤 4:前端 SDK(inflow.js)——用于 display: FULL
适用于基于浏览器的流程。前端加载 inflow.js,从后端获取 requestId,并渲染弹窗。
加载 SDK:
<script src="https://sandbox.inflowpay.ai/sdk/inflow.js"></script>
⚠️ 这是沙盒 URL。生产环境 URL 尚未公开记录。部署到生产环境前,用户应向 InFlow 确认生产环境 SDK URL。
渲染请求:
// Get the requestId from your backend (which calls POST /v1/requests/{login|register|payment|...})
const requestId = await fetchRequestIdFromYourBackend();
const request = new InFlow.Request(requestId);
request.render({
statusCallback: (status) => {
// status.status is one of: PENDING, APPROVED, DECLINED, EXPIRED, CANCELLED
if (status.status === InFlow.STATUS.APPROVED) {
// SDK indicated user approved. Show "processing..."
// DO NOT fulfill the order here — wait for the webhook.
}
}
});
登录、注册、支付和详情请求都适用相同模式。只有生成 requestId 的后端端点不同。
根据用户的框架调整周边标记——必要时将其封装在 React/Vue/Svelte 组件中。SDK 本身与框架无关。
步骤 5:Webhook 处理程序
InFlow 会将经过签名的事件发送到你注册的 webhook URL。
🚨 关键手动步骤:用户必须在 InFlow 控制面板中注册 webhook URL——目前没有可用于此操作的 API。实现处理程序后,请明确告知用户:
“前往 InFlow 控制面板,进入 webhooks 页面,注册
https://yourdomain.com/api/webhooks/inflow(或你的等效 URL),并将 webhook secret 复制到INFLOW_WEBHOOK_SECRET环境变量中。在完成这些操作之前,不会收到任何事件。”
如需在本地测试,建议使用 ngrok 或类似工具,通过 HTTPS 暴露 localhost。
Webhook 负载结构——注意两个 status 字段:
{
"eventId": "<uuid>",
"webhookId": "<uuid>",
"type": "TRANSACTION_UPDATED",
"status": "DELIVERED", ← OUTER: InFlow's delivery state. Ignore for fulfillment.
"data": {
"transactionId": "<uuid>",
"approvalId": "<uuid>",
"amount": 99.99,
"currency": "USDC",
"blockchain": "SOLANA",
"status": "PAID", ← INNER: actual transaction state. Use this for fulfillment.
"transactionHash": "0x...",
"created": "2026-01-01T00:00:00.000Z"
},
"created": "2026-01-01T00:00:00.000Z",
"delivered": "2026-01-01T00:00:01.000Z"
}
**关键区别:**事件信封中有两个
status字段:
event.status(外层)——InFlow 的 webhook 投递状态:PENDING/DELIVERED/FAILED/DISABLED。此字段表示 InFlow 是否成功投递了事件。不要据此执行业务逻辑分支。event.data.status(内层)——实际的交易或审批状态:PAID、INITIATED、PENDING、INSUFFICIENT_FUNDS、APPROVED、DECLINED等。履约时应检查此字段。
事件类型:TRANSACTION_CREATED、TRANSACTION_UPDATED
data 字段是一个带判别字段的联合类型:
- 如果包含
requestId和type(LOGIN/PAYMENT 等)→ 则为ApprovalResponse - 如果包含
transactionId、blockchain和transactionHash→ 则为TransactionResponse
只有在以下条件全部成立时,才履行订单:event.type === "TRANSACTION_UPDATED",data 字段的结构为 TransactionResponse(存在 transactionId 是最简单的判别方法),并且 event.data.status === "PAID"。不要泛化为“任何状态为 PAID 的 TRANSACTION_* 事件”——ApprovalResponse 负载也可能有 status 字段(APPROVED / PENDING 等),将其用于履约会导致程序崩溃或错误执行。
需要关注的 data.status 值(TransactionResponse):
PAID— 付款已完成(这是“履行订单”的信号)INITIATED、PENDING— 处理中,不执行任何操作INSUFFICIENT_FUNDS、GENERAL_ERROR— 失败,通知用户REFUNDED、RETURNED— 款项已退回
**必须:验证 HMAC 签名。**每个 webhook 都使用 HMAC-SHA256 签名。签名位于 x-inflow-signature 标头中。处理前必须验证签名。
// Generic — adapt to whatever crypto library the project uses.
// Node.js example using built-in crypto module:
import crypto from 'node:crypto';
function verifyInflowWebhook(rawBody, signatureHeader, secret) {
// Guard against missing or malformed header — timingSafeEqual throws on length mismatch
if (typeof signatureHeader !== 'string' || signatureHeader.length === 0) return false;
if (!rawBody || !secret) return false;
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
if (expected.length !== signatureHeader.length) return false;
// Constant-time compare to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureHeader),
);
}
任何语言的处理逻辑都相同:使用 webhook secret 对原始请求正文进行 HMAC-SHA256 计算,将结果编码为十六进制,再与 x-inflow-signature 标头进行比较。
重要:必须针对原始请求正文的字节进行验证,不能针对重新序列化的 JSON 对象。如果框架会自动解析 JSON,则必须在解析前捕获原始正文。具体实现方式取决于所用框架。
Webhook 处理程序的职责:
- 验证签名 → 如果无效,则返回 HTTP 401
- 解析 JSON 正文
- 在幂等性存储中检查
eventId→ 如果已处理,则跳过(InFlow 可能会在最长 72 小时内重试同一事件) - 根据
event.data.approvalId或event.data.transactionId查找本地订单(参见上文的订单关联部分) - 按照完整的三条件规则分支处理(不能遗漏任何条件):
- 履行订单:以下条件必须全部满足:
event.type === "TRANSACTION_UPDATED",且存在event.data.transactionId(这是TransactionResponse,不是ApprovalResponse),并且event.data.status === "PAID" - 标记为失败:存在
event.data.transactionId,且event.data.status为INSUFFICIENT_FUNDS/GENERAL_ERROR/RETURNED - 仅记录日志:其他事件类型或非终态状态(
INITIATED、PENDING等) - 绝不要根据
event.status(外层投递状态)履行订单——它仅表示 InFlow 已成功投递 webhook
- 履行订单:以下条件必须全部满足:
- 返回 HTTP 200 以确认收到事件
如果处理程序没有返回 200,InFlow 会以指数退避方式持续重试,最长可达 72 小时。
第 6 步:轮询(Webhook 的替代方案)
如果无法设置 Webhook 监听器(本地开发、批处理作业、简单脚本),请轮询请求状态:
GET https://api.inflowpay.ai/v1/requests/{requestId}
X-API-Key: <INFLOW_API_KEY>
返回当前的 ApprovalResponse。每隔几秒轮询一次,直到 status 不再是 PENDING。
审批通过后,获取完整的交易详情:
GET https://api.inflowpay.ai/v1/transactions/{transactionId}
其中,transactionId 来自已审批的 ApprovalResponse.transactionId。
第 7 步:策略(启用 0 点击无头支付)
策略是预先批准的支出规则。当 HEADLESS 支付请求包含 policyId,且请求未超出策略限额时,系统会立即自动批准,无需消费者交互。
适用于以下情况:
- 需要在无需逐笔交易审批的情况下进行购买的自主 AI Agent
- 定期付款(订阅、自动充值)
- 高频小额交易
直接创建(由卖家发起):
POST https://api.inflowpay.ai/v1/policies
X-API-Key: <INFLOW_API_KEY>
Content-Type: application/json
{
"type": "PAYMENT",
"action": "APPROVE",
"currency": "USDC",
"budget": 1000,
"threshold": 100,
"period": "MONTHLY",
"expires": "2026-12-31T00:00:00.000Z"
}
由消费者发起的请求(消费者必须通过自己的设备批准策略):
POST https://api.inflowpay.ai/v1/requests/policy
X-API-Key: <INFLOW_API_KEY>
Content-Type: application/json
{
"userId": "<consumer-uuid>",
"display": "FULL",
"userDetails": ["EMAIL"]
}
这会返回一个 ApprovalResponse——流程与登录或支付相同。消费者通过 SDK 或移动设备批准后,策略即会生效。
策略字段:
action—APPROVE、DECLINE、REVIEWperiod—DAILY、MONTHLY、YEARLY、ONCEbudget— 该周期的总额度threshold— 单笔交易的最高金额spent— 当前用量(在PolicyResponse中只读)
增删改查:
GET /v1/policies
GET /v1/policies/{policyId}
POST /v1/policies
PUT /v1/policies/{policyId}
DELETE /v1/policies/{policyId}/delete
将策略附加到支付请求:
{
"userId": "<consumer-uuid>",
"amount": 50,
"currency": "USDC",
"display": "HEADLESS",
"userDetails": ["EMAIL"],
"policyId": "<policy-uuid>"
}
如果请求未超出预算和单笔限额,响应中的 status 会直接显示为 APPROVED。
第 8 步:钱包只读操作
只读访问卖家钱包状态,适用于仪表板、对账和余额检查。
GET /v1/balances — all currency balances
GET /v1/balances/{currency} — single currency balance
GET /v1/transactions — paginated transaction history
GET /v1/transactions/{id} — single transaction
GET /v1/users/self — current authenticated user
所有操作均返回 JSON。完整的架构请参阅 references/api-reference.md。
验证
运行冒烟测试,确认一切正常。请根据项目使用的工具调整命令(npm 脚本、curl、http 文件等):
- API 密钥身份验证 — 使用已配置的
X-API-Key调用GET /v1/users/self。应返回经过身份验证的用户。如果返回 401,说明密钥有误。 - 搜索用户 — 使用已知电子邮件地址调用
POST /v1/users/search。确认返回 200 和userId;如果用户不存在,则应返回 404。 - 支付请求 — 向已知的
userId发起一笔小额测试支付。确认响应包含requestId和status: PENDING。 - 轮询(如果已实现)— 调用
GET /v1/requests/{requestId},并确认消费者交互时状态会更新。 - Webhook(如果已实现)— 收到实际事件后,检查处理程序日志,确认已验证签名、解析负载、执行处理程序逻辑并返回 HTTP 200。(你也可以使用
GET /v1/events查看历史事件,并使用POST /v1/events/{eventId}/resend重放事件。)
如果用户在生产环境中使用,还应建议:
- 使用持久化存储(数据库或 Redis)保存幂等性键
eventId - 记录所有 Webhook 事件,以供审计
- 为处理失败的 Webhook 设置死信队列
Agentic 用户设置(无现有账户)
如果用户没有现有的 InFlow 账户,请创建一个 Agentic(程序化)用户。这是一次性设置,无需现有身份验证。
POST https://api.inflowpay.ai/v1/users/agentic
Content-Type: application/json
{
"locale": "EN_US",
"timezone": "US/Pacific"
}
返回:
{
"userId": "<uuid>",
"privateKey": "<your-api-key>",
"locale": "EN_US",
"timezone": "US/Pacific",
"created": "...",
"updated": "..."
}
privateKey 应填入 INFLOW_API_KEY 环境变量。请妥善保管——此密钥无法再次获取。
幂等性
在每个步骤开始前,检查工作是否已完成。跳过已完成的步骤:
- 如果
.env/.env.example中已有INFLOW_API_KEY,跳过环境变量设置 - 如果 InFlow 路径已有带 HMAC 验证的 webhook 处理器,跳过 webhook 场景
- 如果前端某处已加载
inflow.js,跳过 SDK 脚本标签 - 如果已有封装
api.inflowpay.ai调用的辅助模块(在源代码中查找INFLOW_API_KEY或inflowpay.ai),请复用,而不是新建 - 如果
.env/.env.local不在.gitignore中,将其添加进去
始终先阅读项目,再开始编写——不要重复实现已有逻辑。
最终摘要(实现完成后)
实现完成后,请按以下格式向用户提供清晰的摘要:
- 创建或修改的文件 — 列出每个文件及其用途
- 需要设置的环境变量 — 名称、用途,以及获取方式
- 当前已启用的流程 — 支付 / 登录 / 注册 / webhook / 轮询 / 策略
- 用户必须完成的手动步骤 — 明确说明:
- 获取 API 密钥(从控制面板获取,或通过
POST /v1/users/agentic获取) - 在 InFlow 控制面板中注册 webhook URL(此操作没有 API — 如果创建了 webhook 处理器,请醒目提示)
- 将 webhook 密钥复制到
INFLOW_WEBHOOK_SECRET - 上线前向 InFlow 确认生产环境 SDK URL(代码中默认使用 sandbox URL)
- 获取 API 密钥(从控制面板获取,或通过
- 测试命令 — 验证每个流程的确切命令
- 后续步骤 — 集成接通后要做什么
摘要应简洁。用户阅读后应能完成集成。