Menu
Sections
BitVoy EC Payment API

Stablecoin Payment for
Your Online Store

独自ECサイトにUSDC / JPYC / USDTなどのステーブルコイン決済をAPI連携で導入。 Intent発行 → 決済ページリダイレクト → Webhook通知の3ステップで、 暗号資産による支払いを即日受付できます。

特長

ECサイトにステーブルコイン決済を導入するための、すべての機能を備えています。

シンプルなAPI連携

REST APIでIntent(決済要求)を発行し、ユーザーをBitVoy決済ページにリダイレクトするだけ。複雑なブロックチェーン処理はBitVoyが全て代行します。

Webhook通知

決済完了時にWebhookで即座に通知。HMAC署名付きで改ざん検知も万全です。注文ステータスの自動更新に最適です。

リアルタイム為替レート

CoinGecko APIを利用し、JPY / USD等の法定通貨価格をリアルタイムでステーブルコインに変換。サーバーサイドで正確な金額を算出できます。

複数通貨 × 複数チェーン

顧客が好みのステーブルコインとチェーンを選択可能。

USDC JPYC USDT
Avalanche Base

ガスレス決済(AA対応)

ERC-4337 Account Abstractionにより、ユーザーのガス代をPaymasterがスポンサー。顧客はガス代を気にせず決済できます。

直接入金

決済が完了すると、指定のウォレットアドレスに直接送金されます。中間の保管はなく、数秒で資金を受け取れます。


決済フロー

BitVoy EC Payment APIは、Payment Intent方式を採用しています。ECサイトのサーバーからIntentを発行し、顧客をBitVoy決済ページにリダイレクト。決済完了後、WebhookとリダイレクトでECサイトに結果が通知されます。

ECサイト
注文確定
チェックアウト
サーバー
Intent発行
POST /intents
BitVoy
決済ページ
パスキー認証
+ 送金
Webhook
intent.succeeded
注文確定処理
完了ページ
return_url
リダイレクト

詳細シーケンス

Customer EC Site BitVoy API Blockchain | | | | | 注文確定 | | | |-------------------->| | | | | POST /intents | | | |-------------------->| | | | intent_id | | | | payment_start_url | | | |<--------------------| | | リダイレクト | | | |<--------------------| | | | | | | | パスキー認証 + 送金承認 | | |-------------------------------------------->| | | | | トランザクション送信 | | | |-------------------->| | | | tx確認 | | | |<--------------------| | | Webhook: succeeded | | | |<--------------------| | | | (注文ステータス更新) | | | return_urlリダイレクト | | |<--------------------------------------------| | | | | |

Intent ステータス遷移

CREATED PRESENTED AUTHORIZED PROCESSING SUCCEEDED
ステータス説明
CREATEDIntent発行済み、ユーザー未操作
PRESENTEDユーザーが決済画面を表示
AUTHORIZEDパスキー認証・送金承認完了
PROCESSINGトランザクション送信済み、チェーン確認待ち
SUCCEEDED決済完了(ブロック確認済み)
FAILED送金失敗またはトランザクション revert
EXPIRED有効期限切れ(デフォルト15分)
CANCELEDユーザーがキャンセル

API リファレンス

ベースURL: https://bitvoy.org

POST /oidc-payment/intents

Payment Intentを発行します。ECサイトのサーバーサイドから呼び出してください。

リクエストパラメータ

パラメータ必須説明
rp_client_id必須クライアントID
client_secret必須クライアントシークレット
order_ref必須ECサイト側の注文番号(冪等性キー)
amount必須金額(人間が読める単位。例: "100" = 100 JPYC)
currency必須通貨コード: USDC / JPYC / USDT
payee必須入金先ウォレットアドレス(文字列またはオブジェクト)
chain必須avalanche / base
execution_mode任意STANDARD(デフォルト)/ AA(ガスレス)
network任意mainnet(デフォルト)/ testnet
expires_in任意有効期限(秒、デフォルト: 900 = 15分)
return_url任意決済完了後のリダイレクト先URL
metadata任意ECサイト固有の追加情報(JSONオブジェクト)

