mcpField
I
inflow-payments

inflow-payments

github.com/nodeops-app開発399 回インストール7 週間前に更新
リポジトリ

inflow-payments について

inflow-paymentsは、特定のフレームワークや言語に依存せず、バックエンドサービス、アプリ、CLI、エージェント、ワーカーへInFlowのステーブルコイン決済を統合するスキルです。利用者の特定、決済リクエスト、状態確認を扱い、APIキーをサーバー側だけに置くことを求めます。

主な機能

  1. 01利用者の検索・登録・特定
  2. 02ステーブルコイン決済リクエストの作成
  3. 03事前承認型の決済ポリシー設定
  4. 04Webhookまたはポーリングによる状態確認
  5. 05残高・取引・ウォレットアドレスの参照
  6. 06APIキーのバックエンド限定

活用シーン

  1. 01アプリやサービスへのInFlow決済導入
  2. 02エージェント向けポリシー決済の設定
  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 Payments Integration

InFlow はステーブルコイン決済プラットフォームです。販売者(加盟店、エージェント)が消費者に支払いを請求し、消費者はモバイル、メール、SMS を通じて承認します。決済はオンチェーンで行われます。消費者は予算内のリクエストを自動承認する ポリシー を事前に設定でき、自律型エージェントによる 0 クリックのフローを実現できます。

このスキルは、特定のフレームワークや言語を強制することなく、バックエンドサービス、フルスタックのウェブアプリ、CLI、自律型エージェント、ワーカーなど、あらゆるプロジェクトに InFlow を統合します。重要: 統合自体は常にバックエンドで行います。ブラウザーコードを使うのは、任意で利用できる 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ヘッドレス/0 クリックのフロー向けに、事前承認済みの支出ルールを設定
ウェブフック受信用の POST エンドポイント取引/承認ステータスの変更を受信
ポーリングのフォールバックGET /v1/requests/{requestId}ウェブフックを使わずにステータスを確認
ウォレットの読み取りGET /v1/balances, GET /v1/transactions残高と取引履歴を読み取り
暗号資産ウォレットGET/POST /v1/deposit-addresses, /v1/withdrawal-addressesオンチェーンのウォレットアドレスを管理
エージェント型ユーザーPOST /v1/users/agenticプログラムから利用するアカウントを作成し、API キーを取得

通貨: USDC、USDT、EURC、PYUSD(ステーブルコインのみ。法定通貨には非対応) ブロックチェーン: APTOS、BASE、SOLANA、WORLD

このスキルで対応しない機能

以下の機能は InFlow の公開 API では公開されていません。これらをユーザーから求められた場合は、対応を中止し、サポートされていないことを伝えてください。回避策を試みてはいけません。

  • マーケットプレイスの売上分配 — PaymentRequest に splits[] フィールドはありません。複数の受取人への支払いには、アトミック性の保証がない、個別の順次リクエストが必要になります。
  • 返金の作成 — 取引のステータスが REFUNDED になることはありますが、返金を開始する API はありません。返金はダッシュボード上でのみ行うか、帯域外で処理します。
  • API によるウェブフック URL の登録 — ウェブフック URL は InFlow ダッシュボードで設定します。プログラムから登録するための POST /v1/webhooks はありません。
  • 販売者向け OAuth フロー — 一部の古いドキュメントでは「OAuth」に言及していますが、消費者認証は全面的に SDK 経由で行われます。統合する販売者向けの OAuth フローはありません。

デフォルトと判断ツリー

ユーザーから指定がない場合は、次のデフォルトを使用します。

質問デフォルト理由
通貨USDC最も広く保有されているステーブルコインです
userDetails フィールド["EMAIL"]必須フィールドであり、メールアドレスは有用な識別情報として最低限必要です
表示モード以下の判断を参照ブラウザーが関与するかどうかによって異なります
本番環境とサンドボックスユーザーが確認するまでサンドボックス SDK URL本番用 SDK URL はまだ文書化されていません

