Skip to content

dispute-service - API Contract

Field Value
Port 9012
Mode Domain Orchestration
Base path /api/v1
Owning sprint Sprint 22
Build status Features 22.1/22.2/22.3 built (state machine, commands/handlers, API) - not live-verified, no Docker
ADR ADR-028

Bounded context: invoice dispute / PSP chargeback workflow. Coordinates billing-service and payment-service exclusively via outbox events (ADR-009/019) - never shared DB access (ADR-006). Audit and provisional-hold enforcement mandatory (ADR-028 Section 2/5, ADR-021).

Authentication and Authorization

All endpoints require a valid JWT. SUBSCRIBER may open/submit-evidence/withdraw/read only their own dispute (ownership enforced by comparing the caller's linked customer-service id - UserContext.customerId(), not the raw Keycloak subject - against Dispute.customerId). ADMIN/CALL_CENTER_AGENT may resolve any dispute; ADMIN may act on any dispute at all.

Endpoints

Method Path Auth Summary
POST /api/v1/disputes SUBSCRIBER/ADMIN Open a dispute against an invoice and/or payment; moves straight to UNDER_REVIEW.
POST /api/v1/disputes/{id}/evidence/upload SUBSCRIBER/ADMIN Multipart upload; stores the file in MinIO and records the evidence row in one call.
GET /api/v1/disputes/{id}/evidence/{evidenceId}/download-url SUBSCRIBER/ADMIN Time-limited (15 min) presigned MinIO download URL.
POST /api/v1/disputes/{id}/resolve ADMIN/CALL_CENTER_AGENT Body {outcome: CUSTOMER\|MERCHANT, resolutionAmount?}. CUSTOMER requires resolutionAmount.
POST /api/v1/disputes/{id}/withdraw SUBSCRIBER/ADMIN Customer withdraws their own dispute.
GET /api/v1/disputes/{id} SUBSCRIBER/ADMIN Fetch a dispute, its evidence, and its full state-transition history.
GET /api/v1/disputes?customerId=... SUBSCRIBER/ADMIN Paginated list; a non-admin caller is always scoped to their own customer id.

Events

Direction Event
Publish dispute.opened.v1, dispute.evidence-submitted.v1, dispute.resolved-customer.v1, dispute.resolved-merchant.v1, dispute.withdrawn.v1, dispute.closed.v1
Consume none (producer-only this sprint; billing/payment/ticket-service consume in Features 22.4/22.5/22.6)

Notes

  • Provisional-hold invariant (ADR-028 Section 5): dispute.opened.v1 is exclusively a hold signal; only dispute.resolved-customer.v1 may trigger a real credit/refund, and that action is performed by billing-service/payment-service (Features 22.4/22.5), never by dispute-service itself.
  • Evidence binaries are never persisted in dispute-db - only the MinIO object reference (objectRef).
  • Every state-changing endpoint writes exactly one audit_log row (ADR-021, NFR-12).
  • CloseDisputeCommand exists (Feature 22.2) but is not exposed via this API - closure is triggered once a resolution's downstream action is confirmed, a later feature's concern.
  • Not yet built: the API surface has not been live-verified against a real cluster (no Docker this session) - @PreAuthorize/security-filter-chain behavior and the MinIO upload/download round trip are reviewed and unit-tested at the component level, not exercised end to end.