---
id: CAP-MBR-MEM-010
title: Undo a purchase or respond to a payment problem
feature: FEAT-MBR-MEM-03
product_area: membership
module: memberships
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - 'inventory:CAP-MBR-COM-007'
  - 'inventory:CAP-MBR-COM-008'
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-membership:README.md#2026-10-18 — Final acceptance'
  - 'services-membership:docs/migrations/participant/README.md'
purpose: >-
  When an order is voided or cancelled, or its payment is reversed or disputed, the membership it bought is undone,
  ended or suspended exactly once, and Commerce learns what is refundable.
outcomes:
  - A cancelled purchase leaves no trace of membership it never delivered.
  - A started term cancelled after payment ends on a clear date with the program's refund policy applied.
  - A reversed payment withholds rights until it is resolved, without lifting any staff suspension.
concepts:
  - CON-MBR-MEM-002
  - CON-MBR-MEM-001
actors:
  - ACT-MBR-011
  - ACT-MBR-010
  - ACT-MBR-007
surfaces:
  - api
permissions: []
rules:
  - id: BR-CAP-MBR-MEM-010-01
    proved_by: [ATS-MBR-MEM-010-01, TST-MBR-MEM-240]
    statement: >-
      Preparing a cancellation says what cancelling the line would do to the membership and what fraction the
      program's policy makes refundable, and writes nothing.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#allows the cancellation of the pending term, the
      whole line refundable
    origin: none
  - id: BR-CAP-MBR-MEM-010-02
    proved_by: [ATS-MBR-MEM-010-02, TST-MBR-MEM-011]
    statement: >-
      Cancelling a line cancels a pending term and closes its claim, cancels a committed term that has not started by
      the effective date, terminates a started term the day before the effective date, and is denied for a term already
      closed.
    kind: lifecycle
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: 'services-membership:modules/commerce-participant/tests/unit/refund-policy.spec.ts#terminates a committed term that has started'
    origin: none
  - id: BR-CAP-MBR-MEM-010-03
    proved_by: [ATS-MBR-MEM-010-03, TST-MBR-MEM-011]
    statement: >-
      The refundable fraction is everything before the term starts; once it has started, the unused days or unused
      whole months under the program's prorated policy, or nothing under no refund; and nothing after the term ended or
      for a term that never ends.
    kind: calculation
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: 'services-membership:modules/commerce-participant/tests/unit/refund-policy.spec.ts#refunds the unused whole months by prorated_months'
    origin: none
  - id: BR-CAP-MBR-MEM-010-04
    proved_by: [ATS-MBR-MEM-010-04, TST-MBR-MEM-098, TST-MBR-MEM-099]
    statement: >-
      A member may cancel their own purchase while its term has not started; once it has started, the cancellation
      needs staff approval.
    kind: permission
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: 'services-membership:tests/acceptance/commerce/MBR-ACC-0335.acceptance.ts#MBR-ACC-0335'
    origin: none
  - id: BR-CAP-MBR-MEM-010-05
    proved_by: [ATS-MBR-MEM-010-05, TST-MBR-MEM-007, TST-MBR-MEM-240]
    statement: >-
      While a cancellation is pending the membership is fenced, so a competing purchase is refused, and the fence
      clears when the cancellation completes.
    kind: invariant
    enforced_at: [service, database]
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#fences the membership in TX-K, refusing a
      competing purchase meanwhile
    origin: none
  - id: BR-CAP-MBR-MEM-010-06
    proved_by: [ATS-MBR-MEM-010-06, TST-MBR-MEM-240]
    statement: A cancellation is performed once per cancellation obligation; a redelivery answers the same records.
    kind: invariant
    enforced_at: [service, database]
    must_prove: true
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#answers a redelivered cancel obligation with the
      same records
    origin: none
  - id: BR-CAP-MBR-MEM-010-07
    proved_by: [ATS-MBR-MEM-009-14, ATS-MBR-MEM-010-07, ATS-MBR-MEM-010-13, ATS-MBR-MEM-020-15]
    statement: >-
      Voiding an unfulfilled order deletes the pending term and the membership the reservation created, when nothing
      else holds it, so a first purchase leaves nothing behind.
    kind: lifecycle
    enforced_at: [database]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#releases a
      voided order: the pending term and the membership the claim created are deleted
    origin: none
  - id: BR-CAP-MBR-MEM-010-08
    proved_by: []
    statement: >-
      Voiding an order whose line was already fulfilled keeps the membership and tells Commerce the reservation was not
      released because it was committed.
    kind: policy
    enforced_at: [database]
    must_prove: true
    provenance: unknown
    source: FND-MBR-MEM-034
    origin: none
  - id: BR-CAP-MBR-MEM-010-09
    proved_by: [ATS-MBR-MEM-004-06, ATS-MBR-MEM-010-08, ATS-MBR-MEM-010-11, TST-MBR-MEM-007, TST-MBR-MEM-114]
    statement: >-
      A reversed payment or an opened dispute suspends the membership and its children, once however often it is
      delivered; its resolution restores the membership unless another Commerce suspension still stands, and a staff
      suspension is never lifted by Commerce.
    kind: policy
    enforced_at: [service]
    must_prove: true
    provenance: test
    source: >-
      services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#suspends on a
      reversed payment, idempotently, and restores on resolution
    origin: none
  - id: BR-CAP-MBR-MEM-010-10
    proved_by: [ATS-MBR-MEM-010-09, ATS-MBR-MEM-010-10, ATS-MBR-MEM-010-12, ATS-MBR-MEM-014-14]
    statement: >-
      A cancelled or terminated term's chapters end with it, its granted terms follow it, and the change is recorded
      with the order.
    kind: lifecycle
    enforced_at: [service]
    must_prove: true
    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
    origin: none
  - id: BR-CAP-MBR-MEM-010-11
    proved_by: []
    statement: >-
      A status reason whose identifier matches the cancellation's reason code is recorded as the term's closing reason.
    kind: policy
    enforced_at: [service]
    must_prove: false
    provenance: documented
    source: 'services-membership:modules/commerce-participant/services/cancellation/cancellation.ts (header)'
    origin: none
