# Copper Lane Payments — Retry change · authored alternative

_Draft retry-alternative · version 1 · based on revision 1 · content sha256:6f1911244e421f287163fec53bf4e3e9f8af67e3c1c1cbb5b337e7e7a3fa0be8_

Illustrative payment system · authored sample knowledge

_Readiness 50% · 1 open decisions · 1 decided_

## Inputs and outputs

**Inputs**

- **Buy the €100 lamp** (`int-start-checkout`): Mira · customer → Checkout
- **Read an order’s payment evidence** (`int-support-read`): Merchant support → Payment service
- **Request refund-1001** (`int-request-refund`): Merchant support → Payment service
- **Receive payment confirmation** (`int-payment-confirmation`): Fictional processor → Confirmation handler
- **Receive refund confirmation** (`int-refund-confirmation`): Fictional processor → Confirmation handler

**Outputs**

- **Submit another payment after timeout** (`int-submit-payment`): Payment service → Fictional processor
- **Check the existing operation** (`int-check-outcome`): Payment service → Fictional processor
- **Submit the same refund operation** (`int-submit-refund`): Payment service → Fictional processor

## Outline

- **Mira · customer** (`actor-customer`, Actor) — Buys one €100 lamp

  Mira wants one payment and a clear answer. This is an authored fictional purchase; no real money or card details are involved.
- **Merchant support** (`actor-support`, Actor) — Explains outcomes and requests refunds

  Support reads the recorded evidence for an order and requests a refund when the lamp is unavailable. A request and a confirmed result are distinct.
- **Fictional processor** (`part-processor`, External system) — An external system with an authored contract

  Outside the merchant payment system. Its sample contract permits identical same-key requests for 24 hours and repeated, unordered confirmations. These assumptions are not guarantees about real providers.
- **Copper Lane Payments** (`system-payments`, Boundary, system in scope) — One shared picture of a fictional payment

  Illustrative system · authored sample records. A merchant payment service connected to a fictional processor. No bank, wallet, settlement product or real processor integration is represented.
  - **Checkout** (`part-checkout`, Process) — Makes the payment outcome understandable

    Owns the fictional checkout interface. Its 10-second wait may end before the payment outcome is known. A timeout shows Confirming payment rather than inventing failure.
  - **Payment records** (`part-payment-store`, Data store) — Keeps requests, outcomes and evidence identities

    The fictional payment service owns this authoritative store. Tables describe a proposed design. Example values are authored notes, not live transaction rows or bank observations.
  - **Payment service** (`part-payments`, Process) — Owns logical operations and recorded state

    Owns payment and refund operations, their stable keys and state transitions. Processor calls cross the network. Its confirmation component calls payment operations in-process.
    - **Confirmation handler** (`part-confirmations`, Component) — Checks event identity and compatible outcomes

      A component inside the payment-service process. Receives fictional processor callbacks, recognizes repeated events and applies compatible state changes. Delivery order alone never establishes truth.

## Interactions

- **Apply a compatible outcome** (`int-apply-confirmation`): Confirmation handler → Payment service · Command · In-process · Contract Apply confirmed outcome

  The confirmation component invokes payment operations in-process. Check event identity and logical operation before recording an accepted transition.
- **Check the existing operation** (`int-check-outcome`): Payment service → Fictional processor · Query · Network · Contract Fictional processor operations

  A bounded, on-demand read asks the fictional processor about the existing operation. An unanswered lookup leaves the outcome unknown; it does not start another payment.
- **Receive payment confirmation** (`int-payment-confirmation`): Fictional processor → Confirmation handler · Event · Network · Contract Processor confirmation endpoint

  The fictional processor sends authored event evt-9001 for pay-1001, processor reference processor-pay-7001, EUR 10000 captured. src-processor-confirmation records this prepared evidence.
- **Read the recorded answer** (`int-read-status`): Checkout → Payment service · Query · Network · Contract Payment service API

  Checkout reads the current logical payment. Confirming payment means its processor outcome is still unknown; it is not a failed charge.
- **Read and record payment state** (`int-record-state`): Payment service → Payment records · Data · Network · Contract Payment state storage

  Persist the logical payment, operation key, accepted outcome and processed confirmation identity. Repeating a delivery must not create another payment.
