Files
goodgo-platform/PAYMENT_MODULE_SECURITY_REVIEW.md
Ho Ngoc Hai 25420720e7 fix(api,ci): remove type-only imports for DI and isolate CI ports from dev
- Remove `type` keyword from NestJS injectable class imports across all
  modules to fix runtime DI resolution (330+ handler/listener files)
- Offset CI docker-compose ports (5433/6380/8109/9002) to avoid
  conflicts with running dev containers
- Update .env.test, playwright.config.ts, and e2e workflow to use
  isolated CI ports with configurable overrides
- Fix prisma/seed.ts to use deterministic IDs for Prisma 7 upsert
  compatibility (phoneHash replaced phone as unique index)
- Add dedicated Docker bridge network for CI service containers

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
2026-04-13 01:40:14 +07:00

17 KiB

GoodGo Platform Payment Module - Security Review File Inventory

Overview

Comprehensive file listing for the Order & Escrow entities security review in the payments module. Location: /Users/velikho/Desktop/WORKING/goodgo-platform-ai/apps/api/src/modules/payments/


1. DOMAIN LAYER - ENTITIES

Core Entities

File Description
domain/entities/order.entity.ts ORDER ENTITY - Manages order lifecycle with state machine (CREATED→PAYMENT_PENDING→PAYMENT_CONFIRMED→ESCROW_HELD→SHIPPED→DELIVERED→ESCROW_RELEASED→COMPLETED). Validates transitions. Emits events: OrderCreatedEvent, OrderPaidEvent, OrderCancelledEvent. Critical fields: buyerId, sellerId, listingId, amount (Money VO), platformFee, sellerPayout.
domain/entities/escrow.entity.ts ESCROW ENTITY - Manages escrow lifecycle (PENDING→HELD→RELEASED/DISPUTED/REFUNDED). Stores escrow amount, fee, and calculated netPayout. Emits: EscrowHeldEvent, EscrowReleasedEvent, EscrowDisputedEvent. Validates state transitions.
domain/entities/payment.entity.ts PAYMENT ENTITY - Manages payment transactions (PENDING→PROCESSING→COMPLETED/FAILED/REFUNDED). Stores userId, provider (VNPAY/MOMO/ZALOPAY), type, amount, callbackData, idempotencyKey. Emits: PaymentCreatedEvent, PaymentCompletedEvent, PaymentFailedEvent, PaymentRefundedEvent.

Value Objects

File Description
domain/value-objects/money.vo.ts MONEY VALUE OBJECT - Wraps amounts as bigint in VND. Validates: amount > 0, max limit 999_999_999_999. Used for all financial amounts.
domain/value-objects/platform-fee.vo.ts PLATFORM FEE VALUE OBJECT - Calculates 5% platform fee. Methods: fromOrderAmount() (auto-calc 5%), create() (explicit amount). Validates fee >= 0.

2. DOMAIN LAYER - REPOSITORIES (Interfaces)

File Description
domain/repositories/order.repository.ts ORDER REPOSITORY INTERFACE - Defines CRUD + query methods: findById, findByIdempotencyKey, findByBuyerId, findBySellerId, save, update. Idempotency protection.
domain/repositories/escrow.repository.ts ESCROW REPOSITORY INTERFACE - Defines: findById, findByOrderId, save, update. One escrow per order relationship.
domain/repositories/payment.repository.ts PAYMENT REPOSITORY INTERFACE - Defines: findById, findByProviderTxId, findByIdempotencyKey, findByUserId, save, update, updateIfStatus (atomic conditional update for race condition handling).

3. DOMAIN LAYER - EVENTS

File Description
domain/events/order-created.event.ts Emitted when order created - contains orderId, buyerId, sellerId, listingId, amount
domain/events/order-paid.event.ts Emitted when payment confirmed - contains orderId, buyerId, amount
domain/events/order-cancelled.event.ts Emitted when order cancelled - contains orderId, buyerId, sellerId
domain/events/escrow-held.event.ts Emitted when escrow held - contains escrowId, orderId, amount
domain/events/escrow-released.event.ts Emitted when escrow released - contains escrowId, orderId, netPayout
domain/events/escrow-disputed.event.ts Emitted when escrow disputed - contains escrowId, orderId, reason
domain/events/payment-created.event.ts Emitted when payment created - contains paymentId, userId, provider, amount
domain/events/payment-completed.event.ts Emitted when payment completes - contains paymentId, userId, provider
domain/events/payment-failed.event.ts Emitted when payment fails - contains paymentId, userId, provider
domain/events/payment-refunded.event.ts Emitted when payment refunded - contains paymentId, userId, provider, amount

