Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions x402/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
.eggs/
*.egg
.env
.venv
venv/
189 changes: 189 additions & 0 deletions x402/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# RTC x402 Payment Protocol

Implementation of the HTTP 402 Payment Required protocol for RustChain micropayments.

## Overview

The x402 protocol enables machine-to-machine micropayments over HTTP:

1. Client requests a resource
2. Server returns `402 Payment Required` with payment details
3. Client automatically pays via RTC
4. Client retries with payment proof
5. Server verifies payment and serves the resource

This enables AI agents, IoT devices, and automated services to transact without human intervention.

## Installation

```bash
pip install flask requests pynacl
# Optional for BIP39 support:
pip install mnemonic
```

## Quick Start

### Server Side

```python
from flask import Flask
from rtc_payment_middleware import require_rtc_payment

app = Flask(__name__)

@app.route('/api/data')
@require_rtc_payment(amount=0.001, recipient='your_wallet_id')
def get_data():
return {'data': 'premium content'}
```

### Client Side

```python
from rtc_payment_client import RTCClient

client = RTCClient(
wallet_seed='your-24-word-seed-phrase',
max_payment=1.0 # Safety limit
)

# Automatic 402 handling
response = client.get('https://api.example.com/api/data')
# Client detects 402 → signs RTC payment → retries → returns 200
```

## Protocol Specification

### 402 Response Headers

| Header | Description | Example |
|--------|-------------|---------|
| `X-Payment-Amount` | Payment amount required | `0.001` |
| `X-Payment-Currency` | Currency code | `RTC` |
| `X-Payment-Address` | Recipient wallet | `gurgguda` |
| `X-Payment-Network` | Network identifier | `rustchain` |
| `X-Payment-Nonce` | Unique payment nonce | `a1b2c3d4...` |
| `X-Payment-Endpoint` | Payment submission URL | `https://50.28.86.131/wallet/transfer/signed` |

### Payment Proof Headers

| Header | Description |
|--------|-------------|
| `X-Payment-TX` | Transaction hash |
| `X-Payment-Signature` | Ed25519 signature of `nonce:tx_hash` |
| `X-Payment-Sender` | Sender's public key (hex) |
| `X-Payment-Nonce` | Original nonce from 402 response |

## Components

### `rtc_payment_middleware.py`

Flask middleware for payment-gated endpoints.

```python
from rtc_payment_middleware import require_rtc_payment

@app.route('/premium')
@require_rtc_payment(
amount=0.001, # RTC amount
recipient='wallet', # Recipient wallet ID
rate_limit=100 # Max requests per minute
)
def premium_endpoint():
# g.rtc_sender contains payer's address
return {'data': 'paid content'}
```

### `rtc_payment_client.py`

HTTP client with automatic payment handling.

```python
from rtc_payment_client import RTCClient

client = RTCClient(
wallet_seed='...', # BIP39 seed phrase
max_payment=1.0, # Max auto-pay amount
auto_pay=True # Enable automatic 402 handling
)

# All HTTP methods supported
response = client.get(url)
response = client.post(url, json=data)

# Check spending
print(client.total_spent) # Total RTC spent
print(client.payment_history) # List of receipts
```

## Security Considerations

1. **Max Payment Limit**: Always set `max_payment` to prevent runaway spending
2. **SSL Verification**: Enable in production with trusted certificates
3. **Nonce Replay**: Server caches nonces to prevent replay attacks
4. **Rate Limiting**: Built-in rate limiting per sender

## Examples

### Run the Demo Server

```bash
export RTC_PAYMENT_ADDRESS=gurgguda
python example_app.py
```

### Test with curl

```bash
# Get 402 response
curl http://localhost:5000/api/data

# Response:
# HTTP/1.1 402 Payment Required
# X-Payment-Amount: 0.001
# X-Payment-Currency: RTC
# X-Payment-Address: gurgguda
# ...
```

### Run the Demo Client

```bash
python example_client.py
```

## API Reference

### `require_rtc_payment(amount, recipient, rate_limit)`

Decorator to require RTC payment for Flask endpoints.

**Parameters:**
- `amount` (float): Payment amount in RTC
- `recipient` (str): Wallet address to receive payment
- `rate_limit` (int): Max requests per minute per sender (default: 100)

### `RTCClient(wallet_seed, max_payment, auto_pay)`

HTTP client with automatic payment handling.

**Parameters:**
- `wallet_seed` (str): BIP39 24-word seed phrase
- `max_payment` (float): Maximum auto-pay amount (default: 1.0)
- `auto_pay` (bool): Enable automatic 402 handling (default: True)

**Properties:**
- `wallet_address`: Client's wallet address
- `total_spent`: Total RTC spent
- `payment_history`: List of PaymentReceipt objects

## License

MIT License - Part of the RustChain ecosystem.

## Links