- **Receive refund confirmation** (`int-refund-confirmation`): Fictional processor → Confirmation handler · Event · Network · Contract Processor confirmation endpoint

  Authored event evt-9002 confirms refund-1001 for EUR 10000 against pay-1001. src-refund-confirmation does not establish bank posting.
- **Request one logical payment** (`int-request-payment`): Checkout → Payment service · Command · Network · Contract Payment service API

  Checkout supplies ord-1042 and EUR 10000. The payment service retains logical payment pay-1001 and operation key pay-op-1001.
- **Request refund-1001** (`int-request-refund`): Merchant support → Payment service · Command · Network · Contract Payment service API

  Support requests EUR 10000 against pay-1001. refund-1001 uses its own operation key refund-op-1001 and initially remains requested.
- **Buy the €100 lamp** (`int-start-checkout`): Mira · customer → Checkout · Command · Network · Contract Checkout interface

  Mira submits order ord-1042 through the fictional checkout. src-purchase-requirement records the desired business outcome.
- **Submit another payment after timeout** (`int-submit-payment`): Payment service → Fictional processor · Command · Network · Contract Fictional processor operations

  Unsafe proposed change: a timeout creates a new logical payment and new operation key immediately, although pay-1001 may already have succeeded. Authored scenario; not an executed test.
- **Submit the same refund operation** (`int-submit-refund`): Payment service → Fictional processor · Command · Network · Contract Fictional processor operations

  The payment service submits refund-op-1001 with identical refund parameters. A retry of that logical refund retains the key inside the fictional contract window.
- **Read an order’s payment evidence** (`int-support-read`): Merchant support → Payment service · Query · Network · Contract Payment service API

  Support reads pay-1001 and its recorded source before deciding whether to request the refund. No bank balance or settlement observation is shown.

## Contracts

### Apply confirmed outcome (`contract-apply-confirmation`) · In-process

The confirmation component invokes payment operations inside the same payment-service process. No HTTP hop exists between these two parts.

- Record event identity and a compatible outcome together. A repeated event cannot create another logical payment or refund.

### Checkout interface (`contract-checkout`) · HTTP API

Checkout owns POST /checkout/pay. It accepts the order reference and shows known, unknown or confirmed payment state.

- The authored checkout waiting deadline is 10 seconds; reaching it does not establish processor failure.

### Processor confirmation endpoint (`contract-confirmations`) · HTTP API

The payment service owns POST /processor-confirmations. Its confirmation component receives processor-originated payment and refund events.

- Confirmations can be duplicated or unordered. Event identity and compatible state transitions determine whether recorded state changes; exactly-once delivery is not assumed.

### Payment service API (`contract-payment-api`) · HTTP API

The payment service owns POST /payments, GET /payments/{id} and POST /refunds. Checkout and support call these fictional endpoints.

- An unanswered request retains the same logical payment. Responses distinguish confirming, paid and confirmed-decline states.

### Payment state storage (`contract-payment-store`) · SQL

The payment service owns reads and writes of order references, logical payments, confirmations and refunds in the modeled PostgreSQL design.

- Event identity is recorded with the accepted state transition. The schema is illustrative, not a deployed payment backend.

### Fictional processor operations (`contract-processor`) · HTTP API

The fictional processor owns payment submission, existing-operation lookup and refund submission. This is an authored interface, with no credentials or real provider.

- Identical parameters and operation key identify one operation for 24 hours. Beyond that window, inspect the existing outcome before another attempt; unresolved stays unresolved.

## Use cases

### Pay for the €100 lamp (`uc-pay-order`)

Illustrative system · authored sample records. Mira buys a EUR 100.00 lamp, order ord-1042. Payment pay-1001 initially has an unknown outcome after checkout times out.

- **Actor:** Mira · customer
- **Goal:** Pay once and understand whether the shop can fulfil the order.
- **Trigger:** Mira chooses Pay for the €100 lamp.
- **Outcome:** Processor-confirmed payment lets the shop fulfil the order. Unknown and confirmed-decline outcomes remain visibly different.

**Preconditions**

- The fictional order ord-1042 requests EUR 10000. No real account, card, payment or provider credentials exist.

**Steps**

