This plan breaks the backend's build-out into 6 phases. Each phase is scoped so its checklist items can become individual GitHub issues (see CONTRIBUTING.md). Phases are ordered so the system is demoable as early as possible: first a bare API, then real user/wallet state, then a live WhatsApp loop, then real money movement, then swaps, then contracts.
Goal: Stand up a working, testable NestJS service with no business logic yet.
Scope:
- NestJS project scaffold (strict TypeScript), modular
src/modules/*layout - Typed, validated configuration (
@nestjs/config+ Joi) - Database wiring (
@nestjs/typeorm+ Postgres) with a migrations pipeline common/cross-cutting concerns (global exception filter, logging interceptor)/healthendpoint- CI (build + test on every push/PR to
main)
Checklist:
- Scaffold NestJS app with strict TypeScript and ESLint
- Add
ConfigModulewith env validation for DB/Stellar/WhatsApp vars - Add
DatabaseModule(TypeORM + Postgres) and a migration CLI script - Add
HealthModulewith aGET /healthendpoint - Add global exception filter + logging interceptor
- Add GitHub Actions CI running
npm run buildandnpm run test
Dependencies: None.
Definition of done: npm run build and npm run test pass locally and in
CI; GET /health returns 200 { status: "ok" } on a running instance.
Goal: Let a WhatsApp phone number be linked to a custodial Stellar wallet.
Scope:
usersmodule: create/find a user by WhatsApp phone numberwalletmodule: generate a Stellar keypair, encrypt the secret at rest, persist it- Encryption/decryption helper built around
WALLET_ENCRYPTION_KEY - Balance lookup endpoint (reads from Horizon via the
stellarmodule)
Checklist:
- Write the
usersandwalletsTypeORM migrations - Implement
UsersService.create/findByPhoneNumberagainst Postgres - Implement secret encryption/decryption (AES-256-GCM with
WALLET_ENCRYPTION_KEY) - Implement
WalletService.createto generate, encrypt, and persist a keypair, funded via Friendbot on testnet - Implement
WalletService.getBalanceusingStellarService.getAccount - Add integration tests covering create-user → create-wallet → get-balance
- Add rate limiting / duplicate-wallet guard per user
Dependencies: Phase 1 (DatabaseModule, StellarService scaffold).
Definition of done: POST /users then POST /wallets then
GET /wallets/:publicKey/balance works end-to-end against a local Postgres
and Stellar testnet, with the raw secret never persisted or logged in
plaintext.
Goal: Turn inbound WhatsApp messages into recognized commands and reply.
Scope:
- Webhook verification (already stubbed) hardened with signature checks
- Inbound message parsing into intents:
balance,send,receive,swap,deploy,help - Outbound message sending via the WhatsApp Cloud API (
WhatsappService.sendMessage) - Minimal conversation/session state (what a user is mid-way through, e.g. confirming a send)
Checklist:
- Implement
WhatsappService.sendMessageagainst the Cloud API - Add HMAC signature verification on inbound webhook POSTs
- Build an intent parser (regex/keyword-based to start) for
balance/send/receive/swap/deploy/help - Add a
conversationsmodule for simple per-user session state (Postgres or in-memory TTL cache) - Wire
balanceintent end-to-end: WhatsApp message in, wallet balance reply out - Add a fallback/help reply for unrecognized messages
- Add tests for the intent parser and the webhook signature check
Dependencies: Phase 2 (needs users/wallets to answer balance).
Definition of done: Sending "balance" from a linked WhatsApp number to the configured test number returns the caller's real testnet balance within a few seconds.
Goal: Move real value between Stellar accounts, triggered from WhatsApp.
Scope:
StellarService.sendPayment— build, sign (with the decrypted custodial secret), and submit a real payment operation- Trustline creation for non-native assets
- Transaction status polling and a WhatsApp confirmation reply
- Transaction history endpoint
Checklist:
- Implement
StellarService.sendPayment(payment op, fee, sequence handling, submit) - Implement trustline creation (
changeTrust) for supported non-XLM assets - Add
sendintent handling: parse amount/asset/recipient from a WhatsApp message - Add recipient resolution (phone number → linked wallet, or raw Stellar address)
- Add a confirmation step before submitting a payment (avoid fat-fingered sends)
- Add
GET /wallets/:publicKey/transactionsbacked by Horizon - Add tests for payment-building logic using the Horizon testnet or a mocked server
Dependencies: Phase 2 (wallets), Phase 3 (messaging loop and intent parser).
Definition of done: A WhatsApp user can send "send 5 XLM to +1555..." and have the recipient's linked wallet receive funds on testnet, with both parties notified.
Goal: Let a user swap one asset for another from WhatsApp.
Scope:
StellarService.swap— path payment strict send/receive- Quote lookup (order book / DEX path finding) before executing
swapintent parsing and confirmation flow
Checklist:
- Implement path-finding/quote lookup (
GET /paths/strict-sendequivalent via the SDK) - Implement
StellarService.swap(path payment strict send, slippage tolerance) - Add
swapintent parsing ("swap 10 XLM for USDC") - Add a quote-then-confirm step before submitting a swap
- Handle missing-trustline and no-path error cases with a clear WhatsApp reply
- Add tests for quote parsing and swap transaction building
Dependencies: Phase 4 (payment submission plumbing, confirmation UX).
Definition of done: A WhatsApp user can swap between two supported testnet assets and see the resulting balances update on both sides of the trade.
Goal: Let a user deploy and call Soroban contracts from WhatsApp.
Scope:
ContractsService.deploy— upload/create a contract instance from a known wasm hashContractsService.invoke— call a contract method with user-supplied argsdeploy/invokeintents, mapping simple WhatsApp phrasing to specific contracts
Checklist:
- Implement
ContractsService.deploy(CreateContract host function, sign, submit, simulate first) - Implement
ContractsService.invoke(InvokeHostFunction, arg encoding via@stellar/stellar-sdknativeToScVal) - Add a registry of known contract wasm hashes/IDs (starting with contracts published by the
MiHashport-Contractrepo) - Add
deploy/invokeintent parsing for a small fixed set of supported contracts - Add simulation-before-submit to surface errors before spending fees
- Add tests for arg encoding and the deploy/invoke request building
Dependencies: Phase 4 (signing/submission plumbing); requires at least one contract from the sibling MiHashport-Contract repo compiled and its wasm hash published/deployed on testnet.
Definition of done: A WhatsApp user can say "deploy escrow" and receive back a live testnet contract ID, then invoke one of its methods and see the result reflected on-chain.