Metadata-Version: 2.4
Name: pinelabs-online-mpp-server-sdk
Version: 0.1.2
Summary: Plural P3P Seller SDK — x402 Plural P3P server middleware (Python)
Author: Plural
License: MIT
Keywords: mpp,x402,payment,seller,plural,upi,sbmd,machine-payments
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Framework :: Flask
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27
Provides-Extra: flask
Requires-Dist: flask>=2.3; extra == "flask"
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == "fastapi"
Requires-Dist: starlette>=0.36; extra == "fastapi"
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: flask>=2.3; extra == "dev"
Requires-Dist: fastapi>=0.110; extra == "dev"

# Pine Labs Online P3P Seller SDK (Python)

Python SDK for Pine Labs Online P3P seller integrations. It mirrors
`@pine-labs-online/p3p-seller-sdk`, creates mandates, generates HTTP 402
payment challenges, verifies buyer credentials, captures payments through P3P,
and builds `Payment-Receipt` headers.

## Installation

```bash
pip install pinelabs-p3p-seller-sdk[flask]
pip install pinelabs-p3p-seller-sdk[fastapi]
```

Import module: `pinelabs_p3p_seller`. Requires Python 3.9 or newer.

## Config

```python
from pinelabs_p3p_seller import (
    P3PEnvironment,
    PaymentGateway,
    PaymentMethod,
    PluralSellerConfig,
)

config = PluralSellerConfig(
    clientId="...",
    clientSecret="...",
    challengeSecretKey="...",
    env=P3PEnvironment.SANDBOX,
    paymentGateway=PaymentGateway.PineLabsOnline,
    availablePaymentMethods=[PaymentMethod.UpiSbmd, PaymentMethod.Crypto],
)
```

`clientId` and `clientSecret` are used internally for `POST /api/auth/v1/token`.
The SDK caches and refreshes bearer tokens before expiry. `env` selects the
Pine Labs host used for auth and `/mpp/v1/*` service calls.

## Mandates

```python
from pinelabs_p3p_seller import Amount, CreateMandateOptions, PluralP3P

p3p = PluralP3P.create(config)
mandate = p3p.create_mandate(CreateMandateOptions(
    mobileNumber="9876543210",
    customerReference="9876543210",
    amount=Amount(value=100000, currency="INR"),
    paymentMethod=PaymentMethod.UpiSbmd,
    validityInDays=20,
))
```

This maps to `POST /mpp/v1/pre-authorize` and sends
`customer.mobile_number`.

## Paid Resource Flow

```python
from flask import Flask, jsonify
from pinelabs_p3p_seller import Amount, ChargeOptions
from pinelabs_p3p_seller.flask_mw import payment_required

app = Flask(__name__)

@app.get("/api/premium")
@payment_required(config, ChargeOptions(
    amount=Amount(value=50000, currency="INR"),
    resource="/api/premium",
))
def premium():
    return jsonify({"data": "premium content"})
```

The middleware reads `P3P-Credential: Payment <payload>`, not `Authorization`,
so it does not conflict with application bearer auth.

## Capture

```python
from pinelabs_p3p_seller import CaptureOptions

result = p3p.capture(CaptureOptions(
    token="MPP_TOK_123",
    amount=Amount(value=50000, currency="INR"),
    paymentMethod=PaymentMethod.UpiSbmd,
    customerReference="9876543210",
    mobileNumber="9876543210",
    challengeId="ch_123",
    merchantOrderReference="order-123",
))
```

The debit body uses `customer.mobile_number`, `payment_amount`,
`payment_token`, and `challenge_id`. The SDK sends `Idempotency-Key` and
`Request-Hash` headers and does not send `Merchant-ID`.

## Generic Middleware Helper

```python
from pinelabs_p3p_seller.server.middleware import decide_payment

decision = decide_payment(
    credential_header=request.headers.get("P3P-Credential"),
    config=config,
    charge_options=ChargeOptions(
        amount=Amount(value=50000, currency="INR"),
        resource="/api/premium-data",
    ),
)
```

## License

MIT
