Skip to content
Open
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
13 changes: 13 additions & 0 deletions examples/peer-to-peer/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copy this file to .env and fill in your values.
# Get credentials from an Open Payments-enabled wallet (e.g. https://wallet.interledger-test.dev)

# Authenticated Open Payments client (identity used to sign all requests)
OP_WALLET_ADDRESS_URL=https://your-wallet.example/your-address
OP_KEY_ID=your-key-id

# Path to private key PEM file (absolute or relative to examples/peer-to-peer/)
OP_PRIVATE_KEY_PATH=private.key

# Wallet addresses for the peer-to-peer payment
SENDING_WALLET_ADDRESS_URL=https://sender-wallet.example/sender
RECEIVING_WALLET_ADDRESS_URL=https://receiver-wallet.example/receiver
13 changes: 13 additions & 0 deletions examples/peer-to-peer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.12-slim

RUN pip install poetry
RUN apt-get update && apt-get install -y --no-install-recommends curl
RUN rm -rf /var/lib/apt/lists/*

ENV POETRY_VIRTUALENVS_CREATE=false

COPY . /app
WORKDIR /app
RUN poetry install --with dev

EXPOSE 3999
82 changes: 82 additions & 0 deletions examples/peer-to-peer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Peer-to-Peer Payment Example

A Python replication of the [Node.js peer-to-peer example](https://github.com/interledger/open-payments-node/tree/main/examples/peer-to-peer) from the official Open Payments Node SDK, using the Open Payments Python SDK instead.

Here are the steps that the example makes:

- Creates an incoming payment on the receiving wallet address
- Requests grants and creates a quote on the sending wallet address
- Initiates an interactive outgoing payment grant that requires browser approval
- Finalizes the grant and creates the outgoing payment

---

## Setup

### 1. Install the SDK

From the repo root:

```bash
poetry install
```

### 2. Get credentials from an Open Payments-enabled wallet

You can use the [Interledger test wallet](https://wallet.interledger-test.dev) to create accounts and generate developer keys.
Instructions are at the [Open Payments documentation](https://openpayments.dev/sdk/before-you-begin/).

### 3. Configure credentials
Copy `.env.example` to `.env` and fill in your values:

```bash
cp .env.example .env
```

```env
OP_WALLET_ADDRESS_URL=https://ilp.interledger-test.dev/your-address
OP_KEY_ID=your-key-id
OP_PRIVATE_KEY_PATH=private.key

SENDING_WALLET_ADDRESS_URL=https://ilp.interledger-test.dev/your-sending-address
RECEIVING_WALLET_ADDRESS_URL=https://ilp.interledger-test.dev/your-receiving-address
```

> Wallet address URLs must start with `https://`, not `$`.
> The sender and receiver must be on the same (or a peered) ILP network for the quote to succeed.

---

## Running

Both services (callback server and main script) are orchestrated via Docker Compose.

```bash
cd examples/peer-to-peer
docker compose run --rm example
```

To run them manually:
```bash
cd examples/peer-to-peer
poetry run callback_server.py
```

and with the server running in the background:
```bash
cd examples/peer-to-peer
poetry run run.py
```

`docker compose run` starts the `callback-server` automatically (via `depends_on`), then launches the `run` service in an interactive terminal.

- The script will print a URL and attempt to open it in your default browser
- Navigate to it and **accept the outgoing payment grant** on the sending wallet
- The callback server captures the redirect automatically
- The outgoing payment is created and funds move from the sender to the receiver

To stop and remove all containers when done:

```bash
docker compose down
```
56 changes: 56 additions & 0 deletions examples/peer-to-peer/callback_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""
Callback server for the Open Payments interactive grant flow.

The auth server redirects the user's browser here after grant approval,
appending `interact_ref` and `hash` as query parameters.

Routes:
GET / – receives the redirect and serves a success page to the browser
GET /result – long-polls until the callback arrives, then returns
interact_ref and hash as JSON (consumed by run.py)
"""

import queue

import flask

app = flask.Flask(__name__)
result_queue: queue.Queue = queue.Queue(maxsize=1)

CALLBACK_HTML = """
<html>
<body style="font-family: monospace; padding: 2rem; text-align: center;">
<img src="https://raw.githubusercontent.com/interledger/open-payments/main/docs/public/img/logo.svg"
width="300" alt="Open Payments" style="max-width: 100%; margin-bottom: 2rem;">
<h1>Authentication successful</h1>
<p>You can close this window and return to your terminal.</p>
</body>
</html>
"""


@app.route("/health")
def health():
return flask.jsonify({"status": "ok"})


@app.route("/")
def callback():
result_queue.put({
"interact_ref": flask.request.args.get("interact_ref"),
"hash": flask.request.args.get("hash"),
})
return CALLBACK_HTML


@app.route("/result")
def result():
try:
data = result_queue.get(timeout=300)
return flask.jsonify(data)
except queue.Empty:
return flask.jsonify({"error": "timeout"}), 408


if __name__ == "__main__":
app.run(host="0.0.0.0", port=3999)
34 changes: 34 additions & 0 deletions examples/peer-to-peer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
callback-server:
build:
context: ../..
dockerfile: examples/peer-to-peer/Dockerfile
command: python examples/peer-to-peer/callback_server.py
ports:
- "3999:3999"
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3999/health"]
interval: 2s
timeout: 5s
retries: 10
volumes:
- ./:/app/examples/peer-to-peer/

example:
build:
context: ../..
dockerfile: examples/peer-to-peer/Dockerfile
command: python examples/peer-to-peer/run.py
stdin_open: true
tty: true
depends_on:
callback-server:
condition: service_healthy
env_file:
- .env
environment:
- OP_PRIVATE_KEY_PATH=/app/private.key
- CALLBACK_SERVER_URL=http://callback-server:3999
volumes:
- ../..:/app
- ${OP_PRIVATE_KEY_PATH}:/app/private.key:ro
Loading