表示モード: 支払い時に利用者がブラウザーを操作しており(ページで inflow.js を読み込める場合)、FULL を使用します。それ以外の場合はすべて HEADLESS を使用します。対象は、サーバー間通信、自律型エージェント、スケジュールされたジョブ、AI ワーカーです。

Webhook とポーリング: 本番環境では Webhook を使用します。ポーリングは、ローカル開発、スクリプト、またはパブリック HTTPS エンドポイントがない場合に限り使用します。

ユーザーの識別: 支払い先には、既知の userId を指定する必要があります。エージェントは、次のいずれかを行う必要があります。

  • ユーザーがすでに知っている場合は、ユーザーから userId を取得する、または
  • 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_ が付いていない環境変数をクライアントコードにバンドルしません。このバグは、認証ヘッダーのないリクエストが失敗する形で表面化します。
  • エージェントがこの undefined を「修正」しようとして、変数名を NEXT_PUBLIC_INFLOW_API_KEY に変更すると、問題が発生します。このプレフィックスにより、バンドラーが値をクライアントバンドルにインライン化し、すべての訪問者にキーが配布されてしまいます。
  • INFLOW_API_KEY に NEXT_PUBLIC_ を付けてはいけません。 正しい修正方法は、呼び出しを Server Component、Route Handler(app/api/.../route.ts)、または Server Action に移すことです。キーを公開する方法は絶対に使わないでください。

Vite(React、Vue、Svelte、Solid など):

  • import.meta.env.INFLOW_API_KEY は、VITE_ が付いていない限り undefined になります。これを「修正」しようとして 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:

  • いずれもサーバーとクライアントが分かれています。ルールは同じです。INFLOW_API_KEY は、フレームワークのサーバー境界(loader、action、server route など)の内側でのみ読み取り、コンポーネントコードでは読み取らないでください。
「ただのデモだから」という落とし穴

ユーザーが 「ただのデモです」、「テスト用です」、「バックエンドは後で追加します」、「今だけです」 などと言っても、ルールは変わりません。埋め込まれた加盟店キーは、次の場所から漏えいします。

  • Git の履歴(特に git push --force による「修正」の後)
  • ブラウザーの開発者ツール(誰でも開けます)
  • ブラウザー拡張機能(スクリプトを読み取ります)
  • チケットで共有されたエラーレポート/クラッシュダンプ
  • Slack/GitHub/Stack Overflow に投稿されたスクリーンショット
  • CDN にアップロードされたビルド成果物
  • クロールされるデプロイ済みプレビュー URL

ブラウザーに置かれた「一時的な」キーは、インターネット上では永久に使えるキーです。短期間だけ埋め込む安全な方法はありません。

手早くデモを行う場合も、正しい方法は バックエンドを使うこと です。小規模なもので十分です。20 行の Express スクリプト、単一の Vercel Function、または Cloudflare Worker で対応できます。いずれも、キー漏えい後に謝罪メールを書くより時間はかかりません。


バックエンドのみの最短手順(ブラウザー、フロントエンドなし)

ユーザーが InFlow を純粋なバックエンド、API、エージェント、ワーカー、またはサービスに統合する場合は、ブラウザー/SDK の説明をすべて省略し、この短い手順に従ってください。 これは最も一般的なバックエンド統合の形です。

作業の順序:

  1. 環境変数を設定する(ステップ 1)— INFLOW_API_KEY、必要に応じて INFLOW_WEBHOOK_SECRET
  2. 利用者を特定する(ステップ 2)— メールアドレス/携帯電話番号/ユーザー名を指定して POST /v1/users/search を呼び出し、userId を取得します
  3. display: HEADLESS を指定して支払いを作成する(ステップ 3)— 0 クリックにするための policyId があれば渡します。ない場合、利用者にはモバイルまたはメールで承認プロンプトが表示されます
  4. 照合キーを永続化する — { yourOrderId, requestId, transactionId? } を保存し、Webhook ハンドラーが適切な注文を特定できるようにします。Webhook のペイロードには InFlow の ID しか含まれないため、ビジネスオブジェクトに対応付ける必要があります。
  5. 確認を待つ — 次のいずれかを使用します。
    • Webhook ハンドラー(ステップ 5)— 本番環境ではこちらを推奨します。HMAC と eventId による冪等性を検証し、識別子の確認を行ったうえで event.data.status === "PAID" の場合に処理を完了します
    • または、ポーリング(ステップ 6)— スクリプト、バッチジョブ、ローカル開発向けです。ステータスが PENDING でなくなるまで GET /v1/requests/{requestId} を呼び出します

