PropSpaceX Payment Service

Multi-provider payment processing — Stripe, Paystack, and Flutterwave behind one NestJS API, with idempotent webhook handling and automated reconciliation.

NestJSTypeScriptMongoDBStripePaystackFlutterwave
GitHub
01

System Architecture

REQUESTPOST /payments/initiatetracing
pending txnchargewebhooksettlenotifystatementsAPI GatewayCheckout flowsEDGEPayment APINestJS modulesSVCProvider RouterUnified interfaceSVCWebhook HandlerIdempotent · signedSVCReconciliationDaily matchingSVCStripe · Paystack+ FlutterwaveEXTMongoDBTransactionsDATARabbitMQpayment.settledQUEUE

The provider router abstracts Stripe, Paystack, and Flutterwave behind one API. Confirmations come back asynchronously as signed webhooks, are processed idempotently, and settled events fan out through RabbitMQ. Daily reconciliation matches internal records against provider statements.

02

Database Design

SCHEMA4 tables · 2 relationshover a table
transactionsMongoDB_idobjectIdreferencestringproviderenumprovider_refstringamountdecimal128currencystringstatusenumuser_iduuidproperty_idobjectIdwebhook_eventsMongoDB_idobjectIdtransaction_idobjectIdproviderenumevent_typestringsignature_validboolprocessedboolreceived_atdaterefundsMongoDB_idobjectIdtransaction_idobjectIdamountdecimal128statusenumcreated_atdatereconciliation_runsMongoDB_idobjectIdproviderenumdaydatematchednumbermismatchednumberstatusenum

Provider-agnostic transaction log — webhook events and refunds hang off the transaction; reconciliation runs track daily matching per provider.

03

Engineering Challenges

01

Provider abstraction

One payment API hides the differences between Stripe, Paystack, and Flutterwave request/response models.

02

Webhook reliability

Signature verification, idempotency keys, and replay protection make confirmations safe to process more than once.

03

Reconciliation

A daily job matches internal transaction records against provider statements and flags mismatches.

04

Money correctness

Decimal128 amounts and explicit currency handling avoid floating-point drift in financial records.

04

Trade-offs & Decisions

ChoseOverBecause
NestJSExpressModule boundaries and dependency injection keep per-provider logic isolated and testable.
MongoDBPostgreSQLProvider payloads vary wildly; a flexible schema absorbs them without constant migrations.
Three providersStripe onlyPaystack and Flutterwave cover African payment rails that Stripe does not reach well.