本文へ移動
    hazBasehazBaseDocs
    IMPLEMENTATION PATTERNS

    決済・サービス連携

    ウォレット連携とx402を使い、利用と支払いをつなぐ。

    実装パターン一覧
    具体例

    設備のレポートAPIを、支払い後に利用できるようにする

    設備の稼働レポートを取得するときに、APIがx402形式で支払い条件を返し、利用者が確認・支払い後にレポートを取得する構成です。商品・注文・閲覧権限は自社サービスで管理し、ウォレットでの支払いをhazBaseへ接続します。

    「レポートを選ぶ → 金額を確認する → 支払う → 閲覧する」の流れを構成できます。購入者には同じ注文からの再取得も提供できます。

    システム構成と役割

    1. レポートAPI・注文管理

      価格・資産・支払先・有効期限と、対象レポートを紐づけます。

      Merchant backend / Order DB
    2. 支払い条件の読取

      対応するチェーン・資産を選び、利用者に金額と宛先を表示します。

      @hazbase/kit/x402
    3. 認証・支払い

      セッションと操作承認を使って、支払いを送信します。

      @hazbase/kit/wallet / @hazbase/auth
    4. 支払確認・閲覧権限

      サーバーで支払いと注文を照合し、閲覧権限を一度だけ発行します。

      Payment verification / Access DB

    データモデル例

    業務DBに保存する情報と、チェーンやAPIで管理する情報を対応づけます。フィールド名とサンプル値は、この構成で使うアプリケーション側の設計例です。

    項目・値の例管理する場所役割・対応関係
    orderId / resourceUrlORDER-001 / report URL自社サーバー注文と取得対象の正規URL。購入者のセッションと紐づけます。
    paymentRequestIdissued request ID支払い要求接続先の支払いサービスが発行したIDを使用します。任意の文字列を支払いAPIに渡す構成ではありません。
    network / asset / payToeip155:… / 0x… / 0x…注文・支払い要求許可するチェーン、トークン、加盟店の受取先。自社の設定と照合します。
    amountAtomic1000000注文・支払い要求最小単位で表した金額。小数桁6なら表示上1単位です。円などの法定通貨の金額とは区別します。
    paymentAttemptId / statusattempt ID / settled支払いAPI+DB送信後の試行IDを保存し、同じ試行の完了状態を確認します。
    accessGrantorderId + memberId自社DB注文に対する閲覧権限。一意制約で二重発行を防ぎます。

    処理の流れ

    1. 注文に紐づく支払い要求を用意する

      レポートの取得要求に対して、サーバーが支払い条件を返します。支払い要求の作成や加盟店の設定は接続先サービスの契約に合わせます。ここで使用するWallet SDKは、任意の注文を登録するAPIではありません。

      Merchant API → HTTP 402 / paymentRequestId
    2. 条件を確認して表示する

      x402レスポンスを読み、チェーン・資産・支払先・対象URL・金額の上限を自社ポリシーと照合します。利用者に対象レポートと金額を表示し、支払い意思を確認します。

      summarizeX402Request / isX402RequestExpired
    3. 認証済みウォレットから支払う

      emailSession、smartAccountAddress、deviceBindingId、highTrustTokenを認証フローから取得します。承認した金額と宛先をexpectedAmountAtomic・expectedPayToとして送信します。

      payX402WithHazbaseWallet(input)
    4. 同じ試行の完了状態を確認する

      応答が処理中ならpaymentAttemptIdを保存し、status APIで照会します。送信済み、verified、settledなどの状態を区別し、通信が遅いだけで新たな支払いを作らないようにします。

      getX402HazbaseWalletPaymentStatus({ emailSession, paymentRequestId, paymentAttemptId })
    5. サーバーで照合して閲覧を許可する

      加盟店サーバーが信頼できる支払い情報・証明を検証し、注文・購入者・金額・資産・宛先との一致を確認します。DBトランザクションと一意制約で閲覧権限を保存し、対象レポートを返します。

      Server payment verification → accessGrant → report response

    コードを利用する前の準備

    • 接続先で発行された有効なpaymentRequestIdと、対応するチェーン・資産・加盟店受取先を用意します。
    • 支払い確認画面と、Wallet APIで使用するセッション・端末登録・操作承認を組み込みます。
    • 認証トークンをログ、URL、公開設定に保存しません。サンプル関数のauthには、その利用者の認証フローで得た値を渡します。
    Install
    npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0

    以下はアプリケーションから呼び出すTypeScriptの関数です。接続先・権限・対象データを引数として渡します。UI、DBへの保存、処理の再開は、上記の構成に沿って業務側へ組み込めます。

    支払い条件を読み取り、承認した内容で送信する

    inspectReportPaymentで支払い条件を取り出し、表示・確認後にpayApprovedReportを呼び出します。payloadは支払いサービスから受け取ったJSON、policyは自社サービスで定めた条件です。

    pattern-payments.ts
    import { summarizeX402Request, isX402RequestExpired, type HazbaseX402Request } from '@hazbase/kit/x402';
    import { createHazbaseWalletClient, type PayX402WithHazbaseWalletInput } from '@hazbase/kit/wallet';
    import { getAddress } from 'ethers';
    
    export function inspectReportPayment(
      payload: Record<string, unknown>,
      resourceUrl: string,
      policy: { network: string; asset: string; payTo: string; maxAmountAtomic: bigint },
    ) {
      const request = summarizeX402Request(payload, { sourceUrl: resourceUrl }, {
        networks: [policy.network],
        assets: [{ address: policy.asset }],
        requirePayTo: true,
      });
      if (!request) throw new Error('Unsupported payment request.');
      const terms = request.requirement;
      if (terms.resource !== resourceUrl || getAddress(terms.payTo) !== getAddress(policy.payTo)) {
        throw new Error('Payment does not match the requested service.');
      }
      const amount = BigInt(terms.amountAtomic);
      if (amount <= 0n || amount > policy.maxAmountAtomic) throw new Error('Amount is outside policy.');
      if (isX402RequestExpired(request, Date.now())) throw new Error('Payment request expired.');
      return request;
    }
    
    export async function payApprovedReport(
      request: HazbaseX402Request,
      auth: Pick<PayX402WithHazbaseWalletInput,
        'emailSession' | 'smartAccountAddress' | 'deviceBindingId' | 'highTrustToken'>,
    ) {
      // Call only after the user confirms the displayed resource and payment terms.
      if (isX402RequestExpired(request, Date.now())) throw new Error('Payment request expired.');
      const wallet = createHazbaseWalletClient();
      return wallet.payX402WithHazbaseWallet({
        ...auth,
        paymentRequestId: request.paymentRequestId,
        expectedAmountAtomic: request.requirement.amountAtomic,
        expectedPayTo: request.requirement.payTo,
        waitForReceipt: false,
      });
    }
    
    コード例をダウンロード

    実行結果と、アプリケーションへの反映

    応答のpaymentRequestId・paymentAttemptIdを保持して状態を追跡します。フロントエンドから送られたpaid: trueだけでは閲覧権限を与えず、加盟店サーバーの検証結果で確定します。

    実装で押さえておきたい点

    金額の表示と有効期限

    支払い条件の表示値だけでなく最小単位の金額を照合します。SDKの期限確認は受信時刻に基づく補助判定なので、注文の絶対的な有効期限もサーバーで検証します。

    決済と閲覧権限の二重処理

    タイムアウト時は同じ試行を照会し、注文ごとに支払状態を保持します。支払い完了の通知が複数回来ても、同じ閲覧権限を重複発行しない構成にします。

    支払先や対象の差し替え

    決済対象のURL・資産・チェーン・宛先を注文の正と照合します。ログイン済みの購入者と要求IDの関連も、サーバー側で確認します。

    用途に合わせた拡張

    従量課金・継続利用

    1回のレポート購入から始め、利用枠や期限付きアクセスへ拡張できます。どの支払いが何回・いつまでの利用を許可するかは、サービス側の権限モデルで管理します。

    ウォレット拡張との連携

    x402要求をページ上で公開し、対応ウォレットへ受け渡す構成も選べます。支払いUIを委ねる場合も、注文との照合とサービス提供の判定は加盟店サーバーが担います。