- **1** Mira chooses Pay for the €100 lamp. — _Buy the €100 lamp_
- **2** The service records, submits and resolves one logical payment. — _Request one logical payment, Read and record payment state, Submit another payment after timeout, Receive payment confirmation, Apply a compatible outcome_
- **3** Checkout reads what is known; a missing answer remains visibly unknown. — _Read the recorded answer, Check the existing operation, Read and record payment state_

**Failures and retries**

- No checkout response within 10 seconds: keep pay-1001 confirming and seek the existing operation’s outcome.
- A confirmed decline is an explicit failure; a separate new attempt can then be requested.

**Acceptance criteria**

- Fulfil only after processor-confirmed payment; preserve the same logical operation when its outcome is unanswered.
- Repeated delivery of evt-9001 leaves one recorded EUR 10000 payment; ordering and exactly-once delivery are not assumed.

**Open questions**

- Who owns a payment that remains unresolved? See dec-unresolved-payment-escalation.

### Refund the unavailable lamp (`uc-refund-payment`)

Illustrative system · authored sample records. The lamp is unavailable after payment confirmation. Support requests refund-1001 for EUR 10000 against pay-1001.

- **Actor:** Merchant support
- **Goal:** Request one refund and explain whether its processor outcome is known.
- **Trigger:** The shop cannot supply Mira’s lamp.
- **Outcome:** A separate authored processor record confirms refund-1001; bank posting and settlement remain outside this story.

**Preconditions**

- The recorded payment is processor-confirmed and no full refund is already confirmed.

**Steps**

- **1** Support checks the confirmed payment and its existing refund history. — _Read an order’s payment evidence_
- **2** Support requests a separate refund; the service records its request and compatible processor confirmation. — _Request refund-1001, Read and record payment state, Submit the same refund operation, Receive refund confirmation, Apply a compatible outcome_

**Failures and retries**

- An unanswered or rejected request is not a confirmed refund. Keep uncertainty visible and do not create another refund to conceal it.

**Acceptance criteria**

- Retain refund-op-1001 for retries of the same refund. Show Requested until compatible processor confirmation is recorded.

## Data model

### Processor confirmation (`data-confirmation`) · Owner Payment service

Authored event evt-9001 confirms pay-1001; evt-9002 confirms refund-1001. Event identity supports deduplication, but arrival order alone never establishes truth. Sources: src-processor-confirmation and src-refund-confirmation.

- event\_id: string, identifier, required — A stable identifier in this authored sample, independent of network delivery attempts.
- payment\_id: reference → Payment, required — An explicit relation to the original authored operation or order reference.
- refund\_id: reference → Refund — An explicit relation to the original authored operation or order reference.
- outcome: string, required — The outcome reported by an authored processor record, checked against the logical operation and compatible state.
- → Payment (N:1)
- → Refund (N:1)

### Order reference (`data-order`) · Owner Payment service

Authored order ord-1042 requests EUR 10000 for Mira’s lamp. This is the minimal payment-facing reference, not a complete merchant order system. Source: src-purchase-requirement.

- id: string, identifier, required — A stable identifier in this authored sample, independent of network delivery attempts.
- amount\_minor: integer, required — Integer minor units. EUR 10000 represents the fictional €100.00 purchase or full refund.
- currency: string, required — The authored example uses EUR; it is not a live exchange rate or price feed.

### Payment (`data-payment`) · Owner Payment service

Authored logical payment pay-1001 references ord-1042 for EUR 10000, using pay-op-1001. Its initial timeout leaves the outcome unknown. src-processor-confirmation later records evt-9001 and processor-pay-7001.

- id: string, identifier, required — A stable identifier in this authored sample, independent of network delivery attempts.
- order\_id: reference → Order reference, required — An explicit relation to the original authored operation or order reference.
- amount\_minor: integer, required — Integer minor units. EUR 10000 represents the fictional €100.00 purchase or full refund.
- currency: string, required — The authored example uses EUR; it is not a live exchange rate or price feed.
- operation\_key: string, required — pay-op-1001 identifies the same logical operation for identical requests inside the fictional 24-hour contract window.
- state: enum, confirming | paid | declined, required — Confirming means unknown. Paid requires compatible processor evidence. Declined requires an explicit decline, not a timeout.
- processor\_reference: string — Optional until the processor outcome is known. The authored confirmation supplies processor-pay-7001.
- → Order reference (N:1)

