Appearance
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.yamldocs/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, andaz-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-levelamountCreditswhen 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_KEYandSEPAY_WEBHOOK_SECRET. Native SePay webhooks can requireX-SePay-Webhook-Api-KeyplusX-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_SECONDSis set, native webhooks requireX-SePay-Timestampand verifyX-SePay-Signatureover<timestamp>.<raw body>within the configured tolerance. - Done for the bounded
SVC-019-sepay-legacy-response-parityslice: mapped legacy/api/hooks/sepay-paymentand/api/wallet/webhooks/sepayto 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 compatibilitydataobject. These routes are not default/public cutovers. - Done: add exact non-default Gateway candidate tables for the two SePay callbacks.
POST /api/hooks/sepay-paymentandPOST /api/wallet/webhooks/sepayforward 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.jsonremains 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 forMONETIZATION_EVENT_TRANSPORT=nats, publishing tomonetization.payment.order.paid.v1while 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-serviceandX-Internal-Token. A configured HTTP target withoutPAYMENT_INTERNAL_SERVICE_TOKENorINTERNAL_SERVICE_TOKENdisables the publisher rather than delivering an unauthenticated payment event. - When
AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URLis configured, payment-service posts persisted payment events to audit-service/v1/audit-eventswithsourceService=payment-service,sourceEventId=<payment event id>,category=DOMAIN,severity=INFO,retentionPolicy=payment-events,metadataSchemaVersion=1, andcreatedAt -> 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.v1outbox 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-paymentand/api/wallet/webhooks/sepaystay 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-019now supplies the service-local legacy payload/response parity proof only.
Verification:
GOTOOLCHAIN=go1.25.11 go test ./services/payment-service/... -count=1make test-payment-sepay-routesPAYMENT_SEPAY_GATEWAY_ISOLATED_CONFIRM=disposable-runtime make test-payment-sepay-gateway-isolatedmake test-monetization-event-chainmake test-monetization-routesPAYMENT_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.v1contract 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 compatibilitydata, 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.