---
title: Pine Labs Payment Protocol - Quickstart
slug: ai/p3p/quickstart
hidden: false
sidebar_order: 1
metadata:
  title: P3P Quickstart – Build Agent-Native Payment Flows | Pine Labs Online
  description: >-
    Get started with Pine Labs P3P Quickstart to build agent-native payment
    experiences where AI agents securely discover, authorize, and complete paid
    API requests.
  keywords: 'MPP Payments, Machine Payments'
excerpt: >-
  Create agent-native payment experiences where AI agents securely discover,
  authorize, and complete paid API requests through a standardized machine
  payment protocol.
sidebar_label: Quickstart
---
Build one paid endpoint first. The Server SDK protects the route and returns `402` until the Client retries with a payment credential. The Client SDK handles challenge parsing, token creation, retry, and receipt parsing.

## Supported Payment Methods

**1. UPI ReservePay** (Available now) -  Customer approves a mandate and every paid request debits the approved reserve. <br/>
**2. Cards** (Future scope) -  P3P challenge and receipt stay the same; funding and capture become card-specific. <br/>
**3. Crypto** (Future scope) - P3P challenge and receipt stay the same; wallet, signature, and settlement behavior become rail-specific.


## Payment Lifecycle

<CodeTabs>
  <Tab label="UPI ReservePay">

```mermaid
sequenceDiagram
  participant User as User
  participant Client as Client
  participant Server as Server
  participant P3P as Pine Labs P3P
  participant UPI as UPI ReservePay
 
  User->>Client: Approve spending setup
  Client->>Server: Request UPI ReservePay mandate setup
  Server->>P3P: Create UPI ReservePay mandate
  P3P->>UPI: Request mandate approval and reserve
  Server-->>Client: Mandate response
  User->>UPI: Approve mandate in UPI app
  UPI-->>P3P: Mandate approved and amount reserved
  Client->>Server: Poll mandate status
  Server->>P3P: Read mandate status
  P3P-->>Server: Active mandate
  Server-->>Client: Active mandate
  Client->>Server: Request paid resource
  Server-->>Client: 402 + WWW-Authenticate: Payment
  Client->>P3P: Create token against mandate
  P3P-->>Client: Scoped Payment token
  Client->>Server: Payment with retry credential
  Server->>P3P: Capture Payment
  P3P->>UPI: Debit against reserved mandate
  P3P-->>Server: Receipt confirmation
  Server-->>Client: Payment-Receipt
```
  </Tab>
</CodeTabs>



## Setup Guide
Follow the below steps to integrate with Pine Labs payment protocol

