feat: moonlight-pay backend with security hardening#53
Merged
Conversation
Three new tables namespaced under pay_* for the moonlight-pay integration: - pay_kyc: KYC verification status per Stellar address - pay_custodial_accounts: custodial user accounts with password auth - pay_transactions: unified transaction history for both self and custodial flows Each with repository, proper indexes, and soft-delete support.
Migration 0006: creates pay_kyc, pay_custodial_accounts, pay_transactions tables with enums, indexes, and base audit columns.
10 endpoints namespaced under /api/v1/pay/: KYC (shared): - GET /pay/kyc/:address — get KYC status - POST /pay/kyc — submit KYC Transactions (shared): - GET /pay/transactions — list with pagination and status filter Self-custodial: - GET /pay/self/balance — UTXO balance summary (placeholder) - POST /pay/self/send — initiate send (placeholder bundle logic) Custodial: - POST /pay/custodial/login — username/password auth → JWT - POST /pay/custodial/register — create account + deposit address → JWT - GET /pay/custodial/account — get account info - POST /pay/custodial/send — send with balance check Demo: - POST /pay/demo/simulate-kyc — set KYC to VERIFIED directly Login/register are public (rate-limited). All others require JWT. Pay router mounted before bundle router to avoid middleware conflicts.
- pay_escrow entity with HELD/CLAIMED/EXPIRED status - Escrow repository with findHeldForAddress, findBySender - Escrow service: createEscrow, claimEscrowForAddress (atomic with FOR UPDATE), getEscrowSummary - Channel service: wraps SDK PrivacyChannel for UTXO balance queries
- GET /pay/escrow/:address — pending escrow count and total - POST /pay/report — error report ingestion from pay apps - POST /pay/demo/deposit — demo deposit via PP treasury - crypto.ts: shared PBKDF2 hashing with timing-safe verification
Security: - Atomic balance debit with FOR UPDATE row locking (prevents double-spend) - Timing-safe password verification via HMAC comparison - JWT type claim (custodial vs sep10) with scope enforcement - KYC ownership check (address must match session.sub) - Amount validation (positive integer string via regex) - Recipient address validation via StrKey.isValidEd25519PublicKey - Valid Stellar deposit addresses via Keypair.random() - Suspended account check on login and send - publicKeys array capped at MAX_UTXO_SLOTS (300) - Demo endpoints gated behind NETWORK=local|standalone or PAY_DEMO_ENABLED - Generic registration failure message (no username enumeration) - KYC read moved outside DB transaction (correct connection usage) Reliability: - try/catch on all unprotected handlers - Status param enum validation on transactions list - findByDepositAddress added to custodial account repository - Rate-limit type cast fix - loadEnv allowEmptyValues removal
Rename 0006 migration and add the pay_escrow table with HELD/CLAIMED/ EXPIRED status enum and indexes.
136 tests, all passing, zero infrastructure required: - PGlite (in-memory PostgreSQL via WASM) for real SQL/transactions - Mock JWT and channel service to avoid env var dependencies - Escrow service: create, claim, concurrent double-claim - Custodial send: double-spend, escrow, suspended, JWT type - Self send/balance: validation, limits - Login/register: auth, credentials, suspended - KYC get/post: status lookup, ownership check - Transactions list: pagination, filter - Escrow summary, report, demo simulate-kyc - Crypto: password hashing, timing-safe verification - Validation: amount regex, BigInt, StrKey, demo guard, status enum
The migration was renamed from 0006_clever_mathemanic.sql to 0006_pay_tables.sql but the new file was not staged.
68af65e to
76f35d5
Compare
Replace local filesystem reference with published JSR version (^0.7.0) so Docker builds and CI work without the SDK repo mounted alongside.
- Version 0.5.3 → 0.5.4 - stellar-sdk bumped to 14.6.1 - Added SDK transitive deps (@noble/curves, @noble/hashes, asn1js, tslib) - Added @electric-sql/pglite for test infrastructure
2a7a9d1 to
19c0492
Compare
AquiGorka
added a commit
that referenced
this pull request
Mar 24, 2026
- Fix test deno.json local path to use JSR registry - Add JWT type check on self-custodial endpoints (send, balance) - Move suspended account check after password verification in login - Add address ownership checks on escrow summary and KYC GET endpoints - Add amount regex validation on demo deposit endpoint - Replace ctx.request.url.origin with localhost for internal self-fetch - Truncate debug fields in error report logging - Pass status filter to countByAccountId for correct pagination metadata - Document deposit address design decision and fund-loss risk
AquiGorka
added a commit
that referenced
this pull request
Mar 24, 2026
- Fix test deno.json local path to use JSR registry - Add JWT type check on self-custodial endpoints (send, balance) - Move suspended account check after password verification in login - Add address ownership checks on escrow summary and KYC GET endpoints - Add amount regex validation on demo deposit endpoint - Replace ctx.request.url.origin with localhost for internal self-fetch - Truncate debug fields in error report logging - Pass status filter to countByAccountId for correct pagination metadata - Document deposit address design decision and fund-loss risk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/api/v1/pay/namespace: 13 endpoints for custodial accounts, KYC, escrow, transactions, self-custodial balance/send, demo depositpay_kyc,pay_custodial_accounts,pay_transactions,pay_escrowTest plan
deno test --allow-all --config src/http/v1/pay/tests/deno.json src/http/v1/pay/— 136 tests passlocal-dev/lifecycle) — passed in 207slocal-dev/e2e) — passed in 44s