4. INFRASTRUCTURE LAYER - REPOSITORIES (Implementations)

File Description
infrastructure/repositories/prisma-order.repository.ts ORDER REPOSITORY IMPL - Prisma ORM implementation. Stores: id, buyerId, sellerId, listingId, status, amountVND, platformFeeVND, sellerPayoutVND, idempotencyKey, metadata. Handles order persistence.
infrastructure/repositories/prisma-escrow.repository.ts ESCROW REPOSITORY IMPL - Prisma ORM implementation. Stores: id, orderId, amountVND, feeVND, status, heldAt, releasedAt, disputeReason, disputedAt. Handles escrow persistence.
infrastructure/repositories/prisma-payment.repository.ts PAYMENT REPOSITORY IMPL - Prisma ORM. Stores: id, userId, transactionId, provider, type, amountVND, status, providerTxId, callbackData, idempotencyKey. CRITICAL: updateIfStatus() uses conditional WHERE clause for atomic race condition prevention (Line 84-109).

5. INFRASTRUCTURE LAYER - PAYMENT GATEWAY SERVICES

Payment Gateway Interface

File Description
infrastructure/services/payment-gateway.interface.ts GATEWAY INTERFACE - Defines IPaymentGateway contract: createPaymentUrl(), verifyCallback(), refund(). CallbackVerifyResult includes: isValid, orderId, providerTxId, isSuccess, rawData. Sensitive for security.

VNPay Service

File Description
infrastructure/services/vnpay.service.ts VNPAY PAYMENT GATEWAY - Implements IPaymentGateway. CALLBACK VERIFICATION (Line 72-105): Extracts secure hash, removes it from data, sorts params, generates HMAC-SHA512, uses crypto.timingSafeEqual() for constant-time comparison. Amount multiplied by 100 for VND cents. Returns isValid, orderId (vnp_TxnRef), providerTxId (vnp_TransactionNo), isSuccess (responseCode === '00'). Refund support.

MoMo Service

File Description
infrastructure/services/momo.service.ts MOMO PAYMENT GATEWAY - Implements IPaymentGateway. CALLBACK VERIFICATION (Line 102-147): Extracts signature from data, rebuilds raw signature with accessKey, amount, extraData, IPN/redirect URLs, orderId, etc. Uses HMAC-SHA256, constant-time comparison via crypto.timingSafeEqual(). Success check: resultCode === '0'. Refund support. Amount as Number (not bigint in API).

ZaloPay Service

File Description
infrastructure/services/zalopay.service.ts ZALOPAY PAYMENT GATEWAY - Implements IPaymentGateway. CALLBACK VERIFICATION (Line 98-144): Data passed as JSON string in 'data' field. MAC verified via HMAC-SHA256 with key2. Parses JSON data to extract app_trans_id and zp_trans_id. SECURITY NOTE: Catches JSON parse errors gracefully. Uses constant-time comparison. Refund support (key1).

Payment Gateway Factory

File Description
infrastructure/services/payment-gateway.factory.ts GATEWAY FACTORY - Returns appropriate gateway instance (VNPay/MoMo/ZaloPay) based on provider enum.

6. APPLICATION LAYER - COMMANDS

Order Commands

File Description
application/commands/create-order/create-order.command.ts CREATE ORDER COMMAND - Input: buyerId, sellerId, listingId, amountVND, idempotencyKey. Payload object.
application/commands/create-order/create-order.handler.ts CREATE ORDER HANDLER - Idempotency check via findByIdempotencyKey. Validates amount (Money VO). Calculates platform fee (5%) and seller payout. Creates OrderEntity + EscrowEntity (PENDING status). Saves both. Emits events.
application/commands/cancel-order/cancel-order.command.ts CANCEL ORDER COMMAND - Input: orderId, userId, reason.
application/commands/cancel-order/cancel-order.handler.ts CANCEL ORDER HANDLER - Verifies user owns order, validates state transition via entity.markCancelled(), saves, emits events.

Escrow Commands