1. [Configure Credentials](#configure-credentials)
2. [Setup Customer Mandate](#setup-customer-mandate)
3. [Create a Paid Server Endpoint](#create-paid-server-endpoint)
4. [Call The Paid Resource](#call-paid-service)
5. [Verify The Result](#verify-result)
6. [Go Live](#go-live)



## Prerequisites — Get Your API Credentials

<TwoColumn ratio="1:1" gap="2rem" align="start">
  <Column>

Before you begin integrating, create your free Pine Labs Online developer account to get access to UAT credentials, API keys, and the merchant dashboard.

1. **Sign Up** — Visit the Pine Labs Online Dashboard and register with your business email. 
<br/><a className="hidden sm:inline-flex btn-light-green px-3.5 py-1.5 text-sm shadow-sm" href="https://dashboardv2.pluralonline.com/signup">Create Account</a>
2. **Verify Your Email** — Confirm your email address through the verification link sent to your inbox.
3. **Find Your Credentials** — Once logged in, navigate to **Settings → API Keys** to generate your test-mode API key and secret.
4. **Copy both credentials** — These are your **PINELABS\_CLIENT\_ID** and **PINELABS\_CLIENT\_SECRET** values. Store them securely — you will need them in the next step.
  </Column>
  <Column>

<DocImage src="images/dashboard/signup.png" alt="Pine Labs Dashboard" align="center" />
 </Column>

</TwoColumn>

<Callout type="warning" title="Keep credentials server-side">
Do not expose PINELABS_CLIENT_SECRET in browser code. Use them only in backend services, agents, or server-side runtimes.
</Callout>


<h2 id="configure-credentials"> 1. Configure Credentials</h2>

Set these environment variables in the backend runtime that owns the SDK calls:

```bash
PINELABS_CLIENT_ID="<<PINELABS_CLIENT_ID>>"
PINELABS_CLIENT_SECRET="<<PINELABS_CLIENT_SECRET>>"
```

`PINELABS_CLIENT_ID` and `PINELABS_CLIENT_SECRET` come from Pine Labs after merchant onboarding. Use sandbox credentials with `P3PEnvironment.SANDBOX` and  production credentials with `P3PEnvironment.PRODUCTION`.

The server SDK derives the local P3P challenge HMAC key from `PINELABS_CLIENT_SECRET` internally. There is no separate challenge-signing value to configure or share.



<h2 id="setup-customer-mandate"> 2. Setup Customer Mandate</h2>

For the current UPI ReservePay rail, the customer must have an active mandate before paid requests can complete. Create that mandate with the server SDK before the client starts calling paid routes.

<Tabs>
<Tab label="TypeScript">

```ts
import {
  Amount,
  PaymentMethod,
  PineLabsOnlineP3P
} from "@pine-labs-online/p3p-server-sdk";

const p3p = PineLabsOnlineP3P.create(config);

const mandate = await p3p.createMandate({
  customerReference: "customer-ref-123",
  mobileNumber: "9876543210",
  amount: new Amount(100000, "INR"),
  validityInDays: 20,
  paymentMethod: PaymentMethod.UPI_RESERVE_PAY
});
```

</Tab>
<Tab label="Python">

```python
from pinelabs_p3p_server import (
    Amount,
    CreateMandateOptions,
    PaymentMethod,
    PineLabsOnlineP3P,
)

p3p = PineLabsOnlineP3P.create(config)

mandate = p3p.create_mandate(CreateMandateOptions(
    customerReference="customer-ref-123",
    mobileNumber="9876543210",
    amount=Amount(value=100000, currency="INR"),
    paymentMethod=PaymentMethod.UPI_RESERVE_PAY,
    validityInDays=20,
))
```

</Tab>
</Tabs>

The amount uses paise. `100000` means Rs `1,000`.

The createMandate response includes a **deep_link** field — a UPI intent URL that the customer must approve in their UPI app. Follow these steps:

1. **Extract deep_link**— Read **deep_link** from the create mandate response.
2. **Generate a QR code** — Use any QR library (e.g. qrcode in Python, qrcode.react in JS) to encode the **deep_link** into a scannable QR.
3. **Display to customer** — Show the QR code on your UI. The customer scans it with their UPI app to approve the mandate.
4. **Poll for activation** — After the customer scans, poll the mandate status until it becomes ACTIVE.


<h2 id="create-paid-server-endpoint"> 3. Create a Paid Server Endpoint</h2>

<Tabs>
<Tab label="TypeScript">

```ts
import {
  Amount,
  ChargeOptions,
  P3PEnvironment,
  PaymentGateway,
  PaymentMethod,
  decidePayment
} from "@pine-labs-online/p3p-server-sdk";

const config = {
  clientId: process.env.PINELABS_CLIENT_ID!,
  clientSecret: process.env.PINELABS_CLIENT_SECRET!,
  paymentGateway: PaymentGateway.PineLabsOnline,
  availablePaymentMethods: [PaymentMethod.UPI_RESERVE_PAY],
  env: P3PEnvironment.SANDBOX,
  realm:  P3PEnvironment.SANDBOX
};

export async function GET(request: Request) {
  const decision = await decidePayment({
    credentialHeader: request.headers.get("P3P-Credential") ?? undefined,
    config,
    chargeOptions: new ChargeOptions(
      new Amount(10000, "INR"),
      "/api/weather"
    )
  });

  if (decision.action !== "proceed") {
    return Response.json(decision.problemDetails, {
      status: decision.status,
      headers: decision.headers
    });
  }

  return Response.json(
    { forecast: "Clear", paid: true },
    { headers: decision.headers }
  );
}
```

</Tab>
<Tab label="Python">

```python
import os
from fastapi import Depends, FastAPI
from pinelabs_p3p_server import (
    Amount,
    ChargeOptions,
    P3PEnvironment,
    PaymentGateway,
    PaymentMethod,
    PineLabsOnlineServerConfig,
)
from pinelabs_p3p_server.fastapi_mw import PaymentRequired

app = FastAPI()

config = PineLabsOnlineServerConfig(
    clientId=os.environ["PINELABS_CLIENT_ID"],
    clientSecret=os.environ["PINELABS_CLIENT_SECRET"],
    paymentGateway=PaymentGateway.PineLabsOnline,
    availablePaymentMethods=[PaymentMethod.UPI_RESERVE_PAY],
    env=P3PEnvironment.SANDBOX,
)

require_payment = PaymentRequired(
    config,
    ChargeOptions(
        amount=Amount(value=10000, currency="INR"),
        resource="/api/weather",
    ),
)

@app.get("/api/weather", dependencies=[Depends(require_payment)])
async def weather():
    return {"forecast": "Clear", "paid": True}
```

</Tab>
</Tabs>

**decidePayment()** wraps any route with the full challenge → verify → capture cycle. Define the price and resource path — the SDK handles the rest.

1. **config** — Merchant credentials + environment
2. **chargeOptions** — Price and resource path
3. **credentialHeader** — Reads the P3P-Credential header (undefined on first call)
4. **decision.action !== "proceed"** — Returns 402 challenge if credential missing/invalid
5. **action === "proceed"**  — Payment captured, return resource + receipt headers



<h2 id="call-paid-service">4. Call The Paid Resource</h2>

<Tabs>
<Tab label="TypeScript">

```ts
import {
  P3PCustomerAuthMode,
  P3PEnvironment,
  PaymentMethod,
  PineLabsOnlineClient
} from "@pine-labs-online/p3p-client-sdk";

const client = PineLabsOnlineClient.create({
  selectedPaymentMethod: PaymentMethod.UPI_RESERVE_PAY,
  clientId: process.env.PINELABS_CLIENT_ID!,
  clientSecret: process.env.PINELABS_CLIENT_SECRET!,
  env: P3PEnvironment.SANDBOX
});

const response = await client.get(
  "https://server.example.com/api/weather",
  {},
  {
    customerReference: "customer-ref-123",
    mobileNumber: "9876543210"
  }
);
const receipt = response.headers.get("Payment-Receipt");
```

</Tab>
<Tab label="Python">

```python
from pinelabs_p3p_client import (
    ClientRuntimeContext,
    P3PCustomerAuthMode,
    P3PEnvironment,
    PaymentMethod,
    PineLabsOnlineClient,
    PineLabsOnlineClientConfig,
)

client = PineLabsOnlineClient.create(PineLabsOnlineClientConfig(
    env=P3PEnvironment.SANDBOX,
    selectedPaymentMethod=PaymentMethod.UPI_RESERVE_PAY,
    clientId="client-client-id",
    clientSecret="client-client-secret",
))

response = client.get(
    "https://server.example.com/api/weather",
    context=ClientRuntimeContext(
        customerReference="customer-ref-123",
        mobileNumber="9876543210",
    ),
)

receipt = response.headers.get("Payment-Receipt")
```

</Tab>
</Tabs>

If the first request receives `402`, the client SDK decodes the challenge, creates a one-time token, retries with `P3P-Credential: Payment`, and returns the final server response. Use client credentials in trusted backends or agents, and switch to customer-key mode when your integration is built around customer API tokens.

<h2 id="verify-result">5. Verify The Result</h2>

An unpaid request must return:

```http
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment <challenge>
Content-Type: application/problem+json
Cache-Control: no-store
```

A paid request must return the protected response and `Payment-Receipt`. Store the receipt with your application-level order, usage, or audit record.

<h2 id="go-live"> 6. Go Live</h2>

Before production:

1. Complete Pine Labs merchant onboarding.
2. Move from sandbox credentials to production credentials.
3. Confirm `PINELABS_CLIENT_SECRET` is stored securely because it also derives the local challenge HMAC key.
4. Confirm mandate limits, refund behavior, and receipt storage with your Pine Labs integration owner.



## What's Next

<div className="not-prose card-grid-2">
 <div className="card-grid-item">
    <h4>P3P Overview</h4>
    <p>Protocol architecture and concepts</p>
    <a href="../p3p">View Docs →</a>
  </div>
  <div className="card-grid-item">
    <h4>P3P SDKs</h4>
    <p>Full SDK reference for TypeScript and Python</p>
    <a href="../p3p/sdks">View Docs →</a>
  </div>

</div>
