flutterwave について
flutterwaveは、Flutterwave APIを統合する開発者向けのAIエージェントスキルです。v3とv4に対応するTypeScriptクライアントを備え、決済や送金、検証などの操作例とガイドを提供します。
主な機能
- 01Flutterwave API v3とv4に対応
- 02v4トークンキャッシュ付きTypeScriptクライアント
- 03決済・送金の操作例
- 04Webhook検証ガイド
- 05サンドボックス用テストデータ
活用シーン
- 01ホステッドチェックアウト決済の作成
- 02注文処理前の取引検証
- 03送金の開始と状態確認
- 04シークレットハッシュによるWebhook検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ devmubarak1/ flutterwave --skill flutterwaveインストール後、Claude Code で/flutterwaveを入力して読み込まれたか確認します。
詳細
flutterwave
Flutterwave APIを統合するための、完全な本番対応 AI エージェントスキルです。**v3(本番環境で安定)とv4(次世代パブリックベータ)**の両方をネイティブにサポートしているため、開発者や AI エージェントは、サードパーティ製の CLI やプロキシに依存せず、バージョンをシームレスに選択して切り替えられます。
- 公式ドキュメント(v3): https://developer.flutterwave.com/docs
- API リファレンス(v3): https://developer.flutterwave.com/reference
- 公式 AI インデックス(v3): https://developer.flutterwave.com/llms.txt
- 公式ドキュメントと AI インデックス(v4): https://developer.flutterwave.com/v4.0.0/llms.txt
- Markdown ドキュメントへのショートカット: Flutterwave のドキュメント URL の末尾に
.mdを追加すると(例:https://developer.flutterwave.com/docs/getting-started.md)、HTML のノイズがないクリーンな Markdown を取得できます。
アーキテクチャとバージョンの比較
| 機能 | v3(本番環境で安定) | v4(次世代パブリックベータ) |
|---|---|---|
| ステータス | 世界中で導入され、実績を重ねています | パブリックベータ(高性能、マイクロサービス) |
| ベース URL | https://api.flutterwave.com/v3 | Sandbox:https://developersandbox-api.flutterwave.comLive: https://f4bexperience.flutterwave.com/ |
| 認証 | 固定シークレットキーのベアラー(FLW_SECRET_KEY) | OAuth 2.0 OpenID Connect クライアントクレデンシャル(client_id + client_secret) |
| トークンエンドポイント | N/A | POST https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token |
| トレースヘッダー | 任意 | X-Trace-Id(12~255 文字の UUID、必須) |
| 冪等性 | ペイロード内のカスタム tx_ref | X-Idempotency-Key ヘッダー + ペイロード内の reference |
| 主な入金処理 | POST /payments または POST /charges | customer_id と payment_method_id を指定した POST /charges |
| 主な出金処理 | 口座情報を指定した POST /transfers | payment_instruction と recipient_id を指定した POST /transfers |
ユニバーサルクライアントの実装(TypeScript)
外部 CLI への依存はありません。v3 と v4 の両方での実行、v4 の OAuth 2.0 トークンの自動キャッシュ、リクエストトレースに対応しています。
import crypto from "node:crypto";
export interface FlutterwaveConfig {
secretKey?: string; // v3 secret key (e.g., FLWSECK_TEST-... or FLWSECK-...)
encryptionKey?: string; // v3 3DES encryption key for direct card charges
v4ClientId?: string; // v4 OAuth2 client_id
v4ClientSecret?: string; // v4 OAuth2 client_secret
v3BaseUrl?: string; // default: https://api.flutterwave.com/v3
v4BaseUrl?: string; // sandbox: https://developersandbox-api.flutterwave.com | live: https://f4bexperience.flutterwave.com/
v4IdpUrl?: string; // default: https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token
}
export interface FlwResponse<T = unknown> {
status: "success" | "error";
message: string;
data: T;
meta?: Record<string, unknown>;
}
export class FlutterwaveApiError extends Error {
constructor(
message: string,
public readonly statusCode: number,
public readonly payload?: unknown,
) {
super(message);
this.name = "FlutterwaveApiError";
}
}
export class Flutterwave {
private readonly config: FlutterwaveConfig;
private v4TokenCache: { token: string; expiresAt: number } | null = null;
constructor(config: FlutterwaveConfig = {}) {
this.config = {
secretKey: config.secretKey ?? process.env.FLW_SECRET_KEY,
encryptionKey: config.encryptionKey ?? process.env.FLW_ENCRYPTION_KEY,
v4ClientId: config.v4ClientId ?? process.env.FLW_V4_CLIENT_ID,
v4ClientSecret: config.v4ClientSecret ?? process.env.FLW_V4_CLIENT_SECRET,
v3BaseUrl: config.v3BaseUrl ?? process.env.FLW_V3_BASE_URL ?? "https://api.flutterwave.com/v3",
v4BaseUrl: config.v4BaseUrl ?? process.env.FLW_V4_BASE_URL ?? "https://developersandbox-api.flutterwave.com",
v4IdpUrl: config.v4IdpUrl ?? "https://idp.flutterwave.com/realms/flutterwave/protocol/openid-connect/token",
};
}
/**
* Retrieves or refreshes OAuth 2.0 access token for v4 requests.
*/
private async getV4Token(): Promise<string> {
const now = Date.now();
if (this.v4TokenCache && this.v4TokenCache.expiresAt > now + 60_000) {
return this.v4TokenCache.token;
}
if (!this.config.v4ClientId || !this.config.v4ClientSecret) {
throw new Error("Missing v4 credentials: FLW_V4_CLIENT_ID and FLW_V4_CLIENT_SECRET are required for v4 calls.");
}
const res = await fetch(this.config.v4IdpUrl!, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: this.config.v4ClientId,
client_secret: this.config.v4ClientSecret,
}).toString(),
});
if (!res.ok) {
throw new FlutterwaveApiError(`Failed to fetch v4 OAuth token: ${res.statusText}`, res.status);
}
const data = (await res.json()) as { access_token: string; expires_in: number };
this.v4TokenCache = {
token: data.access_token,
expiresAt: now + data.expires_in * 1000,
};
return data.access_token;
}
/**
* Executes a typed API request to either v3 or v4.
*/
async request<T = unknown>(
version: "v3" | "v4",
path: string,
options: RequestInit & { idempotencyKey?: string } = {},
): Promise<FlwResponse<T>> {
const { idempotencyKey, headers: customHeaders, ...init } = options;
const headers: Record<string, string> = {
"Content-Type": "application/json",
...(customHeaders as Record<string, string>),
};
let baseUrl: string;
if (version === "v3") {
if (!this.config.secretKey) {
throw new Error("Missing FLW_SECRET_KEY for v3 request");
}
baseUrl = this.config.v3BaseUrl!;
headers["Authorization"] = `Bearer ${this.config.secretKey}`;
} else {
baseUrl = this.config.v4BaseUrl!;
const token = await this.getV4Token();
headers["Authorization"] = `Bearer ${token}`;
headers["X-Trace-Id"] = crypto.randomUUID();
if (idempotencyKey) {
headers["X-Idempotency-Key"] = idempotencyKey;
}
}
const url = `${baseUrl}${path.startsWith("/") ? path : `/${path}`}`;
const response = await fetch(url, { ...init, headers });
const body = (await response.json()) as FlwResponse<T>;
if (!response.ok || body.status === "error") {
throw new FlutterwaveApiError(
body.message || `Flutterwave ${version} request failed with status ${response.status}`,
response.status,
body,
);
}
return body;
}
}
よく使われる操作とバージョン対応表
このスキルを使う開発者や AI エージェントは、v3 または v4 のどちらでも任意の操作を実行できます。
| アクション | メソッド | v3 ルート | v4 ベータルート | 主なパラメーター/説明 |
|---|---|---|---|---|
| 標準ホスト型チェックアウト | POST | /payments | /charges | tx_ref、amount、currency、redirect_url、customer: { email, name }。チェックアウト URL を返します。 |
| 取引を ID で確認 | GET | /transactions/{id}/verify | /charges/{id} | 最終的な支払いステータス、決済済み金額、カードトークンを確認します。注文の履行前に必ず実行してください。 |
| 取引を参照で確認 | GET | /transactions/verify_by_reference?tx_ref=... | /charges?reference=... | 加盟店独自の注文/取引参照を使用して支払いを照会します。 |
| 取引一覧 | GET | /transactions | /charges | from、to、status、currency、ページネーションのpageで絞り込みます。 |
| カードへの直接請求 | POST | /charges?type=card | /charges | v3 では 3DES 暗号化ペイロードを使用し、v4 ではauthorizationとともにpayment_method_idを使用します。 |
| 請求の検証(OTP) | POST | /validate-charge | /charges/{id}/validate | 顧客の OTP チャレンジコード(flw_ref、otp)を送信します。 |
| 銀行振込による入金 | POST | /charges?type=bank_transfer | /virtual-accounts | 有効期限タイマー付きの、ナイジェリアの NIP 専用銀行口座を動的に生成します。 |
| モバイルマネー(M-Pesa) | POST | /charges?type=mpesa | /charges | ケニアの顧客のモバイル端末に即時 STK プッシュプロンプトを表示します(KES)。 |
| モバイルマネー(ガーナ) | POST | /charges?type=mobile_money_ghana | /charges | MTN、Telecel(Vodafone バウチャー)、または AirtelTigo のウォレットに請求します(GHS)。 |
| USSD 決済 | POST | /charges?type=ussd | /charges | オフライン決済用に、銀行固有の対話型ダイヤルコード(GTBank の場合は*737*...#など)を返します。 |
| トークン化された請求 | POST | /tokenized-charges | /charges(recurring: true) | 保存済みのcard.tokenを使い、カード情報の入力なしでワンクリックチェックアウトやサブスクリプションの再請求を行います。 |
| 送金を開始(支払い) | POST | /transfers | /transfers | 銀行口座またはモバイルウォレットに資金を支払います。v4 ではpayment_instructionとactionを使用します。 |
| 送金ステータスを取得 | GET | /transfers/{id} | /transfers/{id} | 支払いステータス(SUCCESSFUL、FAILED、PENDING)を確認します。 |
| 送金を再試行 | POST | /transfers/{id}/retries | /transfers/{id}/retry | 失敗した送金を再処理のために再送信します。 |
| 受取人一覧 | GET | /beneficiaries | /transfer-recipients | 保存済みの銀行口座やモバイルウォレットの支払い先を一覧表示します。 |
| 受取人を作成 | POST | /beneficiaries | /transfer-recipients | 受取人(account_number、account_bank、beneficiary_name、currency)を保存します。 |
| バーチャル口座を作成 | POST | /virtual-account-numbers | /virtual-accounts | 顧客のウォレットに直接入金するための、恒久または動的なバーチャル銀行口座を発行します。 |
| バーチャル口座を一括作成 | POST | /bulk-virtual-account-numbers | /virtual-accounts | フィンテックプラットフォーム向けの専用バーチャル口座を一括作成します。 |
| サブアカウントを作成(分割) | POST | /subaccounts | 分割設定 | マーケットプレイスの売上分割用にベンダーのサブアカウントを設定します(split_type: "percentage" | "flat")。 |
| 支払いプランを作成 | POST | /payment-plans | サブスクリプションプラン | サブスクリプションの自動請求間隔(daily、weekly、monthly、yearly)を設定します。 |
| バーチャルカードを作成 | POST | /virtual-cards | カード発行 | 請求先住所を設定した、デジタルドル(USD)または現地通貨のデビットカードを発行します。 |
| 銀行口座を照合 | POST | /accounts/resolve | /accounts/lookup | ナイジェリアの銀行口座番号を検証し、登録されている正式な口座名義人の氏名を取得します。 |
| BVN を検証 | POST | /bvn/match | KYC 検証 | Bank Verification Number が顧客の法的な本人情報と一致することを確認し、コンプライアンス要件を満たします。 |
| 返金を発行 | POST | /transactions/{id}/refund | /refunds | 顧客の支払い元口座へ全額または一部を直接返金します。 |
| チャージバックを管理 | GET / PUT | /chargebacks | /chargebacks | 未解決の異議申し立てを照会し、期限前に反証資料を提出します。 |
| 入金一覧 | GET | /settlements | /settlement | 銀行預金、総取引額、決済処理手数料の控除を照合します。 |
| 請求書の支払いと通話料金のチャージ | POST | /bills | 公共料金の請求書 | 通話料金、モバイルデータバンドル、電力トークンを購入し、ケーブルテレビのサブスクリプションを更新します。 |
アクションコードのレシピ
1. 標準的なチェックアウト決済リンクを作成する(v3)
const flw = new Flutterwave();
const session = await flw.request<{ link: string }>("v3", "/payments", {
method: "POST",
body: JSON.stringify({
tx_ref: `ORD-${Date.now()}`,
amount: "7500.00",
currency: "NGN",
redirect_url: "https://yourdomain.com/checkout/callback",
customer: {
email: "[email protected]",
name: "Amina Bello",
phonenumber: "+2348012345678",
},
customizations: {
title: "Store Checkout",
description: "Payment for order #1082",
},
}),
});
console.log("Customer Payment URL:", session.data.link);
2. 必須のサーバー側取引検証(v3 と v4)
const flw = new Flutterwave();
// v3 verification by Transaction ID
const v3Tx = await flw.request<{
status: string;
amount: number;
currency: string;
tx_ref: string;
card?: { token: string };
}>("v3", `/transactions/${transactionId}/verify`, { method: "GET" });
if (v3Tx.data.status === "successful" && v3Tx.data.amount >= orderAmount) {
// Safe to credit account or ship goods
const savedCardToken = v3Tx.data.card?.token;
}
// v4 Next-Gen verification by Charge ID
const v4Charge = await flw.request<{
status: string;
amount: number;
currency: string;
reference: string;
}>("v4", `/charges/${chargeId}`, { method: "GET" });
3. 即時銀行振込/出金を開始する(v3 と v4)
const flw = new Flutterwave();
// v3 Payout
const v3Payout = await flw.request("v3", "/transfers", {
method: "POST",
body: JSON.stringify({
account_bank: "058", // GTBank
account_number: "0123456789",
amount: 50000,
currency: "NGN",
reference: `PAYOUT-${Date.now()}`,
narration: "Vendor disbursement",
}),
});
// v4 Next-Gen Transfer Orchestrator
const v4Payout = await flw.request("v4", "/transfers", {
method: "POST",
idempotencyKey: `IDEMP-${Date.now()}`,
body: JSON.stringify({
action: "instant",
reference: `V4-PAYOUT-${Date.now()}`,
narration: "Vendor payment",
payment_instruction: {
amount: 50000,
currency: "NGN",
recipient_id: "rcb_B9aAgsdzzl",
},
}),
});
Webhook の取り込みとセキュリティ標準
Flutterwave は、Flutterwave ダッシュボード(Settings > Webhooks)で設定し、verif-hash HTTP ヘッダーで渡されるシークレットハッシュを使用して Webhook を検証します。
import crypto from "node:crypto";
export function handleFlutterwaveWebhook(req: Request) {
const incomingHash = req.headers.get("verif-hash");
const secretHash = process.env.FLW_WEBHOOK_HASH;
if (!incomingHash || !secretHash) {
return new Response("Unauthorized", { status: 401 });
}
// Timing-safe constant-time comparison to prevent side-channel leaks
const a = Buffer.from(incomingHash, "utf8");
const b = Buffer.from(secretHash, "utf8");
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response("Forbidden", { status: 403 });
}
// Respond HTTP 200 immediately to meet Flutterwave's 60-second SLA
// Process the event asynchronously in the background
return new Response(JSON.stringify({ status: "success" }), { status: 200 });
}
テストベクターとサンドボックステスト
| 決済手段 | テスト入力 | 想定される動作 |
|---|---|---|
| 3DS Mastercard | カード:5531881111111111、CVV:123、有効期限:09/32 | OTP が必要 -> 12345 を入力 -> 成功 |
| Verve Card(PIN) | カード:5060666666666666、CVV:111、有効期限:05/29 | PIN 1234 が必要。その後 OTP 12345 を入力 -> 成功 |
| 拒否シミュレーション | カード:5531882222222222、CVV:123、有効期限:09/32 | 直接拒否(Insufficient funds) |
| Kenya M-Pesa | 電話番号:2540700000000 | サンドボックスでシミュレーションされた STK プッシュを自動承認 |
| 銀行口座照会 | 口座番号:0690000031、銀行:044(Access Bank) | 名前を照会:FLW TEST ACCOUNT |
開発者と AI エージェントのベストプラクティス
- バージョンの分離: v3 のリクエストフィールド(例:
customer.emailを直接埋め込む)と v4 の構造(payment_instruction、customer_id)を混在させないでください。 - リダイレクト URL を決して信用しない: 顧客が
redirect_url?status=successfulに到達したというだけで、注文を処理してはいけません。必ずサーバー側で検証してください。 - 一意な参照の使用: すべての決済と出金で一意の
tx_refまたはreferenceを指定し、衝突を避けて冪等性を保証してください。 - 認証情報のセキュリティ:
FLW_SECRET_KEY、FLW_V4_CLIENT_ID、FLW_V4_CLIENT_SECRET、FLW_WEBHOOK_HASHは安全な環境変数に保存してください。認証情報を git にコミットしてはいけません。
コメント
Markdown 対応