File Description
application/commands/hold-escrow/hold-escrow.command.ts HOLD ESCROW COMMAND - Input: orderId. Admin-only operation.
application/commands/hold-escrow/hold-escrow.handler.ts HOLD ESCROW HANDLER (Line 23-67) - Fetches order + escrow by orderId. Calls escrow.hold() state transition. Updates both entities. Emits EscrowHeldEvent. SECURITY NOTE: No Redis lock - potential race condition if multiple concurrent requests.
application/commands/release-escrow/release-escrow.command.ts RELEASE ESCROW COMMAND - Input: orderId. Admin-only operation.
application/commands/release-escrow/release-escrow.handler.ts RELEASE ESCROW HANDLER (Line 24-45) - Fetches order + escrow by orderId. Calls escrow.release() state transition. Updates both entities. Emits EscrowReleasedEvent with netPayout. SECURITY NOTE: No Redis lock - potential race condition.

Payment Commands

File Description
application/commands/create-payment/create-payment.command.ts CREATE PAYMENT COMMAND - Input: userId, provider, type, amountVND, description, returnUrl, ipAddress, transactionId, idempotencyKey.
application/commands/create-payment/create-payment.handler.ts CREATE PAYMENT HANDLER - Idempotency check. Validates amount (Money VO). Gets payment gateway. Calls createPaymentUrl(). Creates PaymentEntity (PENDING status). Saves. Emits PaymentCreatedEvent. Returns paymentUrl for frontend redirect.
application/commands/refund-payment/refund-payment.command.ts REFUND PAYMENT COMMAND - Input: paymentId, reason, userId. Admin command.
application/commands/refund-payment/refund-payment.handler.ts REFUND PAYMENT HANDLER - Verifies payment exists, calls gateway.refund() with provider-specific args, updates payment status to REFUNDED, emits PaymentRefundedEvent.

Callback Handler (CRITICAL)

File Description
application/commands/handle-callback/handle-callback.command.ts HANDLE CALLBACK COMMAND - Input: provider (PaymentProvider enum), callbackData (Record<string, string>).
application/commands/handle-callback/handle-callback.handler.ts HANDLE CALLBACK HANDLER (Line 32-110) - CRITICAL SECURITY FILE. Gets gateway, calls verifyCallback() (validates signature). If invalid: throws ValidationException. If valid: Uses paymentRepo.updateIfStatus() with conditional WHERE ['PENDING', 'PROCESSING'] (Line 48-55) - atomic update to prevent duplicate processing. If update returns null: checks if payment exists (already processed - idempotent response). If success: calls payment.emitCompleted(), else payment.emitFailed(). Publishes events. STRONG RACE CONDITION PROTECTION via conditional update.

7. APPLICATION LAYER - QUERIES

File Description
application/queries/get-order-status/get-order-status.query.ts Query: Input orderId, userId (for authorization).
application/queries/get-order-status/get-order-status.handler.ts Fetches order, verifies ownership (buyer/seller), returns status + details.
application/queries/get-payment-status/get-payment-status.query.ts Query: Input paymentId, userId.
application/queries/get-payment-status/get-payment-status.handler.ts Fetches payment, verifies ownership, returns status + details.
application/queries/list-transactions/list-transactions.query.ts Query: Input userId, status (optional), limit, offset.
application/queries/list-transactions/list-transactions.handler.ts Lists payments for user with pagination, filters by status if provided.

8. PRESENTATION LAYER - CONTROLLERS

File Description
presentation/controllers/orders.controller.ts ORDERS CONTROLLER - Routes: POST / (create order), GET /:id (status), POST /:id/cancel (cancel), POST /:id/escrow/hold (admin), POST /:id/escrow/release (admin). Auth: JwtAuthGuard, RolesGuard for admin ops. Converts DTO to commands.
presentation/controllers/payments.controller.ts PAYMENTS CONTROLLER - Routes: POST / (create payment), POST /callback/:provider (webhook - Throttle + EndpointRateLimit), GET /:id (status), GET (list), POST /:id/refund (admin refund). CRITICAL: Callback endpoint has rate limiting (Throttle + EndpointRateLimitGuard) - prevents callback flooding.

9. PRESENTATION LAYER - DTOs

File Description
presentation/dto/create-order.dto.ts DTO: sellerId, listingId, amountVND (string), idempotencyKey (optional).
presentation/dto/cancel-order.dto.ts DTO: reason (string).
presentation/dto/create-payment.dto.ts DTO: provider (enum), type (enum), amountVND (string), description, returnUrl, transactionId (optional), idempotencyKey (optional).
presentation/dto/refund-payment.dto.ts DTO: reason (string).
presentation/dto/list-transactions.dto.ts DTO: status (optional), limit, offset.

