Skip to content

payment-service Agent Tasks

Agent workflow: follow README.md for Audit -> Investigate -> Code -> Test -> Fix; keep this pack's writable scope and verification commands authoritative.

Dispatch type: per-service

Dispatch ID: SVC-019

Current implementation: foundation scaffold exists under services/payment-service.

Owns: payment orders, provider transactions, webhooks, reconciliation, idempotency.

Must read:

  • legacy payment provider/webhook modules: /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet.utils.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet.controller.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet.service.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet-sepay-webhook.spec.ts.
  • billing-service and wallet-service task packs.
  • monetization broker handoff: docs/agents/service-tasks/monetization-broker-transport.md.
  • shared event contracts in contracts/events/README.md.

Legacy source evidence:

  • /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet*.ts
  • /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/wallet-sepay-webhook.spec.ts
  • payment/order Prisma rows in /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma.

Writable files:

  • services/payment-service/**
  • contracts/openapi/services/payment-service.yaml
  • docs/api/payment-service.md

Orchestrator-owned files:

  • billing subscription activation, wallet balance/ledger mutation, gateway route cutovers, provider secret management, and shared deploy manifests unless delegated.

Initial tasks:

  • Audit provider payloads and webhook idempotency behavior. Done for the first SePay slice from legacy wallet.utils.ts, wallet.controller.ts, wallet.service.ts, and az-credit.service.ts.
  • Propose payment order/transaction/webhook schema. Done in services/payment-service/migrations/000001_payment_service.sql.
  • Define events for billing activation and wallet top-up crediting. Done for payment.order.paid.v1; paid payloads now include the account, organization, purpose, payment evidence, metadata, and top-level amountCredits when the order metadata supplies it for wallet top-ups.
  • Model SePay payment order, transaction, webhook replay, and reconcile flows. Done in memory-store/usecase/HTTP foundation with focused tests.
  • Done: add provider auth verification using SEPAY_WEBHOOK_API_KEY and SEPAY_WEBHOOK_SECRET. Native SePay webhooks can require X-SePay-Webhook-Api-Key plus X-SePay-Signature: sha256=<hex hmac-sha256 raw body>, with tests for missing auth, bad signature, and a valid signed request.
  • Done: add opt-in timestamp/stale-request policy for SePay. When SEPAY_WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS is set, native webhooks require X-SePay-Timestamp and verify X-SePay-Signature over <timestamp>.<raw body> within the configured tolerance.
  • Done for the bounded SVC-019-sepay-legacy-response-parity slice: mapped legacy /api/hooks/sepay-payment and /api/wallet/webhooks/sepay to service-local rehearsal routes under /v1/legacy/*. The adapter accepts the legacy flat bank-transfer and nested payment-gateway SePay payload shapes, records them through native idempotent payment webhooks, and preserves old response envelopes: wallet/unmatched/review/replay cases acknowledge with { success: true }, while subscription payment-order matches include a compatibility data object. These routes are not default/public cutovers.
  • Done: add exact non-default Gateway candidate tables for the two SePay callbacks. POST /api/hooks/sepay-payment and POST /api/wallet/webhooks/sepay forward only to the payment-owned /v1/legacy/* normalization adapters. The gateway preserves provider auth headers and raw payloads, while wrong methods and nested paths remain on the legacy prefixes. deploy/gateway/routes.json remains unchanged. Evidence: docs/agents/handoffs/SVC-019-payment-sepay-gateway-route-rehearsal.md.

Acceptance:

  • Payment success emits payment.order.paid.v1; it does not directly activate subscriptions or credit wallets. Foundation records payment events and dispatches paid events through a durable payment outbox over the current internal HTTP sink to billing/wallet subscribers by default. It also has an opt-in NATS JetStream publisher mode for MONETIZATION_EVENT_TRANSPORT=nats, publishing to monetization.payment.order.paid.v1 while preserving the payment outbox as the retry/dead-letter source of truth. Downstream receivers remain replay-safe by source event id.
  • HTTP fallback identifies itself with X-Internal-Service: payment-service and X-Internal-Token. A configured HTTP target without PAYMENT_INTERNAL_SERVICE_TOKEN or INTERNAL_SERVICE_TOKEN disables the publisher rather than delivering an unauthenticated payment event.
  • When AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL is configured, payment-service posts persisted payment events to audit-service /v1/audit-events with sourceService=payment-service, sourceEventId=<payment event id>, category=DOMAIN, severity=INFO, retentionPolicy=payment-events, metadataSchemaVersion=1, and createdAt -> occurredAt. Publish failures are ignored and stay separate from the billing/wallet delivery outbox.
  • Payment does not store wallet balance as source of truth. Done for the foundation.
  • Matched webhook persistence is atomic with its provider transaction, order outcome, paid/review event, and payment.order.paid.v1 outbox row. A failed event serialization rolls back the webhook as well, so a provider retry can retry the complete effect rather than being acknowledged as a replay with no downstream billing/wallet event. A duplicate that already has a transaction still produces only the replay audit event.
  • SePay/provider webhooks require provider authentication before public hook promotion: missing API key/HMAC, bad signature, valid signed request, and replay/idempotency paths must have focused tests.
  • Public /api/hooks/sepay-payment and /api/wallet/webhooks/sepay stay legacy-proxied until provider auth, legacy webhook response parity, replay/idempotency proof, downstream billing/wallet proof, route rehearsal, browser/runtime proof, and rollback evidence exist. SVC-019 now supplies the service-local legacy payload/response parity proof only.

Verification:

  • GOTOOLCHAIN=go1.25.11 go test ./services/payment-service/... -count=1
  • make test-payment-sepay-routes
  • PAYMENT_SEPAY_GATEWAY_ISOLATED_CONFIRM=disposable-runtime make test-payment-sepay-gateway-isolated
  • make test-monetization-event-chain
  • make test-monetization-routes
  • PAYMENT_SERVICE_POSTGRES_TEST_DATABASE_URL='payment-test-dsn' GOTOOLCHAIN=go1.25.11 go test ./services/payment-service/internal/repository -run 'TestPostgresPaymentWebhookReplayIntegration|TestPostgresPaymentWebhookOutcomeAtomicRollbackAndRetry' -count=1 -v
  • Current payment-service tests prove idempotent payment transaction replay and the payment.order.paid.v1 contract payload shape. The isolated Postgres test proves a failed event write leaves no provider webhook, transaction, event, or outbox row, then a retry commits exactly one of each.
  • Current outbox tests prove delivered/retry/dead transitions for the durable paid-event dispatcher.
  • Current audit tests prove payment audit projection redaction and best-effort publish behavior without a second paid audit event on webhook replay.
  • Current HTTP tests prove SePay provider auth rejects missing API key/HMAC and bad signatures while accepting valid raw-body and timestamp-bound signed requests, and reject missing/malformed/stale timestamps when timestamp tolerance is configured.
  • Current legacy SePay HTTP tests prove flat bank-transfer payload parsing, nested payment-gateway subscription parsing, wallet legacy success envelopes without data, subscription compatibility data, unmatched provider acknowledgment, amount-mismatch review, replay idempotency through the legacy adapter, and provider-auth enforcement on /v1/legacy/wallet/webhooks/sepay.

Next tasks:

  • Before reviewing any public route promotion, retain current external provider evidence, refresh downstream billing/wallet replay evidence, review the candidate route table, and rehearse rollback.
  • Broker publisher mode is available but not default. Next monetization work is wallet/billing subscriber proof, replay/dead-letter tooling, live broker smoke, gateway rehearsal, and rollback evidence before any payment route promotion.

Go-platform documentation is generated from repository Markdown.