省略する項目: ステップ 4(フロントエンド SDK)、以下のフルスタックの正常系フロー図、inflow.js またはブラウザーについての記述。

取引ごとの利用者の操作なしで 0 クリックのヘッドレス決済を行う必要がある自律型エージェントでは、ステップ 7(ポリシー)も実装し、すべての支払いリクエストに policyId を指定してください。


注文の照合(バックエンドのパターン)

決済統合でよくある不具合は、Webhook が届いても、ハンドラーがどの注文に対応するのか特定できないことです。InFlow の Webhook ペイロードには InFlow の ID(requestId、transactionId、approvalId)が含まれますが、ビジネス ID は一切含まれません。 支払いの作成時に、自分で対応関係を保存する必要があります。

最小限の対応付けテーブル(DB/ストア/キャッシュ内):

自分のカラム取得元
your_order_id購入対象を表すビジネスオブジェクト
inflow_request_idPOST /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                                               │
└──────────────────────────────────────────────────────────────────┘

2 つの status フィールドには、それぞれ異なる意味があります。 Webhook エンベロープの外側にある event.status は(InFlow の配信状態である DELIVERED/FAILED/PENDING/DISABLED)、内側の event.data.status は(PAID/PENDING/INSUFFICIENT_FUNDS などの実際の取引状態)を表します。処理を完了するかどうかの判断には、必ず event.data.status を確認してください。 詳細はステップ 5 を参照してください。

ユーザーが手動で行う必要がある 3 つの作業:

  1. INFLOW_API_KEY を取得する(InFlow ダッシュボードから取得するか、エージェントユーザーを作成します。下記の「エージェントユーザーの設定」を参照してください)
  2. InFlow ダッシュボードで Webhook URL https://yourdomain.com/api/webhooks/inflow を登録する(この操作用の API はありません)
  3. URL の登録後、InFlow ダッシュボードから INFLOW_WEBHOOK_SECRET を取得する

統合の実装後は、これらの手動作業を目立つようにまとめてください。


参考資料

このスキルには、references/ に3つの参考ドキュメントが含まれています。このファイルに記載されていない詳細が必要な場合は、これらを参照してください。

  • references/overview.md — プラットフォームの概念、承認モデル、FULL および HEADLESS の表示モード
  • references/flows.md — HTTP の例を含む、実際に使える8つの統合フロー
  • references/api-reference.md — すべてのエンドポイント、スキーマ、列挙型の値

ユーザーから具体的な質問(例:「ポリシーの一覧を取得するには?」「webhook のペイロードはどのような形式ですか?」)があった場合は、推測せずに参考資料を調べてください。


実装ガイド

前提条件

コードを書く前に、次の手順を実施してください。

  1. プロジェクトを確認する — package.json、requirements.txt、go.mod、Cargo.toml などを確認して、言語とフレームワークを特定します。既存の HTTP クライアントと Web フレームワークを使用してください。Express、Next.js、React、その他の特定のフレームワークを無理に導入しないでください。
  2. Hard Backend Gate を通過する(このファイルの前のほうにある「🛑 Hard Backend Gate」セクションを参照)— API キーを保持するサーバー専用の領域を明示してください。その領域がない場合は、先にユーザーの同意を得て作成します。
  3. ユーザーが API キーを持っていることを確認する — 持っていない場合は、このファイルの末尾にあるエージェントによるユーザー設定を案内してください。
  4. プロジェクトの種類を確認する — 純粋なバックエンド/フルスタックの Web アプリ/CLI/エージェント/ワーカーのいずれかです。純粋なフロントエンドのプロジェクトは有効な種類ではありません。Hard Backend Gate を参照してください。
    • 純粋なバックエンド → サーバー決済 + webhook(またはポーリング)
    • フルスタック/Web アプリ → サーバー決済 + フロントエンド SDK + webhook
    • CLI/エージェント/ワーカー → ヘッドレス決済 + webhook(またはポーリング)+ 必要に応じて0クリック用のポリシー
