Building Your First Node

This is the authoritative hands-on walkthrough for getting the Merchant Template running on your machine. The journey map (Your First PayPort Node) explains *why* each stage exists; this page teaches *how* to do the setup steps without guessing.

You deploy an independent FastAPI Node on infrastructure you operate. Labs does not start a process for you when you claim a seat.


Where you are going

Keep this map in mind. After each section, you should know which box you are in.

Merchant Template on GitHub
        ↓
Your own repository (Use this template)
        ↓
Code on YOUR computer (or YOUR VM)
        ↓
Python virtual environment
        ↓
Dependencies installed
        ↓
Your .env configuration
        ↓
Node runs on localhost (local test only)
        ↓
/health + agent.json + 402 work
        ↓
Deploy publicly (Railway / your VM / other host)
        ↓
Host gives you a public HTTPS URL
        ↓
Register that URL with PayPort Labs
        ↓
Node appears in My Nodes

Part One keeps PAYMENT_MODE=mock. Settlement on Base Sepolia is Part Two on the journey map — not required to finish this page.


Where do I type these commands?

These instructions use a terminal (also called a command line, shell, or Console).

A terminal is a text window where you type commands and the computer runs them.

Which computer is executing the command?

Always answer that before you type.

PlaceWhat it means
Your computerTerminal app on Windows / macOS / Linux. Beginner path for Part One local testing.
Your VPS/VMYou first connect with SSH; *then* commands run on that remote server. Optional later — not required.
Railway (or similar host)Often configured in a web UI that builds from your GitHub repo. You may not type uvicorn there the same way.

Do not mix them up.

  • Local setup commands below assume: you are in a terminal on the machine where you want the code to live for local testing (usually your laptop).
  • Railway is a hosting platform, not part of the PayPort Protocol. It is one way to get a public HTTPS URL.
  • You do not need to buy a VM. You do need a public deployment eventually.

Opening a terminal (quick pointers)

  • macOS: Terminal (Spotlight → “Terminal”), or the terminal panel in VS Code / Cursor.
  • Windows: PowerShell, Windows Terminal, or the terminal in VS Code / Cursor.

If a command below starts with source, you are on a Unix-style shell (macOS, Linux, or Git Bash / WSL on Windows). On plain PowerShell the activate line differs — see the note under “Activate the environment.”

  • Linux / VPS: any shell after you log in (or after SSH).

Who hosts this Node?

You do.

You build from the Merchant Template
        ↓
You deploy to Railway / your VM / another public host
        ↓
Host gives you a public HTTPS base URL
        ↓
You register that URL with Labs

Labs Reference Demo — not your deployment: Labs Echo :8142, HVAC :8141, monorepo district folders. Do not register those URLs.


Golden Rule

Do not edit app/main.py or app/core/security.py. Those enforce HTTP 402 and the agent manifest.

You customize app/service.py and your private .env.


Step 0 — Create your own GitHub repository

What you're doing

Making a personal copy of the Merchant Template that you own. Later, Railway (or another host) should deploy your repository — not treat the official PayPort template as your app forever.

In the browser (not the terminal yet)

  1. Open payport-merchant-template
  2. Click Use this template → Create a new repository
  3. Name it something yours (e.g. my-practice-echo)
  4. Create the repository under your GitHub account
PayPort Merchant Template (official)
        ↓
Use this template
        ↓
Your own GitHub repository
        ↓
git clone YOUR_REPO   ← next step

How to know it worked

GitHub shows a new empty-looking project that already contains the Merchant Template files, under your username — not forever working inside pgwilde8/payport-merchant-template.


Step 1 — Download your code (git clone)

What you're doing

Copying your repository from GitHub onto the computer where your terminal is running.

Command (replace with your real GitHub URL):

git clone https://github.com/YOUR_GITHUB/YOUR_REPO.git

What just happened

git clone downloads a full copy of that repository into a new folder named after the repo.

How to know it worked

A new folder exists (e.g. my-practice-echo). You can list it:

ls

You should see your project directory name in the list.


Step 2 — Enter the project folder (cd)

What you're doing

Moving the terminal’s “current folder” into the project so later commands operate on the right files.

Command

cd YOUR_REPO

Example: cd my-practice-echo

What just happened

cd means change directory. Your prompt is now “inside” the project.

How to know it worked

pwd

should end with your project folder name. You should also see files such as requirements.txt, .env.example, and an app/ directory:

ls

Step 3 — Create a Python virtual environment

What you're doing

Creating a private Python toolkit for this Node only, so its packages do not collide with other Python software on the machine.

Command

python3 -m venv venv

What just happened

Python created a folder named venv/ inside your project. That folder holds an isolated interpreter and package set.

How to know it worked

ls

should now include a venv directory.


Step 4 — Activate the virtual environment

What you're doing

Turning that private environment on for this terminal session.

Command (macOS / Linux / WSL / Git Bash)

source venv/bin/activate

Windows PowerShell (if you are not using Git Bash / WSL)

.\venv\Scripts\Activate.ps1

What just happened

source (or the PowerShell activate script) switches this terminal so python and pip refer to the project’s environment.

How to know it worked

Your prompt often shows (venv) at the start, for example:

(venv) ... $

If you open a new terminal window later, activate again — activation is per session.


Step 5 — Install dependencies

What you're doing

Installing the Python packages this Node needs (FastAPI, uvicorn, and the rest listed by the template).

Command

pip install -r requirements.txt

What just happened

requirements.txt is a shopping list of packages. pip install -r installs everything on that list into your active virtual environment.

How to know it worked

The command finishes without a fatal error. You can spot-check:

python -c "import fastapi; print('ok')"

should print ok.


Step 6 — Create your private .env file

What you're doing