レスポンス例

{
  "intent_id": "int_01KK8VGFWGNQ62CAB3MA1N69B7",
  "status": "CREATED",
  "expires_at": "2026-03-20T09:15:00.000Z",
  "intent_token": "eyJ...",
  "payment_start_url": "https://bitvoy.org/oidc/authorize?...intent_id=int_xxx"
}

payment_start_url にユーザーをリダイレクトすると、BitVoy決済ページが表示されます。

GET /oidc-payment/intents/{intent_id}

Intentのステータスと決済結果を確認します。

認証

Authorization: Basic BASE64(client_id:) または ?client_id={client_id}

レスポンス例(SUCCEEDED)

{
  "intent_id": "int_01KK8VGFWGNQ62CAB3MA1N69B7",
  "status": "SUCCEEDED",
  "amount": "100",
  "currency": "JPYC",
  "chain": "avalanche",
  "order_ref": "ORDER-12345",
  "result": {
    "paid_at": "2026-03-20T09:01:00.000Z",
    "tx_hash": "0xf7a41da56eb6b4b276bc3c7d84d94d6944cc8fc9...",
    "chain": "avalanche",
    "paid_amount": "100"
  }
}

Webhook通知

Intentのステータスが変化するたびに、登録された webhook_url にPOSTリクエストが送信されます。

イベント一覧

イベントタイミング
intent.succeeded決済完了時
intent.failed決済失敗時
intent.expired有効期限切れ時

Webhookペイロード例(intent.succeeded)

{
  "event": "intent.succeeded",
  "timestamp": "2026-03-20T09:01:00.000Z",
  "intent": {
    "intent_id": "int_01KK8VGFWGNQ62CAB3MA1N69B7",
    "status": "SUCCEEDED",
    "order_ref": "ORDER-12345",
    "amount": "100000000000000000000",
    "currency": "JPYC",
    "chain": "avalanche",
    "network": "mainnet",
    "payee": {
      "type": "address",
      "address": "0x1234...abcd"
    },
    "created_at": "2026-03-20T09:00:00.000Z",
    "expires_at": "2026-03-20T09:15:00.000Z",
    "result": {
      "paid_at": "2026-03-20T09:01:00.000Z",
      "tx_hash": "0xf7a41da56eb6b4b276bc3c7d84...",
      "chain": "avalanche",
      "network": "mainnet",
      "paid_amount": "100000000000000000000"
    }
  }
}

注意: Webhookペイロード内の amount / paid_amount はminor unit(JPYC: 18桁、USDC: 6桁)です。

Webhookヘッダー

Webhookリクエストには以下のヘッダーが付与されます:

Header説明
X-BitVoy-Eventイベント名(例: intent.succeeded
X-BitVoy-Timestamp送信時刻(ISO 8601)
X-BitVoy-SignatureHMAC-SHA256署名(下記参照)

署名検証

Webhookリクエストには X-BitVoy-Signature ヘッダーが付与されます:

X-BitVoy-Signature: sha256=HEX(HMAC-SHA256(payload, webhook_secret))

導入手順

1

クライアント登録を申請

登録フォームから申請してください。以下の情報をご用意いただきます:

登録完了後、以下の認証情報が発行されます:

2

サーバーサイドにIntent発行を実装

チェックアウト時にサーバーからBitVoy APIを呼び出し、Payment Intentを発行します。 client_secret は必ずサーバーサイドで管理し、フロントエンドに露出させないでください。

3

Webhook受信エンドポイントを実装

intent.succeeded イベントを受け取り、注文ステータスを「支払い済み」に更新します。 必ず X-BitVoy-Signature を検証してください。

4

決済完了ページを実装

return_url に指定したURLでユーザーを受け取り、注文完了画面を表示します。 クエリパラメータに intent_idtxid が付与されます。


実装例

Node.js (Express)

Intent発行(チェックアウト)

const express = require('express');
const app = express();

const BITVOY_API = 'https://bitvoy.org';
const CLIENT_ID = process.env.BITVOY_CLIENT_ID;
const CLIENT_SECRET = process.env.BITVOY_CLIENT_SECRET;
const WEBHOOK_SECRET = process.env.BITVOY_WEBHOOK_SECRET;
const PAYEE_ADDRESS = '0x...';  // 入金先アドレス

// チェックアウト: Intent発行 → リダイレクト
app.post('/checkout', async (req, res) => {
  const { orderId, amount, currency, chain } = req.body;

  const resp = await fetch(`${BITVOY_API}/oidc-payment/intents`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      rp_client_id: CLIENT_ID,
      client_secret: CLIENT_SECRET,
      order_ref: orderId,
      amount: amount,          // 例: "4500" (4500 JPYC)
      currency: currency,      // 例: "JPYC"
      payee: PAYEE_ADDRESS,
      chain: chain,            // 例: "avalanche"
      execution_mode: 'AA',   // ガスレス決済
      return_url: `https://shop.example.com/orders/${orderId}/complete`,
      metadata: { source: 'my-ec-site' }
    })
  });

  const data = await resp.json();

  // intent_id を注文に紐付けて保存
  await saveIntentToOrder(orderId, data.intent_id);

  // ユーザーをBitVoy決済ページにリダイレクト
  res.json({ payment_url: data.payment_start_url });
});

Webhook受信

const crypto = require('crypto');

app.post('/webhook/bitvoy', express.raw({ type: 'application/json' }), async (req, res) => {
  // 1. 署名を検証
  const signature = req.headers['x-bitvoy-signature'];
  const expected = 'sha256=' + crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const payload = JSON.parse(req.body);

  // 2. intent.succeeded イベントを処理
  if (payload.event === 'intent.succeeded') {
    const { intent_id, order_ref, result } = payload.intent;

    // 注文を「支払い済み」に更新
    await updateOrderStatus(order_ref, {
      status: 'paid',
      tx_hash: result.tx_hash,
      paid_at: result.paid_at,
      intent_id: intent_id
    });
  }

  res.json({ received: true });
});

決済完了ページ(return_url)

// ユーザーがBitVoy決済ページから戻ってくるURL
app.get('/orders/:orderId/complete', async (req, res) => {
  const { intent_id, txid } = req.query;
  const order = await getOrder(req.params.orderId);

  // Webhookが先に到達していれば、既に paid になっている
  // まだの場合はポーリングで確認
  if (order.status !== 'paid' && intent_id) {
    const resp = await fetch(
      `${BITVOY_API}/oidc-payment/intents/${intent_id}?client_id=${CLIENT_ID}`
    );
    const intent = await resp.json();
    if (intent.status === 'SUCCEEDED') {
      await updateOrderStatus(order.id, { status: 'paid' });
    }
  }

  res.render('order-complete', { order, txid });
});

Python (Flask)

Intent発行

import requests, os

BITVOY_API = 'https://bitvoy.org'
CLIENT_ID = os.environ['BITVOY_CLIENT_ID']
CLIENT_SECRET = os.environ['BITVOY_CLIENT_SECRET']

@app.route('/checkout', methods=['POST'])
def checkout():
    data = request.get_json()

    resp = requests.post(f'{BITVOY_API}/oidc-payment/intents', json={
        'rp_client_id': CLIENT_ID,
        'client_secret': CLIENT_SECRET,
        'order_ref': data['order_id'],
        'amount': data['amount'],
        'currency': 'USDC',
        'payee': '0x...',
        'chain': 'base',
        'execution_mode': 'AA',
        'return_url': f'https://shop.example.com/orders/{data["order_id"]}/complete',
    })

    intent = resp.json()
    return jsonify({'payment_url': intent['payment_start_url']})

