---
id: WF-MBR-MEM-014
title: Cancel a purchased line
trigger: Commerce prepares and delivers cancellation of a purchased membership line.
product_area: membership
module: memberships
revision: 1
status: draft
design_intent: proposed
implementation_evidence: partially-observed
inference: []
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-MBR-MEM-010
  - CAP-MBR-MEM-014
  - CAP-MBR-MEM-020
moves:
  - STATE-MBR-MEM-002
  - STATE-MBR-MEM-003
  - STATE-MBR-MEM-004
  - STATE-MBR-MEM-011
crosses:
  - SVC-MBR-MEM-commerce-participant
  - SVC-MBR-MEM-memberships
  - SVC-MBR-MEM-events
idempotency: >-
  Preparation is read-only. A unique cancel-obligation reference recognizes
  repeated completed cancellation.
concurrency: >-
  Commerce claims the cancellation fence inside its transaction; the participant
  locks the membership while completing the cancellation.
hops:
  - 'n': 1
    actor_or_service: Commerce
    action: >-
      Compute the cancellation action and policy-based refundable fraction
      without writing.
    anchor: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts:prepareCancellation
    seam: sync
    on_failure: Return a coded denial without changing records.
    left_inconsistent: No partial local change remains.
    observer: Commerce cancellation response
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#allows the
      cancellation of the pending term, the whole line refundable
  - 'n': 2
    actor_or_service: Commerce cancellation transaction
    action: >-
      Claim the cancellation document and set its fence through the owner
      function.
    anchor: >-
      services-membership:db/migrations/20261018044117_commerce_participant.sql:commerce_claim_cancellation
    seam: sync
    on_failure: Abort Commerce’s claim transaction if fencing fails.
    left_inconsistent: No partial local change remains.
    observer: Commerce claim result
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#fences the
      membership in TX-K, refusing a competing purchase meanwhile
  - 'n': 3
    actor_or_service: Commerce
    action: Deliver the authorized cancel obligation to the participant.
    anchor: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts:cancel
    seam: queue
    on_failure: Commerce supplies the retry after failure.
    left_inconsistent: The cancellation fence remains while obligation completion is pending.
    observer: Commerce cancellation evidence
    provenance: observed
    source: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts
      (header)
  - 'n': 4
    actor_or_service: Commerce participant
    action: >-
      Cancel unstarted coverage or truncate started coverage, constrain chapters
      and linked terms, and clear the fence.
    anchor: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts:cancel
    seam: sync
    on_failure: Roll back local changes and await Commerce retry.
    left_inconsistent: No partial local change remains.
    observer: Membership lifecycle log
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#terminates
      the term the day before the effective date, ends its chapter, clears the
      fence
  - 'n': 5
    actor_or_service: Commerce participant
    action: >-
      Remove an otherwise empty first-purchase holding when eligible, publish
      closure and resolve enabled attention.
    anchor: >-
      services-membership:modules/memberships/services/attention/expiring-memberships.ts:resolveExpiringOnDelete
    seam: sync
    on_failure: Abort the complete local cancellation transaction.
    left_inconsistent: No partial local change remains.
    observer: Published business-event log
    provenance: observed
    source: >-
      services-membership:modules/commerce-participant/services/cancellation/cancellation.ts
      (header)
branches:
  - >-
    A self-service cancellation cannot close started coverage without staff
    approval under the preparation policy.
  - Cancelling unstarted renewal coverage preserves the current earlier period.
waits:
  - >-
    Membership completes closure before delegated Commerce and Accounting refund
    work.
evidence:
  - >-
    services-membership at 5994c270:
    modules/commerce-participant/services/cancellation/*.ts;
    db/migrations/20261018044117_commerce_participant.sql
controls: [TC-AVL-002, TC-AVL-003, TC-AVL-004, TC-AZ-001, TC-AZ-003, TC-AZ-004, TC-CHG-001, TC-CHG-002, TC-CHG-003, TC-CHG-004, TC-CRY-001, TC-CRY-002, TC-CRY-003, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-DAT-007, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-005, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-SEC-001, TC-SEC-004, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Cancel a purchased line

## What It Does

Cancellation separates the membership coverage change from Commerce’s money handling. Membership proposes an action and refundable fraction, fences the holding, then closes coverage when authorized.

## The Flow

```mermaid
flowchart TD
  H1["Compute the cancellation action and policy-based refundable fraction without writing"]
  H2["Claim the cancellation document and set its fence through the owner function"]
  H1 --> H2
  H3["Deliver the authorized cancel obligation to the participant"]
  H2 --> H3
  H4["Cancel unstarted coverage or truncate started coverage, constrain chapters and linked terms, and clear the fence"]
  H3 --> H4
  H5["Remove an otherwise empty first-purchase holding when eligible, publish closure and resolve enabled attention"]
  H4 --> H5
```

## When Something Fails

Preparation can refuse without changing records. After claiming, a failed cancellation leaves the fence until retry succeeds. Membership performs no refund. Repeating preparation for an already closed term currently reports a missing claim rather than the more specific closed result.
