Skip to content

billing-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-016

Current implementation: native foundation scaffolded in services/billing-service.

Owns: plans, prices, subscriptions, invoices, billing-cycle state, commercial entitlements.

Must read:

  • legacy AZ package/subscription/payment modules: /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/az-credit.service.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/az-credit-business.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/az-credit.schemas.ts, /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/src/modules/wallet/az-credit.controller.ts.
  • wallet/payment rows in docs/migration/legacy-database-inventory.md.
  • 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/az-credit*.ts
  • AZ package/subscription Prisma rows in /Users/velikho/Desktop/WORKING/HOCTAPAZ/node-platform/apps/api/prisma/schema.prisma.

Writable files:

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

Orchestrator-owned files:

  • payment provider webhook contracts, wallet ledger grants, usage entitlement consumers, gateway route cutovers, and shared deploy manifests unless delegated.

Completed foundation:

  • Audited legacy AzCreditPlan, AzUserSubscription, and AzSubscriptionRequest ownership and kept provider webhooks plus wallet ledger outside billing.
  • Added plan bundle APIs, monthly prices, plan entitlements, subscription plus invoice creation, payment-outcome activation, cancellation, and entitlement snapshots.
  • Added payment.order.paid.v1 consumer adapter. It requires metadata.subscriptionId, reuses the idempotent payment-activation path, and emits billing.subscription.activated.v1 plus billing.entitlements.updated.v1 outbox rows only on first delivery.
  • Exposed POST /v1/events/payment-order-paid for the internal payment-service HTTP fanout rehearsal path. Public wallet/subscription routes remain legacy-proxied.
  • The payment HTTP event receiver requires X-Internal-Service: payment-service plus the shared X-Internal-Token; it rejects missing or invalid credentials before decoding or activating a subscription. Billing's HTTP outbox publisher similarly sends X-Internal-Service: billing-service and the shared token to usage and wallet, while missing production token configuration fails closed.
  • Added durable billing event outbox dispatch for billing.entitlements.updated.v1 events to usage-service /v1/events/billing-entitlements-updated and billing.subscription.activated.v1 events to wallet-service /v1/events/billing-subscription-activated, including in-memory/Postgres pending rows, retry/dead-letter status, and BILLING_EVENT_OUTBOX_* runtime knobs.
  • Added opt-in NATS JetStream publisher mode for the billing outbox. With MONETIZATION_EVENT_TRANSPORT=nats, billing publishes billing.entitlements.updated.v1 to monetization.billing.entitlements.updated.v1 and billing.subscription.activated.v1 to monetization.billing.subscription.activated.v1; HTTP remains the default fallback until subscriber and route proof are complete.
  • Added zero-price activation fanout for seeded FREE_TRIAL: idempotent subscription creation marks the subscription active and invoice paid, stores exactly one billing.entitlements.updated.v1 outbox row for usage-service, and does not emit billing.subscription.activated.v1 without payment evidence.
  • Added non-mutating renewal policy preview at POST /v1/subscriptions/{subscriptionId}/renewal-preview. It computes the next full-month renewal period, amount, entitlement snapshot, and explicit no-proration policy without creating invoices, billing events, wallet ledger entries, or usage counters.
  • Added service-local renewal mutation at POST /v1/subscriptions/{subscriptionId}/renew. It creates idempotent full-period renewal invoices with prorationBehavior=NONE, stores invoice-covered period bounds, leaves paid renewals pending payment-service activation before any usage/wallet outbox events, and advances zero-price renewals with only a usage entitlement event.
  • Added optional best-effort audit-service producer projection for billing state events. AUDIT_SERVICE_URL/AUDIT_SERVICE_BASE_URL enables posting to /v1/audit-events with sourceService=billing-service, sourceEventId=<billing event id>, category=DOMAIN, severity=INFO, outcome=SUCCESS, metadataSchemaVersion=1, retentionPolicy=billing-events, and createdAt -> occurredAt. This producer is separate from the usage/wallet outbox so audit-service failures do not retry downstream entitlement or wallet delivery.
  • Added SeedDefaultPlans(ctx) and native POST /v1/plans/seed-defaults for deterministic, idempotent seeding of legacy fixed plan constants: FREE_TRIAL, AZ_STARTER, AZ_PRO, AZ_CENTER, and AZ_SCHOOL. Entitlements map the legacy commercial limits only: monthly/hourly/weekly credits, monthly tokens, questions, storage bytes, OCR pages, batch import pages, and concurrent jobs. Legacy AZ_CREDIT_ENTERPRISE_PLAN_CODE = "AZ_ENTERPRISE" is returned as customPlanMappings with billingModel=custom_contract, fixedPrice=false, and fixedEntitlements=false; native seeding does not invent hidden enterprise limits or create an active fixed enterprise plan row.
  • Added service-owned Postgres migration, memory store, pgx/Postgres store, OpenAPI contract mirror, Dockerfile, README, focused HTTP tests, and an opt-in Postgres repository integration test.
  • Added an explicitly confirmed disposable-Postgres runner for the focused subscription replay and missing-outbox payment replay integration tests. It publishes Postgres only on a dynamic loopback port, waits with pg_isready, unsets the runtime DATABASE_URL, supplies its own test-only DSN, and removes the container through an exit trap.

Next tasks:

  • Broker publisher mode is available but not default. Next monetization work is usage/wallet subscriber proof, replay/dead-letter tooling, live broker smoke, gateway rehearsal, and rollback evidence before public billing route promotion.
  • Consume payment review events when the payment-service review contract is finalized.
  • Wallet subscription-credit grants are coordinated through wallet-service via billing.subscription.activated.v1; next work is proration expansion and live route proof, not first-activation or renewal invoice scaffolding.
  • Add gateway route rehearsal only after event/adapter parity evidence exists.

Acceptance:

  • Billing does not ingest provider webhooks directly except via payment events.
  • Billing does not own wallet ledger or AZ Credit balance.
  • Payment-event activation is replay-safe by source payment event id.
  • HTTP event ingestion is native-only and does not promote public routes.
  • Plan changes are idempotent and auditable.
  • Legacy plan constants are seeded or explicitly mapped for FREE_TRIAL, AZ_STARTER, AZ_PRO, AZ_CENTER, AZ_SCHOOL, and custom/enterprise plans before subscription route promotion.
  • Invoice/subscription lifecycle parity covers create, activate, cancel, renewal/proration policy, and entitlement snapshots before public billing routes move native.
  • Entitlement events to usage-service and subscription-credit grants to wallet-service are replay-safe and covered by event-delivery tests before plan/subscription cutover.
  • Zero-price plan activation is replay-safe: usage receives the entitlement snapshot once, and wallet-service receives no subscription-credit grant unless a payment-backed billing.subscription.activated.v1 is emitted.

Verification:

  • GOTOOLCHAIN=go1.25.11 go test ./services/billing-service/...
  • BILLING_SUBSCRIPTION_REPLAY_POSTGRES_CONFIRM=disposable-postgres ./scripts/test/billing-subscription-replay-postgres-isolated.sh
  • make test-monetization-event-chain
  • Migration mapping updated before scaffold cutover.
  • OpenAPI parse/diff, Compose config, Helm render/lint, K8s migration and observability coverage after scaffold.

Disposable-runner proof is bounded to billing migrations and the two focused Postgres repository replay tests. It does not use a configured/shared database and does not prove billing runtime startup, provider events, downstream usage/wallet delivery, broker behavior, gateway promotion, or production data.

Go-platform documentation is generated from repository Markdown.