Skip to content

subscription-service - API Contract

Field Value
Port 9005
Mode CQRS + Mediator
Base path /api/v1
Owning sprint Sprint 09
Build status TODO
Requirements FR-13, FR-14, FR-15 (FR-16 MNP post-MVP)

Bounded context: subscription lifecycle state machine and atomic MSISDN allocation/release. Audit mandatory.

Authentication and Authorization

Read and lifecycle endpoints require a valid JWT. Activation is internal (saga-driven).

Endpoints

Method Path Auth Idempotency Summary
POST /api/v1/subscriptions Internal (saga) mandatory Activate a subscription; allocate MSISDN.
GET /api/v1/subscriptions/{id} JWT - Fetch a subscription.
GET /api/v1/subscriptions JWT - List a customer's subscriptions (paged).
POST /api/v1/subscriptions/{id}/suspend JWT - Suspend (e.g. non-payment).
POST /api/v1/subscriptions/{id}/reactivate JWT - Reactivate a suspended subscription.
POST /api/v1/subscriptions/{id}/terminate JWT - Terminate; release MSISDN.

Events

Direction Event
Publish subscription.activated.v1, subscription.suspended.v1, subscription.terminated.v1, subscription.activation-failed.v1, subscription.tariff-changed.v1, msisdn.allocated.v1, msisdn.released.v1
Consume payment.completed.v1 (activation / plan-change trigger), payment.failed.v1 (after grace period)

payment.completed.v1 branching (Sprint 24 Features 24.3/24.4, design-note D1/D2)

The consumer fetches the order (GET /internal/orders/{id}) and branches on its persisted orderType:

  • NEW_LINE - activate a new subscription (unchanged pre-24 behavior; the one-tariff-line invariant counts TARIFF items only, so bundled ADDON items are allowed).
  • PLAN_CHANGE - ChangeTariffCommand against the single tariff item's targetSubscriptionId: re-validates existence, ownership and ACTIVE status, applies the order's pinned tariff snapshot, and publishes subscription.tariff-changed.v1 (keyed by subscriptionId). A terminal failure (subscription gone / not owned / not ACTIVE / same tariff) REUSES subscription.activation-failed.v1 - a documented event-name reuse (design-note D2) - so the existing refund/cancel compensation runs with zero new consumers.
  • ADDON - ignored entirely: standalone addon orders have no activation leg; order-service owns their fulfillment and publishes addon.purchased.v1 itself.

subscription.tariff-changed.v1 rides subscription.events as its third event type: consumers MUST filter on the eventType header and use per-listener consumer groups, and MUST dedup on the event's orderId (the record key is the subscriptionId - NOT unique across successive plan changes of the same subscription).

Notes

  • MSISDN allocation is atomic and concurrency-safe; no MSISDN is double-allocated.
  • Lifecycle transitions are enforced as domain invariants; illegal transitions are rejected.
  • Subscription list pagination: page (default 0), size (default 20) and optional sort=field,asc|desc (direction optional, desc assumed; default createdAt,desc). Sortable fields: createdAt, activatedAt, status. Any other field or a malformed value returns the standard 400 validation error shape.

Reference: service-catalog, event-catalog, ADR-015.