決済・サービス連携 https://docs.hazbase.com/patterns/payments/ IMPLEMENTATION PATTERNS 決済・サービス連携 ウォレット連携とx402を使い、利用と支払いをつなぐ。 ← 実装パターン一覧 具体例 設備のレポートAPIを、支払い後に利用できるようにする 設備の稼働レポートを取得するときに、APIがx402形式で支払い条件を返し、利用者が確認・支払い後にレポートを取得する構成です。商品・注文・閲覧権限は自社サービスで管理し、ウォレットでの支払いをhazBaseへ接続します。 「レポートを選ぶ → 金額を確認する → 支払う → 閲覧する」の流れを構成できます。購入者には同じ注文からの再取得も提供できます。 システム構成と役割 0 1 レポートAPI・注文管理 価格・資産・支払先・有効期限と、対象レポートを紐づけます。 Merchant backend / Order DB 0 2 支払い条件の読取 対応するチェーン・資産を選び、利用者に金額と宛先を表示します。 @hazbase/kit/x402 0 3 認証・支払い セッションと操作承認を使って、支払いを送信します。 @hazbase/kit/wallet / @hazbase/auth 0 4 支払確認・閲覧権限 サーバーで支払いと注文を照合し、閲覧権限を一度だけ発行します。 Payment verification / Access DB データモデル例 業務DBに保存する情報と、チェーンやAPIで管理する情報を対応づけます。フィールド名とサンプル値は、この構成で使うアプリケーション側の設計例です。 項目・値の例 管理する場所 役割・対応関係 orderId / resourceUrl ORDER-001 / report URL 自社サーバー 注文と取得対象の正規URL。購入者のセッションと紐づけます。 paymentRequestId issued request ID 支払い要求 接続先の支払いサービスが発行したIDを使用します。任意の文字列を支払いAPIに渡す構成ではありません。 network / asset / payTo eip155:… / 0x… / 0x… 注文・支払い要求 許可するチェーン、トークン、加盟店の受取先。自社の設定と照合します。 amountAtomic 1000000 注文・支払い要求 最小単位で表した金額。小数桁6なら表示上1単位です。円などの法定通貨の金額とは区別します。 paymentAttemptId / status attempt ID / settled 支払いAPI+DB 送信後の試行IDを保存し、同じ試行の完了状態を確認します。 accessGrant orderId + 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 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 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を委ねる場合も、注文との照合とサービス提供の判定は加盟店サーバーが担います。 関連するAPI・SDK仕様 summarizeX402Request PayX402WithHazbaseWalletInput Wallet payment API Authentication / Wallets サンプルアプリから始める 導入・運用ガイド