# Collect customer information for machine payments

Learn how to collect request context and customer details in machine-payable endpoints.

After you monetize your API for agents, you can collect information about the agent or the customer it represents. Use this information to associate transactions with accounts, allow or block buyers based on specific criteria, and calculate taxes.

This guide explains how to:

- [Analyze and control request traffic](https://docs.stripe.com/payments/machine/mpp/collect-customer-information.md#analyze-and-control-request-traffic)
- [Collect information for fulfillment](https://docs.stripe.com/payments/machine/mpp/collect-customer-information.md#collect-information-for-fulfillment)
- [Associate repeat payments with a Customer](https://docs.stripe.com/payments/machine/mpp/collect-customer-information.md#associate-repeat-payments-with-a-customer)
- [Retrieve billing details from the payment credential](https://docs.stripe.com/payments/machine/mpp/collect-customer-information.md#retrieve-billing-details-from-the-payment-credential)

## Before you begin

Make sure you’ve already [set up a machine-payable endpoint](https://docs.stripe.com/payments/machine/mpp.md).

## Analyze and control request traffic

Incoming requests can provide useful context to understand and control traffic to your endpoint. For example, headers might include a `User-Agent` or request ID, while request metadata might include the endpoint being accessed and the client IP address.

Use this context to:

- Correlate requests and payments with application logs and IDs
- Understand which endpoints and client software generate traffic
- Apply controls such as IP-based rate limits and blocking

Clients and proxies can obscure, omit, or modify these values. Don’t use them to verify identity.

To attach this information to the payment, add non-sensitive data such as an endpoint label, request ID, or user agent to the [metadata](https://docs.stripe.com/api/metadata.md) of your [PaymentIntent](https://docs.stripe.com/api/payment_intents/object.md). With [mppx](https://github.com/wevm/mppx), set metadata through the `paymentIntentOptions` charge parameter.

```typescript
export async function handler(request: Request) {
  // Validation and other pre-charge logic.
  const userAgent = request.headers.get('user-agent') || 'undefined';

  const result = await mppx.charge(
    {
      amount: '0.50',
      currency: 'usd',
      decimals: 2,
      description: 'API request',
      paymentIntentOptions: {
        metadata: {
          // Metadata values can contain up to 500 characters.
          user_agent: userAgent.slice(0, 500),
        },
      },
    }
  )(request);

  // Payment validation and fulfillment logic.
}
```

To verify your integration, create a test mode payment and inspect the resulting PaymentIntent:

```bash
npx @stripe/link-cli mpp pay http://localhost:4242/paid \
  -X POST \
  -d '{}' \
  --context "Testing machine payments integration on Stripe MPP using the link-cli on http://localhost:4242/paid." \
  --test
```

In the [Dashboard](https://dashboard.stripe.com/payments), open the most recent payment and confirm that its metadata includes `user_agent`.

## Collect information for fulfillment

To fulfill some requests, you might need additional information, such as a query, email address, name, or delivery address.

Require the agent to provide this information in the request body or URL parameters. Validate all required fields before you return a payment challenge. If a field is missing or invalid, return a clear, actionable error that identifies the field and its expected format.

In the following example, you require the agent to provide the customer’s name and email address before you return a payment challenge.

```typescript
async function extractCustomerDetails(request: Request) {
  const body = await request.clone().json().catch(() => null);

  const name = typeof body?.name === 'string' ? body.name.trim() : '';
  const email = typeof body?.email === 'string' ? body.email.trim() : '';

  // This example just checks for existence. Apply format, length, and other validation required for your application.
  if (!name || !email) {
    return null;
  }

  return { name, email };
}

function customerDetailsErrorResponse() {
  return Response.json(
    {
      error:
        'Provide a name and email address in the request body. Example: {"name":"Ada Lovelace","email":"ada@example.com"}.',
    },
    { status: 400 }
  );
}

export async function paid(request) {
  const customerDetails = await extractCustomerDetails(request);
  if (!customerDetails) return customerDetailsErrorResponse();

  // Continue with payment and fulfillment.
}
```

## Associate repeat payments with a Customer

Attach a [Customer](https://docs.stripe.com/api/customers/object.md) ID to the PaymentIntent so you can view related payments and customer details in the [Stripe Dashboard](https://dashboard.stripe.com/customers). For authenticated requests, use the Customer associated with the account. For unauthenticated requests, use information from the agent, such as an email address, to find or create a Customer and group related payments when possible.

Use a [metadata](https://docs.stripe.com/api/metadata.md) field, such as `machine_payment: 'true'`, to identify Customers created through the unauthenticated flow. The following example finds or creates a Customer using the name and email address provided by the agent.

> Don’t perform expensive operations or operations with side effects before a request provides a payment credential. With `mppx`, pass a resolver function to `paymentIntentOptions` to defer these operations until after the request provides a credential and, when possible, `mppx` validates it.

```typescript
import Stripe from 'stripe';

const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!);

async function findOrCreateCustomer(email, name, idempotencyKey) {
  const customers = await stripeClient.customers.list({
    email,
    limit: 100,
  });

  const existingCustomer = customers.data.find(
    (customer) => customer.metadata.machine_payment === 'true'
  );

  if (existingCustomer) {
    return existingCustomer.id;
  }

  const customer = await stripeClient.customers.create(
    {
      email,
      name,
      metadata: {
        machine_payment: 'true',
      },
    },
    idempotencyKey ? { idempotencyKey } : undefined
  );

  return customer.id;
}
```

```typescript
export async function paid(request) {
  const customerDetails = await extractCustomerDetails(request);
  if (!customerDetails) return customerDetailsErrorResponse();
  const { email, name } = customerDetails;

  const payment = await mppx.charge({
    amount: '0.50',
    paymentIntentOptions: async ({ challenge }) => ({
      customer: await findOrCreateCustomer(email, name, `mpp_customer_${challenge.id}`),
    }),
  })(request);

  // Validate payment and run fulfillment logic.
}
```

To test your integration, create a test mode payment that includes a name and email address in the request body:

```bash
npx @stripe/link-cli mpp pay http://localhost:4242/paid \
  -X POST \
  -d '{"email": "ada@example.com", "name": "Ada Lovelace"}' \
  --context "Testing machine payments integration on Stripe MPP using the link-cli on http://localhost:4242/paid." \
  --test
```

In the [Dashboard](https://dashboard.stripe.com/customers), open the most recent Customer and confirm that the payment appears in the **Payments** section.

## Retrieve billing details from the payment credential

When an agent pays with a [Shared Payment Token (SPT)](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens.md), retrieve the granted token to access details about the underlying payment method. Use these details to complete customer or billing information that the agent doesn’t provide and to make fulfillment and risk decisions.

The following example extracts the SPT from an incoming request and returns it.

```typescript
import { Credential } from 'mppx';

export function extractSpt(request) {
  let credential;

  try {
    credential = Credential.fromRequest(request);
  } catch {
    return undefined;
  }

  const spt = credential.payload?.spt;

  if (!spt) {
    return undefined;
  }

  return spt;
}
```

Use the SPT returned by `extractSpt` to retrieve the granted token:

```javascript
// This example uses the public preview SDK. See https://github.com/stripe/stripe-node#public-preview-sdks
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

async function retrieveGrantedToken(request) {
  const spt = extractSpt(request);

  if (!spt) {
    return undefined;
  }

  return stripe.sharedPayment.grantedTokens.retrieve(spt);
}
```

Available fields vary by payment method, so make sure your integration handles missing values. Key fields include:

- `payment_method_details.billing_details`: The customer’s name, email address, phone number, and billing address.
- `payment_method_details.card`: The card’s brand and country.

For all possible response fields, see the [Granted Shared Payment Token API reference](https://docs.stripe.com/api/shared-payment/granted-token/retrieve.md).
