Soroban smart contracts that power StackPay — on-chain payment requests, escrowed settlement, and payment proofs on Stellar.
This is the on-chain layer of StackPay, written in Rust and compiled to WebAssembly for Soroban.
- Why StackPay (and why Stellar needs it)
- Architecture
- Contract: PaymentRequest
- Core concepts
- Getting started
- Building
- Testing
- Local network
- Deployment
- Security
- Contributing
- License
Stellar is one of the best networks for moving money — but the ecosystem has no simple, standard way to request a payment and prove it settled. Sending is easy; asking someone to pay you (an invoice, a donation, a subscription, a split) still means copy-pasting addresses and hoping.
StackPay adds that missing primitive:
- A payment request is a tiny on-chain record: who's owed, what asset, how much, and a memo.
- The payer pays; the contract settles and records a payment proof.
- The requester shares one link; the payer pays from any Stellar wallet.
Use cases: freelancer invoices, creator tips, DAO bounty payouts, event tickets, donation jars — all natively on Stellar.
requester ──create_request──▶ PaymentRequest (Soroban)
│ emits RequestCreated
▼
payer ─────────pay──────────▶ contract pulls asset (token transfer)
│ emits RequestPaid (payment proof)
▼
stackpay-backend (indexes events)
│
▼
stackpay-frontend (dApp: links, status)
- PaymentRequest holds requests and performs the asset transfer via the Stellar Asset Contract (SAC) token client.
- Events are indexed off-chain by
stackpay-backendand surfaced in the dApp.
| Function | Description |
|---|---|
create_request(payee, asset, amount, memo, expires_in) |
Create a new request; returns request_id. |
pay(request_id) |
Payer transfers amount of asset to payee; marks Paid, emits RequestPaid. |
cancel(request_id) |
Requester cancels an unpaid, unexpired request. |
get_request(request_id) |
View: full request state. |
status(request_id) |
View: Open / Paid / Cancelled / Expired. |
All amounts use Stroops (7 decimals) for XLM and the asset's own decimals. asset is the Stellar Asset Contract address for the asset being requested.
- Request —
{ id, payee, asset, amount, memo, status, created_at, expires_at }. - Payment proof — once
paysucceeds, theRequestPaidevent + on-chainPaidstatus is the immutable receipt. - Expiry — requests can auto-expire; only unpaid, unexpired requests are payable.
Prerequisites:
- Rust with
wasm32-unknown-unknown. - Stellar CLI (includes Soroban support):
rustup target add wasm32-unknown-unknownClone & build:
git clone https://github.com/Stack-Rocks/stackpay-contracts.git
cd stackpay-contracts
make buildmake build # -> target/wasm32-unknown-unknown/release/stackpay_contracts.wasm
make optimize # smaller wasmUnit tests use soroban-sdk testutils (in-memory ledger):
make testExample:
#[test]
fn pay_marks_paid() {
// create request, pay with token client, assert status == Paid
}stellar network container start # local sandbox
stellar contract deploy --wasm target/.../stackpay_contracts.wasm --network local- Build + optimize wasm.
- Deploy to Testnet for staging, then Mainnet.
- Publish the contract id to
stackpay-backendvia its.env.
- Asset transfers use the official SAC token client (no custom token logic).
require_authon every state-changing call.- Intended for audit before mainnet (see
docs/security.md, WIP).
Part of the Stellar Wave Program on Drips. Look for Stellar Wave / Good first issue labels. Run make test && make lint before a PR.
MIT.