# 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)