scenarios:
  - id: SC-CAP-MBR-MEM-010-01
    given: An active Regular member's term started three months ago under a prorated-months refund policy.
    when: Staff cancel the line effective today.
    then: The term ends yesterday with its chapter, the unused whole months are refundable, and a competing purchase was refused meanwhile.
    provenance: test
    source: >-
      services-membership:tests/e2e/commerce-cancellation.e2e.spec.ts#plans the termination with the program’s prorated
      fraction
  - id: SC-CAP-MBR-MEM-010-02
    given: A card payment is reversed and then disputed.
    when: Commerce enforces twice and later restores the first.
    then: The member is suspended once and restored only when no Commerce suspension stands.
    provenance: test
    source: 'services-membership:tests/acceptance/commerce/MBR-ACC-0355.acceptance.ts#MBR-ACC-0355'
edge_cases: []
constraints: []
depends_on:
  - CAP-MBR-MEM-009
related:
  - CAP-MBR-MEM-003
  - CAP-MBR-MEM-004
journeys:
  - JNY-MBR-MEM-002
open_questions: []
evidence:
  - repository: services-membership
    source_sha: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
    paths:
      - modules/commerce-participant/services/cancellation/cancellation.ts
      - modules/commerce-participant/services/cancellation/refund-policy.ts
      - modules/commerce-participant/services/enforcement/enforcement.ts
      - modules/commerce-participant/db/commerce-enforcements.table.ts
      - modules/commerce-participant/db/commerce-claims.table.ts
      - modules/commerce-participant/tests/unit/refund-policy.spec.ts
      - modules/commerce-participant/tests/integration/participant.integration.spec.ts
      - tests/e2e/commerce-cancellation.e2e.spec.ts
      - tests/acceptance/commerce
  - repository: react-console
    source_sha: baf6dae382d6f41dce976c7f848667ccc37e076f
    paths:
      - acceptance/membership/reports/defects/commerce.md
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]
exercised_by: [FAS-MBR-MEM-03-02, FAS-MBR-MEM-03-03, FAS-MBR-MEM-03-04, FAS-MBR-MEM-03-05]
suites: [TST-MBR-MEM-007, TST-MBR-MEM-011, TST-MBR-MEM-098, TST-MBR-MEM-099, TST-MBR-MEM-114, TST-MBR-MEM-240]
---

# Undo a purchase or respond to a payment problem

## Purpose

Orders are voided before payment, cancelled after it, and sometimes their payments are reversed or disputed. The membership the order bought must follow exactly once, and Commerce must learn what it may refund.

## How It Works

A void of an unpaid order releases Membership's reservation. The pending term goes, and so does the membership it created, if nothing else holds it. Before a paid line is cancelled, Commerce asks Membership what the cancellation would mean. Membership answers whether the term would be cancelled or ended, and what fraction the program's refund policy makes refundable. While the cancellation is pending the membership is fenced against other purchases. When Commerce performs it, a term that has not started is cancelled and a started one ends the day before the effective date. Its chapters end, its granted terms follow, and the fence clears. A member may cancel what has not started; after that staff approve. When a payment is reversed or disputed, Commerce suspends the membership and its children until the matter is resolved. A staff suspension stays in force whatever Commerce does. Every one of these calls may be repeated safely. The product should tell Commerce when a void meets a line already fulfilled; today it answers as if the line were unknown.

## Relationships

- Depends on `CAP-MBR-MEM-009` because only what was reserved or fulfilled through the checkout can be voided, cancelled or enforced.
- Related to `CAP-MBR-MEM-004` because a Commerce enforcement uses the same suspension.