### Refund (`data-refund`) · Owner Payment service

Authored refund-1001 references pay-1001 for EUR 10000 with separate key refund-op-1001. The requested state precedes prepared confirmation evt-9002; bank posting remains unknown to this sample.

- id: string, identifier, required — A stable identifier in this authored sample, independent of network delivery attempts.
- payment\_id: reference → Payment, required — An explicit relation to the original authored operation or order reference.
- amount\_minor: integer, required — Integer minor units. EUR 10000 represents the fictional €100.00 purchase or full refund.
- currency: string, required — The authored example uses EUR; it is not a live exchange rate or price feed.
- operation\_key: string, required — refund-op-1001 is separate from the payment key and retained when retrying the same authored refund.
- state: enum, requested | confirmed, required — Requested records intent. Confirmed requires compatible fictional processor evidence; neither state reports bank posting.
- → Payment (N:1)

## Database designs

### Payment state · illustrative PostgreSQL design (`database-payments`) · PostgreSQL · Payment records

Proposed physical design for the authored logical entities. Payment and refund records link to their order and original operation; processed confirmation identity is explicit. No database or payment backend is provisioned.

#### `public.orders` · Entity Order reference

- id text NOT NULL
- amount\_minor bigint NOT NULL
- currency text NOT NULL
- _PRIMARY KEY (id)_

#### `public.payments` · Entity Payment

- id text NOT NULL
- order\_id text NOT NULL
- amount\_minor bigint NOT NULL
- currency text NOT NULL
- operation\_key text NOT NULL
- state text NOT NULL
- processor\_reference text
- _PRIMARY KEY (id)_
- _FOREIGN KEY (order\_id) → orders (id)_

#### `public.confirmations` · Entity Processor confirmation

- event\_id text NOT NULL
- payment\_id text NOT NULL
- refund\_id text
- outcome text NOT NULL
- _PRIMARY KEY (event\_id)_
- _FOREIGN KEY (payment\_id) → payments (id)_
- _FOREIGN KEY (refund\_id) → refunds (id)_

#### `public.refunds` · Entity Refund

- id text NOT NULL
- payment\_id text NOT NULL
- amount\_minor bigint NOT NULL
- currency text NOT NULL
- operation\_key text NOT NULL
- state text NOT NULL
- _PRIMARY KEY (id)_
- _FOREIGN KEY (payment\_id) → payments (id)_

## Domain

### Processor confirmation (`concept-confirmation`) · Merchant payments

Authored evidence about one payment or refund operation. Event identity and compatible state determine how it can affect recorded knowledge.

- Repeated or unordered delivery is possible; a missing answer is not proof of failure.
- Carried by: Payment service, Processor confirmation

### Order (`concept-order`) · Merchant payments

The merchant request to buy Mira’s €100 lamp, represented only by the payment-facing reference ord-1042.

- The merchant fulfils only after processor-confirmed payment.
- → Order requires a Logical payment
- Carried by: Payment service, Order reference

### Logical payment (`concept-payment`) · Merchant payments

One business payment operation, pay-1001. Retrying its network request does not inherently create a new payment.

- An unanswered request retains its logical identity; a new explicit attempt after a confirmed decline is a different operation.
- → Logical payment has evidence in Processor confirmation
- Carried by: Payment service, Payment

### Refund (`concept-refund`) · Merchant payments

A separate operation that references an existing payment. refund-1001 requests the full fictional EUR 10000.

- Requested and processor-confirmed outcomes are distinct. Confirmation does not establish bank posting.
- → Refund references a Logical payment
- Carried by: Payment service, Refund

## Decisions

### Who owns a payment that remains unresolved? (`dec-unresolved-payment-escalation`, open)

The fictional contract’s key window is bounded. If the existing outcome still cannot be established, keep the uncertainty open. An owner and escalation policy have not been decided.

- Affects: Pay for the €100 lamp, Refund the unavailable lamp, Payment service, Checkout

### A timeout means unknown, not failed (`dec-timeout-is-unknown`, decided)

Checkout’s authored 10-second deadline can expire before the processor outcome is known. Starting another payment would risk charging twice if the unanswered operation succeeded.