10. MODULE & TEST FILES

File Description
payments.module.ts MODULE SETUP - Registers repositories, services, handlers, controllers.
index.ts (module level) Exports public API.
infrastructure/repositories/index.ts Exports repository implementations.
infrastructure/services/index.ts Exports gateway services.
application/index.ts Exports command/query handlers.
domain/repositories/index.ts Exports repository interfaces.
domain/entities/index.ts Exports entities.
domain/value-objects/index.ts Exports VOs.
domain/events/index.ts Exports domain events.
presentation/controllers/index.ts Exports controllers.
presentation/dto/index.ts Exports DTOs.

Test Files

File Description
domain/__tests__/order.entity.spec.ts Order entity unit tests - state machine, transitions
domain/__tests__/escrow.entity.spec.ts Escrow entity unit tests - hold, release, dispute, refund
domain/__tests__/payment.entity.spec.ts Payment entity unit tests
domain/__tests__/money.vo.spec.ts Money VO validation tests
domain/__tests__/platform-fee.vo.spec.ts Platform fee calculation tests
domain/__tests__/payment-events.spec.ts Domain event emission tests
application/__tests__/create-order.handler.spec.ts Create order handler tests
application/__tests__/create-payment.handler.spec.ts Create payment handler tests
application/__tests__/handle-callback.handler.spec.ts Callback handling tests
application/__tests__/handle-callback-edge-cases.handler.spec.ts Callback edge cases (race conditions, idempotency)
application/__tests__/get-payment-status.handler.spec.ts Payment status query tests
application/__tests__/refund-payment.handler.spec.ts Refund command tests
application/__tests__/list-transactions.handler.spec.ts List transactions query tests
infrastructure/__tests__/vnpay.service.spec.ts VNPay gateway tests - signature verification
infrastructure/__tests__/momo.service.spec.ts MoMo gateway tests - HMAC-SHA256 verification
infrastructure/__tests__/zalopay.service.spec.ts ZaloPay gateway tests - JSON parsing + MAC verification
infrastructure/__tests__/payment-gateway.factory.spec.ts Factory pattern tests

SECURITY FINDINGS SUMMARY

STRONG SECURITY MEASURES

  1. Callback Signature Verification: All 3 providers (VNPay, MoMo, ZaloPay) verify HMAC signatures using crypto.timingSafeEqual() for constant-time comparison
  2. Atomic Race Condition Prevention: paymentRepo.updateIfStatus() uses conditional WHERE clause to atomically update only if in PENDING/PROCESSING state
  3. Idempotency Protection: Orders + Payments check idempotencyKey to prevent duplicate operations
  4. Rate Limiting: Callback endpoint has Throttle + EndpointRateLimit decorators
  5. Authorization: All endpoints require JwtAuthGuard; admin operations require RolesGuard
  6. Amount Validation: Money VO validates: 0 < amount ≤ 999_999_999_999 VND
  7. State Machine Validation: Order + Escrow enforce valid status transitions

⚠️ SECURITY CONCERNS (NEEDS REVIEW)

  1. Hold/Release Escrow Race Conditions: No Redis lock on hold-escrow/release-escrow handlers - concurrent requests could cause state inconsistencies
  2. No Distributed Lock Mechanism: Escrow operations not protected against simultaneous requests from different servers
  3. Callback Processing Idempotency: While paymentRepo.updateIfStatus() prevents double-processing, idempotency check doesn't verify callback signature consistency
  4. Payment Provider Secrets: Keys loaded from ConfigService - verify env variable encryption at rest
  5. Refund Authorization: Only ADMIN role check - no business logic validation (e.g., refund window, max refund amount)
  6. Order/Escrow Update Race: While holds are atomic for payments, order + escrow updates in handlers are done sequentially (2 DB calls), not atomically

FILES NOT FOUND / NOT IN SCOPE

  • Redis Lock Usage: No Redis locks found in payments module. CONCERN: Critical for escrow hold/release.
  • Shared Payment Utilities: No external payment utility modules referenced
  • Encryption for Payment Data: No field-level encryption for sensitive payment data (though field-encryption service exists in shared module)