Appearance
Handoff: SVC-016-renewal-mutation-no-proration billing renewal mutation without proration
Scope
- Service/area:
billing-service. - Task pack:
docs/agents/service-tasks/billing-service.md - Goal: accept the bounded
SVC-016slice that adds a service-local no-proration renewal mutation after plan seeding, payment activation, entitlement outbox, and renewal-preview foundations. - Non-goals: public/default route promotion, provider webhook ingestion, wallet ledger writes, usage counter ownership, prorated upgrades/downgrades, live gateway/browser proof, or changes to legacy source under
/Users/velikho/Desktop/WORKING/HOCTAPAZ/.
Docs Read
go-platform/docs/agents/service-agent-execution-queue.jsongo-platform/docs/agents/service-agent-closeout-ledger.jsongo-platform/docs/agents/service-tasks/billing-service.mdgo-platform/docs/agents/service-tasks/payment-service.mdgo-platform/docs/agents/service-tasks/wallet-service.mdgo-platform/docs/agents/service-tasks/usage-service.mdgo-platform/docs/agents/service-tasks/monetization-broker-transport.mdgo-platform/contracts/events/README.mdgo-platform/docs/api/billing-service.mdgo-platform/services/billing-service/README.md
Legacy Evidence Read
/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- Legacy source was read-only evidence only; no legacy files were changed.
Files Changed
go-platform/services/billing-service/internal/domain/billing.gogo-platform/services/billing-service/internal/usecase/billing.gogo-platform/services/billing-service/internal/usecase/renewal_preview_test.gogo-platform/services/billing-service/internal/http/server.gogo-platform/services/billing-service/internal/http/server_test.gogo-platform/services/billing-service/internal/repository/memory.gogo-platform/services/billing-service/internal/repository/postgres.gogo-platform/services/billing-service/internal/repository/postgres_integration_test.gogo-platform/services/billing-service/migrations/000003_invoice_periods.sqlgo-platform/services/billing-service/api/openapi.yamlgo-platform/contracts/openapi/services/billing-service.yamlgo-platform/services/billing-service/README.mdgo-platform/docs/api/billing-service.mdgo-platform/docs/agents/service-tasks/billing-service.mdgo-platform/docs/agents/handoffs/SVC-016-renewal-mutation-no-proration.mdgo-platform/docs/agents/service-agent-closeout-ledger.jsongo-platform/docs/agents/service-agent-closeout-ledger.md
Behavior Implemented
- Added
POST /v1/subscriptions/{subscriptionId}/renewas an idempotent, service-local full-period renewal mutation. - Kept
prorationBehavior=NONEas the only accepted policy. - Added nullable invoice period bounds (
periodStartedAt,periodEndsAt) and Postgres migration000003_invoice_periods.sqlso renewal invoices carry their intended service period. - Paid renewals create an
OPENrenewal invoice and leave the subscription's current period unchanged until payment-service sends payment evidence. - Payment activation now honors invoice period bounds before falling back to existing subscription periods, so paid renewal activation can advance to the renewal period.
- Zero-price renewals create a
PAIDinvoice, advance the subscription period immediately, and emit onlybilling.entitlements.updated.v1for usage-service. - Renewal mutation does not ingest provider webhooks, does not write wallet ledger rows, and does not emit
billing.subscription.activated.v1unless a payment-backed activation path runs.
Tests And Verification
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/billing-service/internal/usecase ./services/billing-service/internal/http ./services/billing-service/internal/repository -run 'TestBillingRenew|TestBillingRenewal|TestBillingSubscriptionPaymentReplayAndEntitlements|TestHandlePaymentOrderPaidActivatesSubscriptionAndEmitsEntitlements|TestCreateFreeTrial' -count=1 -v - Result: passed.
- Evidence: focused tests covered paid renewal invoice creation, idempotent replay, no-proration policy, zero-price renewal period advancement, usage-only entitlement event, no pre-payment usage/wallet outbox emission for paid renewals, and later payment activation using invoice period bounds.
- Command:
GOTOOLCHAIN=go1.25.11 go test ./services/billing-service/... -count=1 - Result: passed.
- Evidence: billing-service auditsink, eventfanout, HTTP, repository, and usecase packages passed.
- Command:
ruby -e 'require "yaml"; ARGV.each { |path| YAML.load_file(path); puts "ok #{path}" }' services/billing-service/api/openapi.yaml contracts/openapi/services/billing-service.yaml - Result: passed.
- Evidence: service and contract OpenAPI files parsed successfully.
- Command:
cmp -s services/billing-service/api/openapi.yaml contracts/openapi/services/billing-service.yaml - Result: passed.
- Evidence: service OpenAPI and contract mirror are byte-identical.
- Command:
make test-monetization-event-chain - Result: passed.
- Evidence: billing/payment/wallet/usage monetization event-chain coverage passed.
- Command:
make test-monetization-broker-transport - Result: passed.
- Evidence: broker transport coverage, self-test smoke, eventfanout packages, and monetization outbox tests passed.
Route State
- Legacy route: public
/api/wallet*,/api/admin/wallet*, subscription pages, provider hooks, and/wallet?tab=billingremain legacy-proxied by default. - Native route: billing-service exposes service-local
/v1/subscriptions/{subscriptionId}/renewfor renewal rehearsal and internal callers. - Gateway state: no default gateway route was changed and no public billing route was promoted.
- Rollback: leave
deploy/gateway/routes.jsonas the active default route table; disable billing-service callers or avoid routing public subscription traffic to/v1/subscriptions/{subscriptionId}/renew.
Data Notes
- Tables touched:
billing_invoicesgains nullableperiod_started_atandperiod_ends_at. - Migration added:
services/billing-service/migrations/000003_invoice_periods.sql. - Validation report: not applicable because no live data was migrated or backfilled.
Risks / Blockers
- Prorated upgrades, downgrades, credit carry-over, and partial-period billing remain unsupported by design.
- Paid renewal wallet credits still require payment-service paid events and wallet-service consumption of
billing.subscription.activated.v1. - No live gateway/browser proof or public route rollback proof is included.
- Optional Postgres renewal proof was not run because
BILLING_SERVICE_POSTGRES_TEST_DATABASE_URLwas not set in this environment.
Next Step
- Add live billing route rehearsal and rollback evidence after payment-service, wallet-service, usage-service, and gateway route proofs are ready; keep public subscription pages legacy-proxied until then.