# MPP QuickStart 暗号資産または法定通貨によるマシン決済を受け付けるサーバーを構築します。 ## MPP 決済エンドポイントビルダー Machine Payments Protocol (MPP) を使用して、暗号資産と Shared Payment Token (SPT) の両方に対応したサーバーを構築・デプロイする方法を解説します。 ### Stripe Node ライブラリーをインストールする パッケージをインストールし、それをコードにインポートします。また、まったくゼロから開始していて package.json ファイルが必要な場合には、コードエディターのダウンロードリンクを使用してプロジェクトファイルをダウンロードします。 #### npm ライブラリーをインストールします。 ```bash npm install --save stripe ``` #### GitHub または、stripe-node ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-node)ダウンロードします。 ### Stripe Ruby ライブラリーをインストールする Stripe ruby gem をインストールし、require を指定してコードに読み込みます。または、まったくゼロから開始していて Gemfile が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Terminal gem をインストールします。 ```bash gem install stripe ``` #### Bundler この行を Gemfile に追加します。 ```bash gem 'stripe' ``` #### GitHub または、stripe-ruby gem のソースコードを直接 [GitHub から](https://github.com/stripe/stripe-ruby)ダウンロードします。 ### Stripe Java ライブラリーをインストールする ビルドに依存関係を追加し、ライブラリーをインポートします。まったくゼロから開始していてサンプルの pom.xml ファイル (Maven 用) が必要な場合は、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Maven POM に以下の依存関係を追加し、{VERSION} を使用するバージョン番号に置き換えます。 ```bash \ncom.stripe\nstripe-java\n{VERSION}\n ``` #### Gradle build.gradle ファイルに依存関係を追加し、{VERSION} を使用するバージョン番号に置き換えます。 ```bash implementation "com.stripe:stripe-java:{VERSION}" ``` #### GitHub JAR を直接 [GitHub から](https://github.com/stripe/stripe-java/releases/latest)ダウンロードします。 ### Stripe Python パッケージをインストールする Stripe パッケージをインストールし、コードにインポートします。まったくゼロから開始していて requirements.txt が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### pip pip を使用してパッケージをインストールします。 ```bash pip3 install stripe ``` #### GitHub stripe-python ライブラリのソースコードを [GitHub から](https://github.com/stripe/stripe-python)直接ダウンロードします。 ### Stripe PHP ライブラリーをインストールする Composer を使用してライブラリーをインストールし、シークレット API キーで初期化します。まったくゼロから開始していて composer.json ファイルが必要な場合には、コードエディターのリンクを使用してファイルをダウンロードします。 #### Composer ライブラリーをインストールします。 ```bash composer require stripe/stripe-php ``` #### GitHub または、stripe-php ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-php)ダウンロードします。 ### サーバーを設定する ビルドに依存関係を追加し、ライブラリーをインポートします。まったくゼロから開始していて go.mod ファイルが必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Go 必ず Go モジュールを使用してを初期化してください。 ```bash go get -u github.com/stripe/stripe-go/v85 ``` #### GitHub または、stripe-go モジュールのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-go)ダウンロードします。 ### Stripe.net ライブラリーをインストールする .NET または NuGet でパッケージをインストールします。まったくゼロから開始する場合には、設定済みの .csproj ファイルが含まれるファイルをダウンロードします。 #### dotnet ライブラリーをインストールします。 ```bash dotnet add package Stripe.net ``` #### NuGet ライブラリーをインストールします。 ```bash Install-Package Stripe.net ``` #### GitHub または、Stripe.net ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-dotnet)ダウンロードします。 ### Stripe ライブラリーをインストールする パッケージをインストールし、コードにインポートします。まったくゼロから開始していて `package.json` が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 ライブラリーをインストールします。 ```bash npm install --save stripe @stripe/stripe-js next ``` ### Stripe を初期化する シークレットキーを使用して Stripe クライアントを設定し、API バージョンを `2026 年 3 月 4日プレビュー` に設定します。クライアントは暗号資産と SPT 決済手段の両方の決済を処理します。 Stripe アカウントで [暗号資産入金](https://support.stripe.com/questions/get-started-with-pay-with-crypto) が有効になっていることを確認してください。 ### PaymentIntent ハンドラを作成する 暗号資産決済の送金先を決定する関数を作成します。再試行または確認のために既存の決済ヘッダーからアドレスを抽出するか、[新しい PaymentIntent を作成](https://docs.stripe.com/api/payment_intents/create.md) して新しい入金 or 預金アドレスを生成します。 `暗号資産` *決済手段* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) と `入金 or 預金` モードを使用して [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) を作成すると、`deposit_options` を使用してサポート対象のネットワークを指定します。Stripe はリクエストされたネットワークの入金 or 預金アドレスを返します。この関数は、クライアントが *ステーブルコイン* (A cryptocurrency that's pegged to the value of a fiat currency or other asset in order to limit volatility) の送信に使用する Tempo ネットワークアドレスを抽出します。 ### サーバー側の決済ハンドラ SPT を作成する [Mppx.create](https://mpp.dev/sdk/typescript/server/Mppx.create) を使用して、SPT 向けのサーバーサイドの決済ハンドラーを設定します。Stripe の決済処理を使ってカードと Link 決済を受け付けるように構成します。 ### Hono アプリを作成する 両方の決済エンドポイントをホストする Hono サーバーを初期化します。 ### FastAPI アプリを作成する 決済エンドポイントをホストする FastAPI サーバーを初期化します。 ### SPT テストヘルパーを作成する `POST` エンドポイントを追加して、テスト用の SPT を作成します。 本番環境では、保護されたエンドポイントではなく、呼び出し側のクライアントのバックエンドが SPT を発行します。 ### 暗号資産決済エンドポイントの作成 暗号資産決済を受け付ける `/暗号資産/決済済み` エンドポイントを定義します。 - [Mppx.create](https://mpp.dev/sdk/typescript/server/Mppx.create) を使用して、Tempo のサーバー側決済ハンドラを作成します。 - リクエストに有効な決済が含まれていない場合、決済の詳細を含む `402 Payment Required` レスポンスを返します。 - 各リクエストの決済チャレンジを確認します。 - リクエストに有効な決済認証情報が含まれている場合にアクセスを許可します。 ### 暗号資産決済エンドポイントの作成 Tempo で暗号資産による決済を受け付ける `/paid` エンドポイントを定義します。 - [Mpp.create](https://mpp.dev/sdk/python/server/Mpp.create) を使用して、Tempo 用のサーバー側決済ハンドラーを作成します。 - 各リクエストで有効な決済認証情報を確認してください。 - リクエストに有効な決済認証情報が含まれていない場合は、Tempo ハンドラーからの決済の詳細を含む `402 Payment Required` レスポンスを返します。 - リクエストに決済チャレンジが含まれている場合は、Tempo ハンドラーで検証してください。 - 決済認証情報またはチャレンジが有効である場合にのみ、アクセスを許可します。 ### SPT 決済エンドポイントの作成 SPT 決済を受け付ける `/spt/paid` エンドポイントを定義します。 - リクエストに有効な決済が含まれていない場合、決済の詳細を含む `402 Payment Required` レスポンスを返します。 - 各リクエストの決済チャレンジを確認します。 - リクエストに有効な決済認証情報が含まれている場合にアクセスを許可します。 ### 決済手段の設定 SPT を作成するためのクライアント側の決済手段を設定します。`createToken` 関数はバックエンドの `/create-spt` エンドポイントを呼び出して、Shared Payment Token を生成します。 ### 決済チャレンジの取得 保護されたエンドポイントから `402` チャレンジを取得して、決済要件を把握します。 ### 決済手段を指定 使用する決済手段を指定します。この例では、コードはテスト用のクレジットカード決済手段を使用しています。 ### 決済認証情報の作成 決済手段を使用して、チャレンジと決済手段から決済認証情報を作成します。 ### 認証情報を使用して再試行 `オーソリ` ヘッダーの決済認証情報を使用して、保護されたエンドポイントに別のリクエストを行います。 ### サーバーを実行する サーバーを構築して実行し、両方のエンドポイントをテストする。 ```bash npm run dev ``` ### サーバーを実行する サーバーを構築して実行し、`http://localhost:4242/paid`.時にエンドポイントをテストする ```bash uv run python main.py ``` ### 決済なしでテスト 決済なしで暗号資産エンドポイントにリクエストを行い、決済詳細を含む `402` ステータスコードが返されることを確認します。 ```bash curl http://localhost:4242/crypto/paid ``` `402 Payment Required` レスポンスにMPP (機械決済プロトコル) 決済情報が記載されます。 ### 決済なしでテスト 決済なしでエンドポイントにリクエストを行い、決済の詳細を含む `402` ステータスコードが返されることを確認します。 ```bash curl http://localhost:4242/paid ``` `402 Payment Required` レスポンスにMPP (機械決済プロトコル) 決済情報が記載されます。 ### 暗号資産による決済をテストする Tempoの [mppx](https://www.npmjs.com/package/mppx) ツールを使用して暗号資産決済フローをテストします。このツールは MPP を処理し、決済を自動的に完了します。 ```bash mppx http://localhost:4242/crypto/paid ``` `mppx account create` と `mppx account fund` を使用して、テストウォレットを作成し、資金を追加できます。決済が成功すると、サーバーはコンテンツを返します。決済は、[Stripe ダッシュボード](https://dashboard.stripe.com) の**決済**で確認できます。 ### 暗号資産による決済をテストする Tempoの [mppx](https://www.npmjs.com/package/mppx) ツールを使用して暗号資産決済フローをテストします。このツールは MPP を処理し、決済を自動的に完了します。 ```bash mppx http://localhost:4242/paid ``` `mppx account create` と `mppx account fund` を使用して、テストウォレットを作成し、資金を追加できます。決済が成功すると、サーバーはコンテンツを返します。決済は、[Stripe ダッシュボード](https://dashboard.stripe.com)の **Payments** で確認できます。 ### SPT 決済をテストする SPT 決済の場合は、SPT による決済フロー全体を示す付属の `client.ts` スクリプトを使用します。 ```bash npx tsx client.ts ``` クライアントスクリプト: 1. `/spt/paid` から `402` チャレンジを取得します。 1. `/create-spt` ヘルパーエンドポイントを使用して SPT を作成する 1. 決済認証情報を作成する 1. 保護されたリソースにアクセスするための認証情報を使用してリクエストを再試行する 決済は、[Stripe ダッシュボード](https://dashboard.stripe.com) の **決済** で確認できます。 - Node.js 20+ - npm or pnpm package manager - Stripe account with crypto payments enabled - Python 3.12+ - [uv](https://github.com/astral-sh/uv) package manager - Stripe account with crypto payments enabled 1. Install dependencies ~~~ npm install ~~~ 2. Configure environment variables ~~~ cp .env.template .env \# Edit .env with your credentials ~~~ 3. Run the server ~~~ npm run dev ~~~ 1. Install dependencies ~~~ uv sync ~~~ 2. Configure environment variables ~~~ cp .env.template .env \# Edit .env with your credentials ~~~ 3. Run the server ~~~ uv run python main.py ~~~ The server provides two endpoints: - `/crypto/paid`: - Accepts crypto payments on Tempo - `/spt/paid`: - Accepts shared payment tokens (SPTs) payments (cards and Link) The server provides one endpoint: - `/paid`: - Accepts crypto payments on Tempo Make a request without payment to see the `402` response: ~~~ curl http://localhost:4242/crypto/paid curl http://localhost:4242/spt/paid ~~~ Make a request without payment to see the `402` response: ~~~ curl http://localhost:4242/paid ~~~ [mppx](https://www.npmjs.com/package/mppx) is a command-line tool for testing crypto MPP endpoints: ~~~ npx mppx http://localhost:4242/crypto/paid ~~~ You can create and fund a test wallet with: ~~~ npx mppx account create npx mppx account fund ~~~ [mppx](https://www.npmjs.com/package/mppx) is a command-line tool for testing crypto MPP endpoints: ~~~ npx mppx http://localhost:4242/paid ~~~ You can create and fund a test wallet with: ~~~ npx mppx account create npx mppx account fund ~~~ import stripe \# Stripe handles payment processing and provides the crypto deposit address. STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY") if not STRIPE_SECRET_KEY: raise ValueError("STRIPE_SECRET_KEY environment variable is required") stripe.api_key = STRIPE_SECRET_KEY stripe.api_version = "2026-03-04.preview" stripe.set_app_info( "stripe-samples/machine-payments", url="https://github.com/stripe-samples/machine-payments", version="1.0.0", ) # Secret used to secure payment challenges. # https://mpp.dev/protocol/challenges#challenge-binding mpp_secret_key = os.urandom(32).hex() \# In-memory cache for deposit addresses (TTL: 5 minutes, max 1024 entries) # NOTE: For production, use a distributed cache like Redis instead of cachetools payment_cache: TTLCache[str, bool] = TTLCache(maxsize=1024, ttl=300) def _extract_recipient_from_authorization(authorization: str | None) -> str | None: if not authorization or not authorization.startswith("Payment "): return None credential = Credential.from_authorization(authorization) request = _b64_decode(credential.challenge.request) to_address = request.get("recipient") if to_address and isinstance(to_address, str): normalized = to_address.lower() if normalized not in payment_cache: raise ValueError("Invalid payTo address: not found in server cache") return normalized raise ValueError("PaymentIntent did not return expected crypto deposit details") async def create_pay_to_address(request: Request) -> str: """ This function determines where payments should be sent. It either: 1. Extracts the address from an existing payment header (for retry/verification), or 2. Creates a new Stripe PaymentIntent to generate a fresh deposit address. """ recipient = _extract_recipient_from_authorization( request.headers.get("authorization") ) if recipient: return recipient # Create a new PaymentIntent to get a fresh crypto deposit address. decimals = 6 # USDC has 6 decimals amount_in_cents = int(10000 / (10 ** (decimals - 2))) payment_intent = stripe.PaymentIntent.create( amount=amount_in_cents, currency="usd", payment_method_types=["crypto"], payment_method_data={"type": "crypto"}, payment_method_options=cast( Any, { "crypto": { "mode": "deposit", "deposit_options": {"networks": ["tempo"]}, } }, ), confirm=True, ) next_action = payment_intent.get("next_action", {}) deposit_details = next_action.get("crypto_display_details", {}) if not deposit_details: raise ValueError("PaymentIntent did not return expected crypto deposit details") deposit_addresses = deposit_details.get("deposit_addresses", {}) tempo_address = deposit_addresses.get("tempo", {}) pay_to_address = tempo_address.get("address") if not pay_to_address: raise ValueError("PaymentIntent did not return expected crypto deposit details") print( f"Created PaymentIntent {payment_intent['id']} " f"for ${amount_in_cents / 100:.2f} -> {pay_to_address}" ) normalized = pay_to_address.lower() payment_cache[normalized] = True return normalized \# Tempo token address for USD stablecoin PATH_USD = "0x" + "20c" + "0" * 37 app = FastAPI(title="MPP REST API") \# GET /paid - Accept crypto payments on Tempo @app.get("/paid") async def get_api(request: Request): recipient_address = await create_pay_to_address(request) mpp = Mpp.create( method=tempo( currency=PATH_USD, recipient=recipient_address, intents={"charge": ChargeIntent()}, chain_id=42431, ), secret_key=mpp_secret_key, ) result = await mpp.charge( authorization=request.headers.get("authorization"), amount="0.01", ) if isinstance(result, Challenge): return JSONResponse( status_code=402, content={"error": "Payment required"}, headers={"WWW-Authenticate": result.to_www_authenticate(mpp.realm)}, ) _credential, receipt = result response = JSONResponse(content={"foo": "bar"}) response.headers["Authentication-Info"] = receipt.to_payment_receipt() return response if __name__ == "__main__": print("Server listening at http://localhost:4242") uvicorn.run(app, host="0.0.0.0", port=4242) // Stripe handles payment processing for both crypto and SPT methods if (!process.env.STRIPE_SECRET_KEY) { console.error("STRIPE_SECRET_KEY environment variable is required"); process.exit(1); } const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2026-03-04.preview" as any, appInfo: { name: "stripe-samples/machine-payments", url: "https://github.com/stripe-samples/machine-payments", version: "1.0.0", }, }); // Secret used to secure payment challenges // https://mpp.dev/protocol/challenges#challenge-binding const mppSecretKey = crypto.randomBytes(32).toString('base64'); // In-memory cache for deposit addresses (TTL: 5 minutes) // NOTE: For production, use a distributed cache like Redis instead of node-cache const paymentCache = new NodeCache({ stdTTL: 300, checkperiod: 60 }); // This function determines where crypto payments should be sent. It either: // 1. Extracts the address from an existing payment header (for retry/verification), or // 2. Creates a new Stripe PaymentIntent to generate a fresh deposit address. async function createPayToAddress(request: Request): Promise { const authHeader = request.headers.get('authorization') if (authHeader && Credential.extractPaymentScheme(authHeader)) { const credential = Credential.fromRequest(request) const toAddress = credential.challenge.request.recipient as `0x${string}` if (!paymentCache.has(toAddress)) { throw new Error('Invalid payTo address: not found in server cache') } return toAddress } // Create a new PaymentIntent to get a fresh crypto deposit address const decimals = 6; // USDC has 6 decimals const amountInCents = Number(1000000) / Math.pow(10, decimals - 2); const paymentIntent = await stripe.paymentIntents.create({ amount: amountInCents, currency: "usd", payment_method_types: ["crypto"], payment_method_data: { type: "crypto", }, payment_method_options: { crypto: { mode: "deposit", deposit_options: { networks: ["tempo"] }, } as any, }, confirm: true, }); if ( !paymentIntent.next_action || !("crypto_display_details" in paymentIntent.next_action) ) { throw new Error( "PaymentIntent did not return expected crypto deposit details", ); } // Extract the Tempo network deposit address from the PaymentIntent const depositDetails = paymentIntent.next_action .crypto_display_details as any; const payToAddress = depositDetails.deposit_addresses["tempo"] .address as string; console.log( `Created PaymentIntent ${paymentIntent.id} for $${( amountInCents / 100 ).toFixed(2)} -> ${payToAddress}`, ); paymentCache.set(payToAddress, true); return payToAddress; } // Create Mppx instance for SPT payments (cards and Link) const mppxSpt = Mppx.create({ methods: [ mppxStripe.charge({ networkId: 'internal', paymentMethodTypes: ['card', 'link'], secretKey: process.env.STRIPE_SECRET_KEY, }), ], secretKey: mppSecretKey }); const PATH_USD = '0x20c0000000000000000000000000000000000000'; const app = new Hono() // GET /crypto/paid - Accept crypto payments on Tempo app.get('/crypto/paid', async (c) => { const request = c.req.raw const recipientAddress = await createPayToAddress(request) const mppxCrypto = Mppx.create({ methods: [ tempo.charge({ currency: PATH_USD, recipient: recipientAddress, testnet: true, }), ], secretKey: mppSecretKey, }) const response = await mppxCrypto.charge({ amount: '1' })(request) if (response.status === 402) { return response.challenge } return response.withReceipt( Response.json({ data: 'Premium content delivered via crypto!', timestamp: new Date().toISOString(), }) ) }) // GET /spt/paid - Accept SPT payments (cards and Link) app.get('/spt/paid', async (c) => { const request = c.req.raw const result = await mppxSpt.charge({ amount: '1', currency: 'usd', })(request); if (result.status === 402) { return result.challenge } return result.withReceipt( Response.json({ data: 'Premium content delivered via SPT!', timestamp: new Date().toISOString(), }) ) }) serve({ fetch: app.fetch, port: 4242, }); console.log(`Server listening at http://localhost:4242`); console.log(`Crypto endpoint: http://localhost:4242/crypto/paid`); console.log(`SPT endpoint: http://localhost:4242/spt/paid`); ## 次のステップ - [マシン決済](https://docs.stripe.com/payments/machine.md) - [MPP](https://docs.stripe.com/payments/machine/mpp.md)