Creating your Node’s private configuration file from the safe example the template ships.

Command

cp .env.example .env

On Windows PowerShell (if cp is unavailable):

Copy-Item .env.example .env

What just happened

.env.example is a public example. .env is your working config. The app reads .env for wallets, mode, ports, and similar settings.

What is .env?

Applications often load settings from environment variables (named values outside the source code). A .env file is a convenient way to set those for local development.

Your .env is private. Do not commit it to GitHub or paste secrets into public chats.

The template’s .gitignore is meant to keep .env out of git — still double-check you never force-add it.

What to change for Part One

Open .env in any editor. Set at least:

DEVELOPER_WALLET_ADDRESS=0xYourBuilderWallet
PAYMENT_MODE=mock
ENVIRONMENT=village
MERCHANT_BASE_URL=http://127.0.0.1:8124
SettingMeaning for Part One
DEVELOPER_WALLET_ADDRESSYour Builder Wallet. Becomes agent.json → recipient_wallet. Must match Labs SIWE.
PAYMENT_MODE=mockSimulated settlement for the first exercise.
ENVIRONMENT=villageVillage teaching path.
MERCHANT_BASE_URLWhere this Node believes it lives. Use localhost while testing locally; change to your public HTTPS URL after deploy.
MERCHANT_AMOUNTPrice in atomic units (6 decimals). 50000 = 0.05 tUSDC. Do not put 0.05 here.

Builder triangle (must match later at registration):

SIWE wallet = DEVELOPER_WALLET_ADDRESS = agent.json recipient_wallet

Optional template alias only: PAYMENT_RECEIVER_WALLET → DEVELOPER_WALLET_ADDRESS. Builders should set DEVELOPER_WALLET_ADDRESS.

Do not look for MERCHANT_RECIPIENT in the Merchant Template. That name is for Labs Reference Nodes only.

How to know it worked

.env exists beside .env.example. After you start the Node (next steps), agent.json shows your wallet in recipient_wallet.


Step 7 — (Optional now) Customize app/service.py

The template ships with an example dental service so the plumbing runs out of the box.

For District Zero Practice Echo, follow the journey map and the Practice Echo prompt card (District Zero): implement a simple echo paid route. You still only edit app/service.py (and related schemas if needed) — not main.py / security.py.

You can smoke-test the template as-shipped first, then adapt the service for Echo before you register.


Step 8 — Run the Node locally

What you're doing

Starting the FastAPI app so it listens for HTTP requests on this machine only.

Command (venv still active):

uvicorn app.main:app --host 127.0.0.1 --port 8124 --reload

What this means

PieceMeaning
uvicornProgram that runs your FastAPI app.
app.main:app“Load the app object from app/main.py.”
127.0.0.1localhost — only this computer can reach it.
port 8124The door number on that machine.
--reloadRestart when you edit code (handy while learning).

Important: localhost is not your registration URL

http://127.0.0.1:8124 is for your testing.

PayPort Labs needs a public HTTPS base URL from a host you operate (Railway, your VM, etc.). You cannot register localhost as your Node.

How to know it worked

The terminal stays open and shows the server is running (often “Uvicorn running on http://127.0.0.1:8124”). Leave it running and open a second terminal for checks (activate venv again in that second window).


Step 9 — Success checks (local)

With the server running, verify each layer.

Health

curl http://127.0.0.1:8124/health

Or open that URL in a browser.

Expect: a successful JSON health response (status ok / healthy — not a connection error).

Manifest

curl http://127.0.0.1:8124/.well-known/agent.json

Expect: JSON including recipient_wallet equal to the wallet you set in .env.

Unpaid paid-route (expect HTTP 402)

Calling a paid route without payment proof should return 402 Payment Required. That means the gate is working — not that your Node is broken.

Confidence script

From the project folder with venv active:

python test_node.py
python test_node.py --mock

Expect: output ending with something like: Your merchant node is PayPort-compatible.


Step 10 — Deploy, then register (next stages)

When local checks pass:

  1. Deploy your repository to a public host you operate (beginner path: Railway).

Details: Deploy Your Node.

  1. The host gives you a public HTTPS base URL (example shape: https://my-practice-echo.up.railway.app).
  2. Set MERCHANT_BASE_URL on the host to that URL.
  3. Open that URL’s /health in a normal browser (not only on your laptop).
  4. Register that URL in District Zero / claim — not Labs :8141/:8142.

Full “why” for URL and wallets: Your First PayPort Node.

Wallet triangle again:

SIWE session = DEVELOPER_WALLET_ADDRESS = agent.json recipient_wallet


Repository layout (reference)

YOUR_REPO/
├── .env.example          # Safe public example — copy to .env
├── .env                # YOUR private config — never commit
├── Dockerfile
├── README.md
├── requirements.txt    # Package list for pip
├── test_node.py
├── app/
│   ├── main.py         # Dual-Mode + 402 gate (locked)
│   ├── config.py
│   ├── service.py      # ← primary file you edit
│   └── ...
└── scripts/
    └── register_first_node.py

After you are on the map

SystemWhat it does
Town Crawler (~300s)Health + schema-valid 402 probe on your URL
Buyer Bot (~120s)Mock X-Payment-Proof purchases while PAYMENT_MODE=mock
My Nodeslab.payportdirect.com/dashboard/nodes
Maplab.payportdirect.com/town

Dual-Mode reminder

PathAuthSMS
VillageUnpaid → 402; paid → X-Payment-ProofAlways virtual
Live clientX-PayPort-Client-Key + Idempotency-KeyTwilio only if ENVIRONMENT=production and Twilio is set

Issue live keys: POST /admin/client-keys with X-Admin-Token = your SESSION_SECRET.


Next