|
| 1 | +""" |
| 2 | +BRC-105 minimal server example. |
| 3 | +
|
| 4 | +A Starlette app with one paid endpoint (`/data`) that costs 100 satoshis, |
| 5 | +and one free endpoint (`/health`). |
| 6 | +
|
| 7 | +Run: |
| 8 | + pip install "bsv-brc[starlette]" uvicorn |
| 9 | + python examples/brc105_minimal_server.py |
| 10 | +
|
| 11 | +Then in another terminal: |
| 12 | + python examples/brc105_client.py |
| 13 | +
|
| 14 | +⚠️ DEMO ONLY — DO NOT DEPLOY ⚠️ |
| 15 | +This example uses a fake `verify_payment` stub that accepts every payment |
| 16 | +without actually verifying anything on-chain. In production you must wire |
| 17 | +this up to a real BSV wallet via `wallet.internalizeAction()` (or equivalent). |
| 18 | +The `get_identity_key` override here also bypasses BRC-103 authentication |
| 19 | +for local testing — production code must NOT do this. |
| 20 | +""" |
| 21 | + |
| 22 | +from __future__ import annotations |
| 23 | + |
| 24 | +from starlette.applications import Starlette |
| 25 | +from starlette.requests import Request |
| 26 | +from starlette.responses import JSONResponse |
| 27 | +from starlette.routing import Route |
| 28 | + |
| 29 | +from bsv_brc.brc105 import ( |
| 30 | + BSVPayment, |
| 31 | + NonceManager, |
| 32 | + PaymentMiddleware, |
| 33 | + PaymentResult, |
| 34 | + StaticPricing, |
| 35 | +) |
| 36 | + |
| 37 | + |
| 38 | +# --- handlers --- |
| 39 | + |
| 40 | +async def health(_: Request) -> JSONResponse: |
| 41 | + return JSONResponse({"status": "ok"}) |
| 42 | + |
| 43 | + |
| 44 | +async def data(request: Request) -> JSONResponse: |
| 45 | + # PaymentMiddleware attaches the result to request.state on success. |
| 46 | + paid = request.state.payment.satoshis_paid |
| 47 | + return JSONResponse({"data": "hello, paying customer", "satoshis_paid": paid}) |
| 48 | + |
| 49 | + |
| 50 | +# --- payment plumbing (DEMO STUBS — replace in production) --- |
| 51 | + |
| 52 | +async def fake_verify_payment(payment: BSVPayment, identity_key: str) -> PaymentResult: |
| 53 | + """ |
| 54 | + DEMO ONLY. Always accepts. In production, call your BSV wallet's |
| 55 | + internalizeAction() with the payment's transaction and return the |
| 56 | + real result from the wallet. |
| 57 | + """ |
| 58 | + return PaymentResult(satoshis_paid=100, accepted=True) |
| 59 | + |
| 60 | + |
| 61 | +def fake_get_identity_key(_: Request) -> str: |
| 62 | + """ |
| 63 | + DEMO ONLY. Bypasses BRC-103 auth for local testing. |
| 64 | + Production code must read a real identity key set by BRC-104 middleware. |
| 65 | + """ |
| 66 | + return "demo-identity-key-not-for-production" |
| 67 | + |
| 68 | + |
| 69 | +# --- app --- |
| 70 | + |
| 71 | +app = Starlette( |
| 72 | + routes=[ |
| 73 | + Route("/health", health), |
| 74 | + Route("/data", data), |
| 75 | + ], |
| 76 | +) |
| 77 | + |
| 78 | +app.add_middleware( |
| 79 | + PaymentMiddleware, |
| 80 | + nonce_manager=NonceManager(secret=b"demo-server-secret-change-me"), |
| 81 | + pricing=StaticPricing(100), # 100 satoshis per request |
| 82 | + verify_payment=fake_verify_payment, |
| 83 | + get_identity_key=fake_get_identity_key, |
| 84 | + excluded_paths={"/health"}, |
| 85 | +) |
| 86 | + |
| 87 | + |
| 88 | +if __name__ == "__main__": |
| 89 | + import uvicorn |
| 90 | + |
| 91 | + uvicorn.run(app, host="127.0.0.1", port=8000) |
0 commit comments