mcpField
I
inflow-payments

inflow-payments

github.com/nodeops-app开发399 次安装7 周前更新
代码仓库

关于 inflow-payments

inflow-payments 是将 InFlow 稳定币支付接入后端服务、应用、CLI、Agent 或工作进程的技能,不绑定特定框架或语言。它涵盖消费者识别、付款请求和状态确认,并要求 API 密钥仅保存在服务器端。

主要功能

  1. 01搜索、注册和识别消费者
  2. 02创建稳定币付款请求
  3. 03配置预授权支付策略
  4. 04通过 Webhook 或轮询确认状态
  5. 05读取余额、交易和钱包地址
  6. 06限定 API 密钥仅用于后端

适用场景

  1. 01为应用或服务添加 InFlow 结账
  2. 02为 Agent 设置无交互策略支付
  3. 03处理支付状态通知并关联业务订单
  4. 04在批处理任务中轮询付款状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/nodeops-app/skills --skill inflow-payments

安装后在 Claude Code 中输入/inflow-payments确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-reference.md
flows.md
overview.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. 设置环境变量(步骤 1)— INFLOW_API_KEY,以及可选的 INFLOW_WEBHOOK_SECRET
  2. 识别消费者(步骤 2)— 使用电子邮件/手机/用户名调用 POST /v1/users/search → 获取 userId
  3. 使用 display: HEADLESS 创建付款(步骤 3)— 如果有用于免点击支付的 policyId,请传入;否则消费者会收到手机/电子邮件审批提示
  4. 持久化你的关联键 — 存储 { yourOrderId, requestId, transactionId? },以便 webhook 处理程序查找对应订单。webhook 负载中只包含 InFlow 的 ID;你必须将其映射回自己的业务对象。
  5. 等待确认 — 任选其一:
    • webhook 处理程序(步骤 5)— 推荐用于生产环境。验证 HMAC,使用 eventId 实现幂等性,并在 event.data.status === "PAID" 时履行订单(同时检查判别字段)
    • 或轮询(步骤 6)— 适用于脚本、批处理作业和本地开发。调用 GET /v1/requests/{requestId},直到状态不再是 PENDING

**跳过:**步骤 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。

用户必须手动完成的三件事:

  1. 获取 INFLOW_API_KEY(从 InFlow 仪表板获取,或创建 Agent 用户——请参阅下方的“Agent 用户设置”)
  2. 在 InFlow 仪表板中注册 webhook URL https://yourdomain.com/api/webhooks/inflow(此操作没有 API)
  3. 在注册 URL 后,从 InFlow 仪表板获取 INFLOW_WEBHOOK_SECRET

实现集成后,请醒目地总结这些手动步骤。


参考资料

此技能随附 references/ 中的三份参考文档。需要了解本文件未涵盖的详细信息时,请阅读这些文档:

  • references/overview.md — 平台概念、审批模型、FULL 与 HEADLESS 显示模式
  • references/flows.md — 8 个完整的集成流程,包含 HTTP 示例
  • references/api-reference.md — 所有端点、所有架构、所有枚举值

用户提出具体问题时(例如“如何列出策略?”、“Webhook 载荷是什么样的?”),请查阅参考资料,不要猜测。


实施指南

前提条件

编写任何代码之前:

  1. 阅读项目 — 通过 package.json、requirements.txt、go.mod、Cargo.toml 等文件识别语言和框架。使用项目中已有的 HTTP 客户端和 Web 框架。不要强行使用 Express、Next.js、React 或任何特定框架。
  2. 通过严格的后端门槛检查(见本文件前面的“🛑 严格的后端门槛”部分)——指出 API 密钥将存放于哪个仅服务端可访问的区域。如果不存在,则需征得用户同意后创建,再继续。
  3. 确认用户持有 API 密钥 — 如果没有,请按照本文件底部的 Agent 用户设置流程引导他们。
  4. 确认项目类型 — 纯后端 / 全栈 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 格式,例如 +15551234567
  • username — 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 处理程序的职责:

  1. 验证签名 → 如果无效,则返回 HTTP 401
  2. 解析 JSON 正文
  3. 在幂等性存储中检查 eventId → 如果已处理,则跳过(InFlow 可能会在最长 72 小时内重试同一事件)
  4. 根据 event.data.approvalId 或 event.data.transactionId 查找本地订单(参见上文的订单关联部分)
  5. 按照完整的三条件规则分支处理(不能遗漏任何条件):
    • 履行订单:以下条件必须全部满足: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
  6. 返回 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、REVIEW
  • period — DAILY、MONTHLY、YEARLY、ONCE
  • budget — 该周期的总额度
  • 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 文件等):

  1. API 密钥身份验证 — 使用已配置的 X-API-Key 调用 GET /v1/users/self。应返回经过身份验证的用户。如果返回 401,说明密钥有误。
  2. 搜索用户 — 使用已知电子邮件地址调用 POST /v1/users/search。确认返回 200 和 userId;如果用户不存在,则应返回 404。
  3. 支付请求 — 向已知的 userId 发起一笔小额测试支付。确认响应包含 requestId 和 status: PENDING。
  4. 轮询(如果已实现)— 调用 GET /v1/requests/{requestId},并确认消费者交互时状态会更新。
  5. 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 中,将其添加进去

始终先阅读项目,再开始编写——不要重复实现已有逻辑。


最终摘要(实现完成后)

实现完成后,请按以下格式向用户提供清晰的摘要:

  1. 创建或修改的文件 — 列出每个文件及其用途
  2. 需要设置的环境变量 — 名称、用途,以及获取方式
  3. 当前已启用的流程 — 支付 / 登录 / 注册 / webhook / 轮询 / 策略
  4. 用户必须完成的手动步骤 — 明确说明:
    • 获取 API 密钥(从控制面板获取,或通过 POST /v1/users/agentic 获取)
    • 在 InFlow 控制面板中注册 webhook URL(此操作没有 API — 如果创建了 webhook 处理器,请醒目提示)
    • 将 webhook 密钥复制到 INFLOW_WEBHOOK_SECRET
    • 上线前向 InFlow 确认生产环境 SDK URL(代码中默认使用 sandbox URL)
  5. 测试命令 — 验证每个流程的确切命令
  6. 后续步骤 — 集成接通后要做什么

摘要应简洁。用户阅读后应能完成集成。

评论

支持 Markdown