Metadata-Version: 2.4
Name: pinelabs-online-mpp-server-sdk
Version: 0.1.1
Summary: Pine Labs Online MPP Seller SDK — x402 Machine Payments Protocol server middleware (Python)
Author: Pine Labs Online
License: MIT
Keywords: mpp,x402,payment,seller,Pine Labs Online,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"

# pinelabs-online-mpp-server-sdk (Python)

Python port of [`@pinelabs-online/mpp-server-sdk`](../mpp-server-sdk). x402
Machine Payments Protocol server-side SDK for monetising API endpoints.

Issues HTTP 402 challenges, verifies UPI SBMD credentials, captures
payments, and returns receipts — works standalone or as **Flask** /
**FastAPI** middleware.

## Installation

```bash
pip install pinelabs-online-mpp-server-sdk[flask]     # with Flask support
pip install pinelabs-online-mpp-server-sdk[fastapi]   # with FastAPI support
# or from source
cd mpp-server-sdk-python
pip install -e '.[flask,fastapi]'
```

Requires Python ≥ 3.9. Core deps: `httpx`, `PyJWT[crypto]`.

## Quick Start

### Flask

```python
from flask import Flask, jsonify
from pinelabs-online_mpp_server import Amount, ChargeOptions, MppEnvironment, pinelabs-onlineserverConfig
from pinelabs-online_mpp_server.flask_mw import payment_required

app = Flask(__name__)
config = pinelabs-onlineserverConfig(
    clientId="…", clientSecret="…", challengeSecretKey="…",
    baseUrl=MppEnvironment.SANDBOX,
)

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

### FastAPI

```python
from fastapi import FastAPI, Depends
from pinelabs-online_mpp_server import Amount, ChargeOptions, MppEnvironment, pinelabs-onlineserverConfig
from pinelabs-online_mpp_server.fastapi_mw import PaymentRequired

app = FastAPI()
config = pinelabs-onlineserverConfig(
    clientId="…", clientSecret="…", challengeSecretKey="…",
    baseUrl=MppEnvironment.SANDBOX,
)

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

@app.get("/api/premium", dependencies=[Depends(require_payment)])
async def premium():
    return {"data": "premium content"}
```

### Generic (any framework)

```python
from pinelabs-online_mpp_server import Amount, ChargeOptions, MppEnvironment, pinelabs-onlineserverConfig
from pinelabs-online_mpp_server.server.middleware import decide_payment

decision = decide_payment(
    authorization_header=request.headers.get("Authorization"),
    grantex_token_header=request.headers.get("X-Grantex-Token"),
    config=config,
    charge_options=ChargeOptions(
        amount=Amount(value=50000, currency="INR"),
        resource="/api/premium-data",
    ),
)

if decision.action != "proceed":
    # Build a 402/403 response from decision.problem_details + decision.headers
    ...
else:
    # Run business logic; attach decision.headers (Payment-Receipt) to your response
    ...
```

## Configuration

```python
pinelabs-onlineserverConfig(
    clientId="…", clientSecret="…", challengeSecretKey="…",
    realm="pinelabs-online MPP",
    baseUrl=MppEnvironment.SANDBOX,
    defaultChallengeExpirySeconds=300,
    requestTimeoutMs=30_000,
    maxRetries=3,
    initialRetryDelayMs=500,
    grantex=serverGrantexConfig(
        jwksUrl="https://grantex.dev/.well-known/jwks.json",
        requiredScopes=["mpp:payment:initiate"],
        enforceGrant=True,
    ),
)
```

## API

### `pinelabs-onlineMPP.create(config)` → `pinelabs-onlineMPPInstance`

| Method | Description |
|---|---|
| `generate_challenge(options)` | Create a signed 402 challenge |
| `verify_credential(auth_header)` | Verify a `Payment` credential |
| `capture(options)` | Capture a payment via pinelabs-online's API |
| `build_receipt_header(result, challenge_id)` | Build `Payment-Receipt` header value |
| `build_receipt_data(result, challenge_id)` | Build receipt data object |
| `verify_grant_token(token)` | Verify a Grantex grant token (`None` when not configured) |

Also exposed: `ChallengeGenerator`, `CredentialVerifier`, `CaptureClient`,
`GrantTokenVerifier`, `AuthManager`, `build_receipt_header`,
`build_receipt_data`, `build_failure_receipt_data`.

## Error handling

```python
from pinelabs-online_mpp_server import MppError, MppCaptureError, MppVerificationError

try:
    result = mpp.capture(options)
except MppCaptureError as err:
    print(err, err.capture_error and err.capture_error.http_status)
except MppError as err:
    print(err.code, err.http_status)
```

## License

MIT