- Keep confirming: Retain the same logical payment and key while seeking its existing outcome. Identical retries inside the fictional 24-hour window refer to that operation.
- Start another payment immediately: Treat a missing answer as failure and create another payment with a new operation key. — The first payment may already have succeeded. A new operation permits a possible duplicate charge.
- **Chosen:** Keep confirming

Keep Confirming payment visible until compatible evidence establishes the outcome. Beyond the 24-hour key window, inspect the existing operation first. Confirmed decline permits a separate explicit attempt.

- Evidence: note src-purchase-requirement: Authored src-purchase-requirement: Mira buys a EUR 100.00 lamp. The fictional shop fulfils only after processor-confirmed payment.
- Evidence: note src-payment-request: Authored src-payment-request: order ord-1042, logical payment pay-1001, EUR 10000 minor units and operation key pay-op-1001.
- Evidence: note src-checkout-timeout: Authored src-checkout-timeout: no reply reached checkout within 10 seconds. This proves the wait ended, not that the processor failed.
- Evidence: note src-processor-confirmation: Authored src-processor-confirmation: evt-9001 confirms EUR 10000 captured for pay-1001 with reference processor-pay-7001. Repeated delivery uses the same event identity.
- Evidence: note src-refund-confirmation: Authored src-refund-confirmation: evt-9002 confirms refund-1001 for EUR 10000 against pay-1001. Bank posting is outside this story.
- Evidence: note src-processor-contract: Authored src-processor-contract: identical same-key requests identify one operation for 24 hours. Confirmations may repeat or arrive out of order. Beyond that window, inspect the existing outcome before another attempt.
- Affects: Pay for the €100 lamp, Refund the unavailable lamp, Checkout, Payment service, Confirmation handler

## Flows

### Resolve one payment without inventing its outcome (`flow-pay-order`) · Pay for the €100 lamp › pay-process

flow-pay-order details pay-process in uc-pay-order. Two start points distinguish the original request from a later processor event. Authored records reveal knowledge, not live telemetry.

1. \[start\] Payment request arrives — Payment service (→ 2)
2. \[process\] Record pay-1001 before calling the processor — Payment service (→ 3)
3. \[process\] Submit pay-op-1001 with the same parameters — Payment service (→ 4)
4. \[decision\] What does the available evidence establish? — Payment service (confirmed → 6, confirmed decline → 7, no answer before deadline → 8)
5. \[end\] Payment confirmed; the shop may fulfil — Checkout
6. \[process\] Record the confirmed payment outcome — Payment service (→ 5)
7. \[error\] Confirmed decline; an explicit new attempt is possible — Checkout
8. \[process\] Start another payment with a new key — Payment service (→ 9)
9. \[end\] Another payment may exist; the first is still unknown — Checkout
10. \[start\] A processor confirmation arrives separately — Confirmation handler (→ 11)
11. \[decision\] Was this confirmation identity already processed? — Confirmation handler (already processed → 12, new, compatible evidence → 13)
12. \[end\] Duplicate evt-9001; recorded outcome unchanged — Confirmation handler
13. \[process\] Record the compatible confirmation and payment outcome — Payment service (→ 5)

### Request and confirm the €100 refund (`flow-refund-payment`) · Refund the unavailable lamp › refund-process

flow-refund-payment details refund-process in uc-refund-payment. A request and a later confirmation have separate entry points. There is no claim about bank posting.

1. \[start\] Support requests a refund for pay-1001 — Payment service (→ 2)
2. \[decision\] Is this confirmed payment eligible for this refund? — Payment service (not eligible → 3, eligible → 4)
3. \[error\] Reject an unsupported or already completed refund — Payment service
4. \[process\] Record refund-1001 and submit refund-op-1001 — Payment service (→ 5)
5. \[end\] Refund requested; its outcome is still unknown — Merchant support
6. \[start\] A refund confirmation arrives separately — Confirmation handler (→ 7)
7. \[decision\] Was this refund event already processed? — Confirmation handler (already processed → 8, new, compatible evidence → 9)
8. \[end\] Duplicate refund event; recorded outcome unchanged — Confirmation handler
9. \[process\] Record the compatible refund confirmation — Payment service (→ 10)
10. \[end\] Refund confirmed by the fictional processor — Merchant support
