# MPP quickstart Build a server that accepts machine payments with crypto or fiat. ## MPP payment endpoint builder Learn how to build and deploy a server that accepts both crypto and SPT payments using MPP. ### Install the Stripe Node library Install the package and import it in your code. Alternatively, if you’re starting from scratch and need a package.json file, download the project files using the Download link in the code editor. #### npm Install the library: ```bash npm install --save stripe ``` #### GitHub Or download the stripe-node library source code directly [from GitHub](https://github.com/stripe/stripe-node). ### Install the Stripe Ruby library Install the Stripe ruby gem and require it in your code. Alternatively, if you’re starting from scratch and need a Gemfile, download the project files using the link in the code editor. #### Terminal Install the gem: ```bash gem install stripe ``` #### Bundler Add this line to your Gemfile: ```bash gem 'stripe' ``` #### GitHub Or download the stripe-ruby gem source code directly [from GitHub](https://github.com/stripe/stripe-ruby). ### Install the Stripe Java library Add the dependency to your build and import the library. Alternatively, if you’re starting from scratch and need a sample pom.xml file (for Maven), download the project files using the link in the code editor. #### Maven Add the following dependency to your POM and replace {VERSION} with the version number you want to use. ```bash \ncom.stripe\nstripe-java\n{VERSION}\n ``` #### Gradle Add the dependency to your build.gradle file and replace {VERSION} with the version number you want to use. ```bash implementation "com.stripe:stripe-java:{VERSION}" ``` #### GitHub Download the JAR directly [from GitHub](https://github.com/stripe/stripe-java/releases/latest). ### Install the Stripe Python package Install the Stripe package and import it in your code. Alternatively, if you’re starting from scratch and need a requirements.txt file, download the project files using the link in the code editor. #### pip Install the package through pip: ```bash pip3 install stripe ``` #### GitHub Download the stripe-python library source code directly [from GitHub](https://github.com/stripe/stripe-python). ### Install the Stripe PHP library Install the library with composer and initialize with your secret API key. Alternatively, if you’re starting from scratch and need a composer.json file, download the files using the link in the code editor. #### Composer Install the library: ```bash composer require stripe/stripe-php ``` #### GitHub Or download the stripe-php library source code directly [from GitHub](https://github.com/stripe/stripe-php). ### Set up your server Add the dependency to your build and import the library. Alternatively, if you’re starting from scratch and need a go.mod file, download the project files using the link in the code editor. #### Go Make sure to initialize with Go Modules: ```bash go get -u github.com/stripe/stripe-go/v84 ``` #### GitHub Or download the stripe-go module source code directly [from GitHub](https://github.com/stripe/stripe-go). ### Install the Stripe.net library Install the package with .NET or NuGet. Alternatively, if you’re starting from scratch, download the files which contains a configured .csproj file. #### dotnet Install the library: ```bash dotnet add package Stripe.net ``` #### NuGet Install the library: ```bash Install-Package Stripe.net ``` #### GitHub Or download the Stripe.net library source code directly [from GitHub](https://github.com/stripe/stripe-dotnet). ### Install the Stripe libraries Install the packages and import them in your code. Alternatively, if you’re starting from scratch and need a `package.json` file, download the project files using the link in the code editor. Install the libraries: ```bash npm install --save stripe @stripe/stripe-js next ``` ### Initialize Stripe Set up the Stripe client with your secret key, and set the API version to `2026-03-04.preview`. The client processes payment for both crypto and SPT payment methods. Make sure you have [crypto payins enabled](https://support.stripe.com/questions/get-started-with-pay-with-crypto) for your Stripe account. ### Create the PaymentIntent handler Create a function that determines where to send crypto payments. Either extract the address from an existing payment header for retries or verification, or [create a new PaymentIntent](https://docs.stripe.com/api/payment_intents/create.md) to generate a new deposit address. When you create a [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) with the `crypto` *payment method* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) and `deposit` mode, specify the networks you want to support using `deposit_options`. Stripe returns deposit addresses for the requested networks. The function extracts the Tempo network address that clients use to send *stablecoins* (A cryptocurrency that's pegged to the value of a fiat currency or other asset in order to limit volatility). ### Create a server-side payment handler SPTs Set up a server-side payment handler for SPTs using [Mppx.create](https://mpp.dev/sdk/typescript/server/Mppx.create). Configure it to accept cards and Link payments using Stripe’s payment processing. ### Create the Hono app Initialize the Hono server that hosts both payment endpoints. ### Create SPT test helper Add a `POST` endpoint to create SPTs for testing. In production, the calling client’s backend would issue SPTs, not your protected endpoint. ### Create crypto payment endpoint Define the `/crypto/paid` endpoint that accepts crypto payments. - Create a server-side payment handler for Tempo using [Mppx.create](https://mpp.dev/sdk/typescript/server/Mppx.create). - Returns a `402 Payment Required` response with payment details when the request doesn’t include a valid payment. - Verifies payment challenges for each request. - Allow access when the request includes a valid payment credential. ### Create SPT payment endpoint Define the `/spt/paid` endpoint that accepts SPT payments. - Returns a `402 Payment Required` response with payment details when the request doesn’t include a valid payment. - Verifies payment challenges for each request. - Allow access when the request includes a valid payment credential. ### Configure the charge method Set up the client-side charge method to create SPTs. The `createToken` function calls your backend’s `/create-spt` endpoint to generate shared payment tokens. ### Get the payment challenge Fetch the `402` challenge from the protected endpoint to understand the payment requirements. ### Provide a payment method Specify the payment method to use. In this example, the code uses a test card payment method. ### Create payment credential Use the charge method to create a payment credential from the challenge and payment method. ### Retry with credential Make another request to the protected endpoint with the payment credential in the `Authorization` header. ### Run the server Build and run your server to test both endpoints. ```bash npm run dev ``` ### Test without payment Make a request to the crypto endpoint without payment to confirm it returns a `402` status code with payment details. ```bash curl http://localhost:4242/crypto/paid ``` You’ll receive a `402 Payment Required` response with Machine Payment Protocol (MPP) payment information. ### Test crypto payments Use Tempo’s [mppx](https://www.npmjs.com/package/mppx) tool to test the crypto payment flow. The tool handles MPP and completes the payment automatically. ```bash mppx http://localhost:4242/crypto/paid ``` You can create and fund a test wallet with `mppx account create` and `mppx fund`. The server returns the content after successful payment. You can confirm the payment in the [Stripe Dashboard](https://dashboard.stripe.com) under **Payments**. ### Test SPT payments For SPT payments, use the included `client.ts` script that demonstrates the full payment flow with SPTs. ```bash npx tsx client.ts ``` The client script: 1. Fetches the `402` challenge from `/spt/paid` 1. Creates an SPT using the `/create-spt` helper endpoint 1. Creates a payment credential 1. Retries the request with the credential to access the protected resource You can confirm the payment in the [Stripe Dashboard](https://dashboard.stripe.com) under **Payments**. - Node.js 20+ - npm or pnpm 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 ~~~ // 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`); ## Next steps - [Machine payments](https://docs.stripe.com/payments/machine.md) - [MPP](https://docs.stripe.com/payments/machine/mpp.md)