Skip to content

feat: add idempotent Stellar payment verification#24

Merged
Jayrodri088 merged 1 commit intoStellarState:mainfrom
Josue19-08:feat/issue-7-payment-verification
Mar 27, 2026
Merged

feat: add idempotent Stellar payment verification#24
Jayrodri088 merged 1 commit intoStellarState:mainfrom
Josue19-08:feat/issue-7-payment-verification

Conversation

@Josue19-08
Copy link
Copy Markdown
Contributor

Description

Adds Horizon-backed Stellar payment verification and idempotent database reconciliation for investments.

Closes #7

Type of Change

  • ✨ New feature
  • ✅ Test addition or update
  • 📝 Documentation update
  • 🔧 Configuration change

Checklist

  • All GitHub Actions workflows are green on this PR (required for merge)
  • Commit messages follow Conventional Commits (feat:, fix:, chore:, etc.) — enforced by CI
  • No secrets, API keys, .env, or credentials committed (see CONTRIBUTING.md)
  • My code follows the code style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Testing

How to Test

  1. Run npm ci.
  2. Set STELLAR_HORIZON_URL, STELLAR_USDC_ASSET_CODE, STELLAR_USDC_ASSET_ISSUER, STELLAR_ESCROW_PUBLIC_KEY, STELLAR_VERIFY_ALLOWED_AMOUNT_DELTA, STELLAR_VERIFY_RETRY_ATTEMPTS, STELLAR_VERIFY_RETRY_BASE_DELAY_MS, and DATABASE_URL in .env.
  3. Run npm run lint, npm run type-check, npm test, and npm run build.
  4. Run the migrations and call VerifyPaymentService.verifyPayment({ investmentId, stellarTxHash, operationIndex? }) from the investment flow once the platform has a pending investment.

Test Coverage

  • Unit tests added/updated
  • Integration tests added/updated
  • E2E tests added/updated (if applicable)
  • Manual testing completed

Additional Notes

Changes

  • Added src/services/stellar/verify-payment.service.ts with Horizon transaction fetch + operations fetch, payment matching, stable service errors, and retry/backoff for transient Horizon failures.
  • Added transactional reconciliation that confirms the investment, writes the Stellar hash/operation index, and updates or creates the linked transactions row idempotently.
  • Added schema support for linking transactions to investments and storing stellar_operation_index on both entities.
  • Added mock-based Jest coverage for happy path, retries, missing Horizon transaction, and invalid payment criteria.

Verification criteria

The verifier treats a payment as valid only when the Horizon transaction is marked successful and exactly one payment operation matches the configured USDC asset code + issuer, the configured escrow public key, and the investment amount within the allowed delta. If multiple operations match, the caller must pass operationIndex to disambiguate. Replaying the same hash for the same investment returns an idempotent already_verified result instead of applying state changes twice.

Environment variables

  • STELLAR_HORIZON_URL
  • STELLAR_USDC_ASSET_CODE
  • STELLAR_USDC_ASSET_ISSUER
  • STELLAR_ESCROW_PUBLIC_KEY
  • STELLAR_VERIFY_ALLOWED_AMOUNT_DELTA
  • STELLAR_VERIFY_RETRY_ATTEMPTS
  • STELLAR_VERIFY_RETRY_BASE_DELAY_MS
  • DATABASE_URL

Reviewer checklist

  1. Point the service to testnet Horizon with testnet USDC issuer/code and a testnet escrow account.
  2. Create a pending investment record and note its investment_id.
  3. Send a testnet payment for the same amount to the escrow account and capture the resulting Stellar transaction hash.
  4. Execute verifyPayment({ investmentId: "<investment_id>", stellarTxHash: "<sample_testnet_hash>", operationIndex: 0 }) or omit operationIndex when the transaction has a single matching payment op.
  5. Confirm the investment moves from pending to confirmed and the linked transaction row is completed with the same Stellar hash.
  6. Run the same verification call again and confirm the service returns already_verified without creating duplicate transaction rows.

For Reviewers

  • Code quality and readability
  • Test coverage
  • Security implications
  • Performance impact
  • Breaking changes

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 26, 2026

@Josue19-08 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Jayrodri088 Jayrodri088 merged commit a3f8652 into StellarState:main Mar 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stellar Horizon payment verification and idempotent DB reconciliation

2 participants