Webhook検証

import hmac, hashlib

@app.route('/webhook/bitvoy', methods=['POST'])
def webhook():
    payload = request.get_data()
    sig = request.headers.get('X-BitVoy-Signature', '')

    expected = 'sha256=' + hmac.new(
        WEBHOOK_SECRET.encode(), payload, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(sig, expected):
        return jsonify({'error': 'Invalid signature'}), 401

    data = request.get_json()
    if data['event'] == 'intent.succeeded':
        intent = data['intent']
        update_order(intent['order_ref'], status='paid',
                     tx_hash=intent['result']['tx_hash'])

    return jsonify({'received': True})

フロントエンド(決済ボタン)

<button id="pay-btn">ステーブルコインで支払う</button>

<script>
document.getElementById('pay-btn').addEventListener('click', async () => {
  const res = await fetch('/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      order_id: 'ORDER-12345',
      amount: '4500',
      currency: 'JPYC',
      chain: 'avalanche'
    })
  });
  const { payment_url } = await res.json();

  // BitVoy決済ページにリダイレクト
  window.location.href = payment_url;
});
</script>

よくある質問

Shopifyじゃなくても使えますか?

はい。BitVoy EC Payment APIはプラットフォーム非依存のREST APIです。Node.js、Python、PHP、Ruby、Go等、どのバックエンドからでもHTTPリクエストで連携できます。

既存の決済方法(クレジットカード等)に影響はありますか?

ありません。BitVoyはチェックアウト時の追加の決済手段として導入するだけで、既存の決済フローとは独立して動作します。

対応しているステーブルコインとチェーンは?

USDC、JPYC、USDTに対応しています。チェーンはAvalancheとBaseをサポートしています。チェーンごとのガス代と速度が異なるため、用途に合わせて選択してください。

ガスレス決済(AA)とは?

ERC-4337 Account Abstraction を利用し、Paymasterが顧客のガス代を肩代わりします。execution_mode: "AA" を指定するだけで有効になり、顧客はガス代を意識せずステーブルコインのみで決済できます。

為替レートはどのように計算されますか?

法定通貨からステーブルコインへの変換が必要な場合は、ECサイト側で変換してからIntent発行時の amount に指定してください。BitVoy側でも /shopify/rate エンドポイントでCoinGeckoベースのレート取得が可能です。

手数料はかかりますか?

STANDARDモード: 決済額の1.0%(USDC/JPYCはガス代無料、USDTは初回のみ承認ガス代あり)。
AAモード(PREMIUM): ガス代完全無料(弊社負担)。
Avalanche: 0.5% + ¥20/件(JPY) / 0.5% + $0.15/件(USD)
Base: 0.5% + $0.10/件(USD)
最低取引金額: ドル建て $2 / 円建て ¥300

入金のタイミングは?

顧客がステーブルコインで支払うと、ブロックチェーン上のトランザクション確定後(通常数秒)に、設定した入金先ウォレットアドレスへ直接送金されます。中間の保管はありません。

Webhookが届かない場合は?

Webhookが届かない場合でも、GET /oidc-payment/intents/{intent_id} でステータスをポーリングできます。return_urlへのリダイレクト時にも intent_id が付与されるため、決済結果を確実に確認できます。

同じ注文で二重決済は発生しますか?

発生しません。order_ref にはクライアントごとにユニーク制約があるため、同じ注文番号で重複してIntentを発行することはできません。

テスト環境はありますか?

はい。Intent発行時に network: "testnet" を指定するとテストネット(Fuji / Base Sepolia等)で動作します。テスト用のステーブルコインで決済フローを確認できます。

返品・返金はどうなりますか?

ブロックチェーン上のトランザクションは取り消しできないため、返金はストア側からウォレットアドレスへ手動で送金する形になります。ECサイト上での返金処理は通常通り記録可能です。

導入のご相談

ステーブルコイン決済の導入に興味がありましたら、お気軽にお問い合わせください。