ステップ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 — +15551234567 のような E.164 形式
  • username — 3〜16文字の英数字と -_

userDetails の列挙値: BIRTHDATE、DEPOSIT_ADDRESSES、EMAIL、MOBILE、NAME、NATIONAL_ID、PHYSICAL_ADDRESS、USERNAME。必ず EMAIL を1つ以上含めてください。

ステップ 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(既存のポリシーを介して自動承認する場合)。

金額の形式: 選択したステーブルコインの主要単位での10進数です。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 に署名付きイベントを送信します。

🚨 極めて重要な手動ステップ: webhook URL は、ユーザーが InFlow ダッシュボードで登録する必要があります。登録用の API はありません。ハンドラーを実装した後、ユーザーに必ず次のように伝えてください。

「InFlow ダッシュボードを開き、webhook に移動して、https://yourdomain.com/api/webhooks/inflow(または同等の URL)を登録し、webhook シークレットをコピーして INFLOW_WEBHOOK_SECRET 環境変数に設定してください。設定するまで、イベントは一切送信されません。」

ローカルでテストする場合は、ngrok などを使って localhost を HTTPS で公開するよう提案してください。

Webhook ペイロードの形式 — 2 つの 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"
}

重要な区別: エンベロープには 2 つの 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" であること。「TRANSACTION_* イベントであれば何でも status が PAID のときに履行する」と一般化しないでください。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 シークレットを使って生のリクエストボディに HMAC-SHA256 を適用し、16 進数にエンコードしたうえで、x-inflow-signature ヘッダーの値と比較してください。

重要: 再シリアライズした JSON オブジェクトではなく、生のリクエストボディのバイト列に対して検証してください。フレームワークが JSON を自動的に解析する場合は、解析前に生のボディを取得する必要があります。具体的な方法はフレームワークによって異なります。

Webhook ハンドラーの役割:

  1. 署名を検証する → 無効な場合は HTTP 401 で拒否する
  2. JSON ボディを解析する
  3. eventId を冪等性ストアで確認する → 処理済みの場合はスキップする(InFlow は同じイベントを最大 72 時間再試行する場合があります)
  4. event.data.approvalId または event.data.transactionId を使ってローカルの注文を検索する(上記の注文照合セクションを参照)
  5. 3 つの条件すべてを含むルールに従って分岐する(どの条件も省略しないでください)。
    • 次の条件をすべて満たす場合に履行する:event.type === "TRANSACTION_UPDATED" かつ event.data.transactionId が存在する(ApprovalResponse ではなく TransactionResponse である)かつ 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 エージェント
  • 定期支払い(サブスクリプション、自動チャージ)
  • 高頻度かつ少額の取引

直接作成(販売者が開始):

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 では読み取り専用)

CRUD:

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 でイベントを再送することもできます。)

ユーザーが本番環境を利用している場合は、次の対応も推奨してください。

  • eventId 用の永続的な冪等性ストア(DB または Redis)
  • 監査用にすべての Webhook イベントをログに記録する
  • Webhook の処理に失敗した場合に備えたデッドレターキュー

エージェント型ユーザーのセットアップ(既存アカウントがない場合)

ユーザーが InFlow の既存アカウントを持っていない場合は、エージェント型(プログラムから操作する)ユーザーを作成します。これは一度だけ行うセットアップで、既存の認証は必要ありません。

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 にコピーすること
    • 本番環境に移行する前に、本番 SDK の URL を InFlow に確認すること(コードではデフォルトでサンドボックスの URL が設定されています)
  5. テストコマンド — 各フローを検証するための正確なコマンドを記載します
  6. 次のステップ — 統合の接続後に行う作業を記載します

サマリーは簡潔にしてください。ユーザーが読んだ後、統合を完了できるようにしてください。

コメント