Skip to main content
All new Payoes payments use the Soroban escrow contract. Checkout returns setup instructions if the contract or operator account is not configured correctly. Use the deploy script in scripts/soroban-deploy-contract.sh. It builds the contract, deploys it, initializes it, and prints the values you need for .env.local.

Prerequisites

  • Stellar CLI (stellar)
  • Rust toolchain (cargo)
  • A funded Stellar account on the target network

Quick start (Testnet)

From the repository root:

1. Create Stellar CLI identities

You need two logical roles:
RoleFlagPurpose
Operator--operator-accountContract admin and authorization_signer
Funder--source-accountPays deploy transaction fees
For local development, one identity is enough. Use the same name for both flags.
stellar keys generate --global payoes-operator
If you want a separate funder account:
stellar keys generate --global payoes-funder

2. Fund the accounts

Fund whichever account pays fees on Testnet:
# Single-account setup
curl "https://friendbot.stellar.org?addr=$(stellar keys address payoes-operator)"

# Separate funder setup
curl "https://friendbot.stellar.org?addr=$(stellar keys address payoes-funder)"
If operator and funder are different, fund both. The operator also needs a small XLM balance to pay the initialize transaction fee.

3. Deploy and initialize

Single account (simplest):
./scripts/soroban-deploy-contract.sh \
  --operator-account payoes-operator \
  --source-account payoes-operator
Separate operator and funder:
./scripts/soroban-deploy-contract.sh \
  --operator-account payoes-operator \
  --source-account payoes-funder
Or via npm:
npm run soroban:deploy -- \
  --operator-account payoes-operator \
  --source-account payoes-operator
The script will:
  1. Run cargo test -p payoes
  2. Build the contract WASM
  3. Deploy to the selected network (default: testnet)
  4. Call initialize(admin, authorization_signer, fee_recipient) with the operator public key
  5. Print:
CONTRACT_ID=C...
OPERATOR_SECRET=S...

4. Configure apps/web/.env.local

Copy the script output into your env file:
STELLAR_TESTNET_OPERATOR_SECRET=S...
SOROBAN_TESTNET_RPC_URL=https://soroban-testnet.stellar.org
SOROBAN_TESTNET_CONTRACT_ID=C...
Restart the dev server:
npm run dev

5. Verify the contract

stellar contract info interface \
  --network testnet \
  --contract-id YOUR_CONTRACT_ID
You should see register_payment, deposit, release_deposit_to_operator, record_settlement, and refund_held_deposit.

Script reference

./scripts/soroban-deploy-contract.sh --help
FlagRequiredDescription
--operator-accountYesStellar CLI identity for contract admin and authorization
--source-accountYesIdentity that pays deploy fees
--networkNoStellar CLI network name (default: testnet)
Supported networks are any name from:
stellar network ls
Common values: testnet, mainnet, futurenet, local.
Deploy fees are paid by --source-account. initialize is signed by --operator-account because the contract requires admin authorization.

Common checkout errors

SymptomFix
soroban_not_configuredSet SOROBAN_TESTNET_CONTRACT_ID and SOROBAN_TESTNET_RPC_URL
operator_not_configuredSet STELLAR_TESTNET_OPERATOR_SECRET from script output
contract_not_initializedRe-run the deploy script on a fresh contract ID
unauthorized_signerOperator public key must match contract authorization_signer. Redeploy with the correct operator identity
payment_already_finalizedPayment already settled, refunded, or simulated on-chain. Start a new checkout session
payment_not_registeredCheck operator funding, contract pause state, and server logs

Redeploying after contract changes

When the Rust contract changes:
  1. Re-run the deploy script (it creates a new contract ID)
  2. Update SOROBAN_TESTNET_CONTRACT_ID in .env.local
  3. Restart npm run dev
Existing pending payments registered on the old contract cannot be completed on the new one.

Mainnet

Use the same script with --network mainnet and set the *_MAINNET_* env vars instead:
./scripts/soroban-deploy-contract.sh \
  --operator-account payoes-operator \
  --source-account payoes-funder \
  --network mainnet
Mainnet accounts must be funded outside Friendbot. Treat operator secrets as production credentials.

Reference Testnet deployment

An example Testnet contract ID is documented in Soroban payment router status. Use your own deployment for local development unless you control that contract’s authorization signer.