- [RustChain Repository](https://github.com/Scottcjn/Rustchain)
- [x402 Protocol Spec](https://github.com/x402/spec)
- [HTTP 402 RFC](https://tools.ietf.org/html/rfc7231#section-6.5.2)
97 changes: 97 additions & 0 deletions x402/example_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Example Flask Application with RTC Payment-Gated Endpoints

This demonstrates the x402 Payment Required protocol with RustChain.

Run:
RTC_PAYMENT_ADDRESS=gurgguda python example_app.py

Test:
# First request returns 402
curl http://localhost:5000/api/data

# With payment proof (after paying)
curl http://localhost:5000/api/data \
-H "X-Payment-TX: <tx_hash>" \
-H "X-Payment-Signature: <signature>" \
-H "X-Payment-Sender: <wallet_address>" \
-H "X-Payment-Nonce: <nonce>"
"""

import os
from flask import Flask, jsonify, g
from rtc_payment_middleware import require_rtc_payment

app = Flask(__name__)

# Configuration
PAYMENT_ADDRESS = os.environ.get('RTC_PAYMENT_ADDRESS', 'gurgguda')


@app.route('/')
def index():
"""Public endpoint - no payment required."""
return jsonify({
'message': 'Welcome to the RTC-gated API',
'endpoints': {
'/': 'This help message (free)',
'/api/data': 'Premium data endpoint (0.001 RTC)',
'/api/analysis': 'Analysis endpoint (0.005 RTC)',
'/api/bulk': 'Bulk data endpoint (0.01 RTC)'
}
})


@app.route('/api/data')
@require_rtc_payment(amount=0.001, recipient=PAYMENT_ADDRESS)
def get_data():
"""Premium data endpoint - requires 0.001 RTC payment."""
return jsonify({
'status': 'success',
'data': {
'message': 'This is premium data',
'timestamp': __import__('time').time(),
'paid_by': getattr(g, 'rtc_sender', 'unknown'),
'amount_paid': getattr(g, 'rtc_payment_amount', 0)
}
})


@app.route('/api/analysis')
@require_rtc_payment(amount=0.005, recipient=PAYMENT_ADDRESS)
def get_analysis():
"""Analysis endpoint - requires 0.005 RTC payment."""
return jsonify({
'status': 'success',
'analysis': {
'trend': 'positive',
'confidence': 0.87,
'recommendation': 'hold',
'generated_for': getattr(g, 'rtc_sender', 'unknown')
}
})


@app.route('/api/bulk')
@require_rtc_payment(amount=0.01, recipient=PAYMENT_ADDRESS)
def get_bulk_data():
"""Bulk data endpoint - requires 0.01 RTC payment."""
return jsonify({
'status': 'success',
'bulk_data': [
{'id': i, 'value': f'item_{i}'} for i in range(100)
],
'count': 100,
'paid_by': getattr(g, 'rtc_sender', 'unknown')
})


if __name__ == '__main__':
print(f"Starting RTC payment-gated API server...")
print(f"Payment address: {PAYMENT_ADDRESS}")
print(f"Endpoints:")
print(f" GET / - Free (info)")
print(f" GET /api/data - 0.001 RTC")
print(f" GET /api/analysis - 0.005 RTC")
print(f" GET /api/bulk - 0.01 RTC")
app.run(debug=True, port=5000)
60 changes: 60 additions & 0 deletions x402/example_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""
Example Client Using RTC Auto-Pay

This demonstrates automatic 402 handling with the RTCClient.

Usage:
python example_client.py
"""

from rtc_payment_client import RTCClient

# Initialize client with wallet
# In production, load seed from secure storage
DEMO_SEED = "abandon " * 24 # DO NOT use this - just for demo

def main():
print("RTC Payment Client Demo")
print("=" * 50)

# Create client
client = RTCClient(
wallet_seed=DEMO_SEED,
max_payment=0.1, # Safety limit
auto_pay=True
)

print(f"Wallet address: {client.wallet_address}")
print()

# Make request to payment-gated endpoint
print("Requesting /api/data (costs 0.001 RTC)...")

try:
response = client.get("http://localhost:5000/api/data")

if response.status_code == 200:
print(f"Success! Response: {response.json()}")
print(f"Total spent: {client.total_spent} RTC")
elif response.status_code == 402:
print("Payment required but auto_pay failed")
print(f"Response: {response.json()}")
else:
print(f"Unexpected status: {response.status_code}")
print(f"Response: {response.text}")

except Exception as e:
print(f"Error: {e}")

# Show payment history
print()
print("Payment History:")
for receipt in client.payment_history:
print(f" TX: {receipt.tx_hash[:16]}...")
print(f" Amount: {receipt.amount} RTC")
print(f" To: {receipt.recipient}")
print()


if __name__ == '__main__':
main()
5 changes: 5 additions & 0 deletions x402/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
flask>=2.0.0
requests>=2.25.0
pynacl>=1.5.0
mnemonic>=0.20
cryptography>=3.4.0
Loading