@peable.to/shared-types 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,51 @@
1
+ // Payment method breadth on the card rail — which methods a merchant's payers
2
+ // are offered (`pmc_…`), and the web domains wallets may run on (`pmd_…`).
3
+ // Both are Orbit's objects; the provider's configuration and domain are
4
+ // bindings behind them (ADR 0016). Card data and bank credentials are entered
5
+ // in the provider's hosted fields and never reach Peable (PCI SAQ-A).
6
+ import { FIAT_CURRENCY_CODES } from './money.js';
7
+ /**
8
+ * The methods a configuration can turn on.
9
+ *
10
+ * `apple_pay` and `google_pay` are WALLETS over a card: a payment made with one
11
+ * is a `card` payment, so they appear here and never in a PaymentIntent's
12
+ * `payment_method_types`. `bizum` is listed because merchants ask for it and the
13
+ * honest answer is a reason, not an absence: the provider does not offer it.
14
+ *
15
+ * Deliberately absent: the provider's own consumer wallet (its brand on Orbit's
16
+ * checkout, `payment-provider-boundary.md`) and giropay (discontinued by the
17
+ * scheme).
18
+ */
19
+ export const PAYMENT_METHOD_CONFIGURATION_TYPES = [
20
+ 'card',
21
+ 'apple_pay',
22
+ 'google_pay',
23
+ 'sepa_debit',
24
+ 'ideal',
25
+ 'bancontact',
26
+ 'eps',
27
+ 'bizum',
28
+ ];
29
+ /** The card-rail types a PaymentIntent can carry in `payment_method_types`. */
30
+ export const CARD_RAIL_PAYMENT_METHOD_TYPES = [
31
+ 'card',
32
+ 'sepa_debit',
33
+ 'ideal',
34
+ 'bancontact',
35
+ 'eps',
36
+ ];
37
+ /**
38
+ * The currencies each method can take a payment in. A method is offered on a
39
+ * payment only when its currency is listed — so iDEAL never appears on a USD
40
+ * payment, and a payer is never shown a method that would fail.
41
+ */
42
+ export const PAYMENT_METHOD_CURRENCIES = Object.freeze({
43
+ card: FIAT_CURRENCY_CODES,
44
+ apple_pay: FIAT_CURRENCY_CODES,
45
+ google_pay: FIAT_CURRENCY_CODES,
46
+ sepa_debit: ['EUR'],
47
+ ideal: ['EUR'],
48
+ bancontact: ['EUR'],
49
+ eps: ['EUR'],
50
+ bizum: ['EUR'],
51
+ });
@@ -1 +1,2 @@
1
- export {};
1
+ /** Stripe's refund reasons. */
2
+ export const REFUND_REASONS = ['duplicate', 'fraudulent', 'requested_by_customer'];
@@ -0,0 +1,120 @@
1
+ /** A capability's state as the provider reports it; `null` means never requested. */
2
+ export type AccountCapabilityStatus = 'active' | 'pending' | 'inactive';
3
+ /** Whose account this is: the merchant's own, or one of its marketplace sellers. */
4
+ export type AccountType = 'merchant' | 'connected';
5
+ /**
6
+ * What the provider is waiting on. Requirement IDENTIFIERS only
7
+ * (`individual.verification.document`), never their values: the gateway
8
+ * records that a document is owed and never learns what it says.
9
+ */
10
+ export interface AccountRequirements {
11
+ currently_due: string[];
12
+ eventually_due: string[];
13
+ past_due: string[];
14
+ /** Submitted and being checked — nothing for the merchant to do. */
15
+ pending_verification: string[];
16
+ /** Why charges or payouts are disabled, in the provider's own code, or `null`. */
17
+ disabled_reason: string | null;
18
+ }
19
+ export interface AccountBusinessProfile {
20
+ name: string | null;
21
+ url: string | null;
22
+ support_email: string | null;
23
+ support_url: string | null;
24
+ product_description: string | null;
25
+ /** Merchant category code. */
26
+ mcc: string | null;
27
+ }
28
+ export declare const PAYOUT_INTERVALS: readonly ["manual", "daily", "weekly", "monthly"];
29
+ export type PayoutInterval = (typeof PAYOUT_INTERVALS)[number];
30
+ export declare const PAYOUT_WEEKLY_ANCHORS: readonly ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
31
+ export type PayoutWeeklyAnchor = (typeof PAYOUT_WEEKLY_ANCHORS)[number];
32
+ /**
33
+ * When the provider pays the balance out. `manual` means only an explicit
34
+ * `POST /v1/payouts` does — the provider still executes it; Orbit never moves
35
+ * the money itself (ADR 0015 §4).
36
+ */
37
+ export interface AccountPayoutSchedule {
38
+ interval: PayoutInterval;
39
+ /** Days between a payment and its funds becoming payable; `null` when not reported. */
40
+ delay_days: number | null;
41
+ /** `weekly` only. */
42
+ weekly_anchor: PayoutWeeklyAnchor | null;
43
+ /** `monthly` only: the day of the month, 1–31. */
44
+ monthly_anchor: number | null;
45
+ }
46
+ export interface AccountSettings {
47
+ payouts: {
48
+ schedule: AccountPayoutSchedule | null;
49
+ /** What the merchant's bank statement shows for a payout. */
50
+ statement_descriptor: string | null;
51
+ };
52
+ payments: {
53
+ /** What a cardholder's statement shows — the merchant is the merchant of record. */
54
+ statement_descriptor: string | null;
55
+ };
56
+ branding: {
57
+ primary_color: string | null;
58
+ secondary_color: string | null;
59
+ };
60
+ }
61
+ export interface Account {
62
+ /** `ca_…` — the same id space as a marketplace seller. */
63
+ id: string;
64
+ object: 'account';
65
+ /** `true` for a `production` merchant (ADR 0017). */
66
+ livemode: boolean;
67
+ type: AccountType;
68
+ /** ISO 3166-1 alpha-2. Fixed at the provider once created. */
69
+ country: string;
70
+ default_currency: string | null;
71
+ business_profile: AccountBusinessProfile;
72
+ capabilities: {
73
+ card_payments: AccountCapabilityStatus | null;
74
+ transfers: AccountCapabilityStatus | null;
75
+ };
76
+ requirements: AccountRequirements;
77
+ /** Whether the account can take payments as merchant of record (`on_behalf_of`). */
78
+ charges_enabled: boolean;
79
+ /** Whether the provider will pay this account's balance out. */
80
+ payouts_enabled: boolean;
81
+ settings: AccountSettings;
82
+ /** The last provider read; `null` before the first one completed. */
83
+ last_synced_at: number | null;
84
+ created: number;
85
+ }
86
+ /** `POST /v1/account`: the account's settings, written to the provider and read back. */
87
+ export interface UpdateAccountParams {
88
+ /**
89
+ * Opening the account: required the first time (when the merchant has none),
90
+ * refused afterwards — a provider account's country cannot change.
91
+ */
92
+ country?: string;
93
+ business_type?: 'individual' | 'company';
94
+ business_profile?: Partial<AccountBusinessProfile>;
95
+ settings?: {
96
+ payouts?: {
97
+ schedule?: {
98
+ interval: PayoutInterval;
99
+ delay_days?: number;
100
+ weekly_anchor?: PayoutWeeklyAnchor;
101
+ monthly_anchor?: number;
102
+ };
103
+ statement_descriptor?: string;
104
+ };
105
+ payments?: {
106
+ statement_descriptor?: string;
107
+ };
108
+ branding?: {
109
+ primary_color?: string;
110
+ secondary_color?: string;
111
+ };
112
+ };
113
+ }
114
+ /** `POST /v1/account_links`: hosted onboarding (or an update of what was submitted). */
115
+ export interface CreateAccountLinkParams {
116
+ refresh_url: string;
117
+ return_url: string;
118
+ /** Defaults to `account_onboarding`. */
119
+ type?: 'account_onboarding' | 'account_update';
120
+ }
@@ -0,0 +1,204 @@
1
+ import type { CurrencyCode } from './money';
2
+ import type { List } from './list';
3
+ /** One currency's figure in a balance. */
4
+ export interface BalanceAmount {
5
+ /** Signed base units. */
6
+ amount: string;
7
+ currency: CurrencyCode | string;
8
+ }
9
+ /**
10
+ * `GET /v1/balance` — read from the ledger, per currency.
11
+ *
12
+ * A merchant without a fiat account has NO balance and is told so (404
13
+ * `account_missing`), never shown zeros (ADR 0015 consequences).
14
+ */
15
+ export interface Balance {
16
+ object: 'balance';
17
+ livemode: boolean;
18
+ /** Payable now: the provider has made these funds available. */
19
+ available: BalanceAmount[];
20
+ /** Not yet available at the provider. */
21
+ pending: BalanceAmount[];
22
+ /**
23
+ * FairCoin received straight into the merchant's own wallet (ADR 0015 §5).
24
+ * Recorded for the merchant's books only: Orbit never held it, so it is never
25
+ * payable and no payout draws on it.
26
+ */
27
+ informational: BalanceAmount[];
28
+ /** The account (`ca_…`) this balance mirrors; `null` for an Oxy first-party merchant. */
29
+ account: string | null;
30
+ }
31
+ /** Orbit's vocabulary for what moved — the provider's own types, provider-neutral. */
32
+ export declare const BALANCE_TRANSACTION_TYPES: readonly ["charge", "payment", "refund", "payment_refund", "adjustment", "application_fee", "application_fee_refund", "payout", "payout_cancel", "payout_failure", "transfer", "transfer_refund", "dispute", "dispute_reversal", "processing_fee", "faircoin_payment", "faircoin_payment_reversal", "other"];
33
+ export type BalanceTransactionType = (typeof BALANCE_TRANSACTION_TYPES)[number];
34
+ /**
35
+ * - `pending` / `available` — the provider's own status for the funds.
36
+ * - `informational` — a FairCoin payment: recorded, never part of a payable balance.
37
+ */
38
+ export declare const BALANCE_TRANSACTION_STATUSES: readonly ["available", "pending", "informational"];
39
+ export type BalanceTransactionStatus = (typeof BALANCE_TRANSACTION_STATUSES)[number];
40
+ /** One component of a transaction's fee. `processing_fee` is the acquirer's own. */
41
+ export interface BalanceTransactionFeeDetail {
42
+ amount: string;
43
+ currency: string;
44
+ type: 'application_fee' | 'processing_fee' | 'tax' | 'other';
45
+ }
46
+ export interface BalanceTransaction {
47
+ /** `txn_…` */
48
+ id: string;
49
+ object: 'balance_transaction';
50
+ livemode: boolean;
51
+ /** Gross, signed. */
52
+ amount: string;
53
+ /** What was taken, or `null` when the source gave no figure (never `0` for unknown). */
54
+ fee: string | null;
55
+ /** `amount - fee`, as the provider reports it, or `null` with `fee`. */
56
+ net: string | null;
57
+ currency: string;
58
+ type: BalanceTransactionType;
59
+ /** A coarser grouping for reports (`charge`, `refund`, `payout`, `fee`, …). */
60
+ reporting_category: string;
61
+ status: BalanceTransactionStatus;
62
+ /** When the funds became (or become) available; `null` for an informational entry. */
63
+ available_on: number | null;
64
+ /** When it happened at its source. */
65
+ created: number;
66
+ description: string | null;
67
+ exchange_rate: number | null;
68
+ fee_details: BalanceTransactionFeeDetail[];
69
+ /**
70
+ * The Orbit object that caused it (`ch_…`, `re_…`, `po_…`, `fee_…`, `tr_…`,
71
+ * `pi_…`), or `null` when the provider's source is not an object Orbit holds.
72
+ */
73
+ source: string | null;
74
+ /** The payout (`po_…`) that paid this transaction out, once known. */
75
+ payout: string | null;
76
+ /** The account (`ca_…`) whose balance it moved; `null` for a first-party merchant. */
77
+ account: string | null;
78
+ }
79
+ /** `GET /v1/balance_transactions` filters, beside the list parameters. */
80
+ export interface ListBalanceTransactionsParams {
81
+ type?: BalanceTransactionType;
82
+ /** `po_…`: only the transactions that payout paid out. */
83
+ payout?: string;
84
+ /** An Orbit object id the transaction was caused by. */
85
+ source?: string;
86
+ currency?: string;
87
+ }
88
+ export declare const PAYOUT_STATUSES: readonly ["pending", "in_transit", "paid", "failed", "canceled"];
89
+ export type PayoutStatus = (typeof PAYOUT_STATUSES)[number];
90
+ /**
91
+ * A payout (`po_…`) — the provider moving the merchant's balance to the
92
+ * merchant's bank. Executed by the provider, always; Orbit instructs (a manual
93
+ * payout) or mirrors (a scheduled one). The bank account itself stays at the
94
+ * provider and is never on this object.
95
+ */
96
+ export interface Payout {
97
+ id: string;
98
+ object: 'payout';
99
+ livemode: boolean;
100
+ /** The account (`ca_…`) paid out. */
101
+ account: string;
102
+ amount: string;
103
+ currency: string;
104
+ status: PayoutStatus;
105
+ /** When the funds are expected in the bank, as the provider estimates. */
106
+ arrival_date: number | null;
107
+ /** `true` when the provider's schedule created it, `false` for `POST /v1/payouts`. */
108
+ automatic: boolean;
109
+ method: 'standard' | 'instant';
110
+ type: 'bank_account' | 'card';
111
+ description: string | null;
112
+ statement_descriptor: string | null;
113
+ /** The provider's failure code when `failed`; never its prose. */
114
+ failure_code: string | null;
115
+ /** The payout's own `txn_…` once ingested. */
116
+ balance_transaction: string | null;
117
+ metadata: Record<string, string>;
118
+ created: number;
119
+ }
120
+ /** `POST /v1/payouts` — a payout instruction for an account on a manual schedule. */
121
+ export interface CreatePayoutParams {
122
+ amount: string;
123
+ currency: CurrencyCode;
124
+ description?: string;
125
+ statement_descriptor?: string;
126
+ method?: 'standard' | 'instant';
127
+ metadata?: Record<string, string>;
128
+ }
129
+ export interface ListPayoutsParams {
130
+ status?: PayoutStatus;
131
+ }
132
+ /** A refund of an application fee (`fr_…`). */
133
+ export interface FeeRefund {
134
+ id: string;
135
+ object: 'fee_refund';
136
+ amount: string;
137
+ currency: string;
138
+ /** The `fee_…` it refunds. */
139
+ fee: string;
140
+ created: number;
141
+ }
142
+ /**
143
+ * Orbit's fee on one payment (`fee_…`, ADR 0015 §3), taken by the provider from
144
+ * the merchant's account when the charge succeeded. Created from the provider's
145
+ * own record, never computed after the fact.
146
+ */
147
+ export interface ApplicationFee {
148
+ id: string;
149
+ object: 'application_fee';
150
+ livemode: boolean;
151
+ /** The account (`ca_…`) the fee was taken from. */
152
+ account: string;
153
+ amount: string;
154
+ amount_refunded: string;
155
+ currency: string;
156
+ /** The `ch_…` it was taken on, when Orbit holds the charge. */
157
+ charge: string | null;
158
+ /** The `pi_…` it was taken on. */
159
+ payment_intent: string;
160
+ /** Refunded in full. */
161
+ refunded: boolean;
162
+ refunds: List<FeeRefund>;
163
+ created: number;
164
+ }
165
+ /**
166
+ * `GET /v1/reports/payout_reconciliation?payout=po_…` — the balance
167
+ * transactions one payout paid out, with their totals. A list page with the
168
+ * report's fields beside it.
169
+ */
170
+ export interface PayoutReconciliationReport extends List<BalanceTransaction> {
171
+ payout: string;
172
+ /**
173
+ * `complete` once the payout is `paid` and its transactions were read from
174
+ * the provider; `pending` before — the set is not final and the totals may grow.
175
+ */
176
+ status: 'complete' | 'pending';
177
+ currency: string;
178
+ /** Over EVERY transaction of the payout, not just this page. */
179
+ summary: {
180
+ count: number;
181
+ amount: string;
182
+ fee: string;
183
+ net: string;
184
+ };
185
+ }
186
+ /** What the daily reconciliation found disagreeing with the provider. Never auto-corrected. */
187
+ export declare const RECONCILIATION_MISMATCH_KINDS: readonly ["missing_in_ledger", "amount_mismatch", "status_mismatch", "missing_at_provider", "payout_mismatch", "unbalanced_journal"];
188
+ export type ReconciliationMismatchKind = (typeof RECONCILIATION_MISMATCH_KINDS)[number];
189
+ export interface ReconciliationMismatch {
190
+ id: string;
191
+ object: 'reconciliation_mismatch';
192
+ livemode: boolean;
193
+ account: string | null;
194
+ kind: ReconciliationMismatchKind;
195
+ /** The Orbit object concerned (`txn_…`, `po_…`), when Orbit holds one. */
196
+ object_id: string | null;
197
+ /** What the provider says (`expected`) against what the ledger holds (`actual`). */
198
+ expected: Record<string, unknown> | null;
199
+ actual: Record<string, unknown> | null;
200
+ detected_at: number;
201
+ last_seen_at: number;
202
+ /** Set when a later run no longer sees the difference. */
203
+ resolved_at: number | null;
204
+ }
@@ -47,8 +47,15 @@ export interface Charge {
47
47
  } | null;
48
48
  /** Peable-hosted receipts land with Phase 3; the provider's own URL never reaches the wire. */
49
49
  receipt_url: string | null;
50
- /** A Peable balance transaction (`txn_…`) once the ledger exists (Phase 5). */
50
+ /**
51
+ * The ledger's `txn_…` for this charge once the provider's balance
52
+ * transaction was ingested (ADR 0015 §4); `null` until then — never a guess.
53
+ */
51
54
  balance_transaction: string | null;
55
+ /** Orbit's `fee_…` taken on this charge, once the provider created it. */
56
+ application_fee: string | null;
57
+ /** The fee instructed on the payment (`application_fee_amount`), or `null`. */
58
+ application_fee_amount: string | null;
52
59
  description: string | null;
53
60
  receipt_email: string | null;
54
61
  metadata: Record<string, string>;
@@ -1,28 +1,88 @@
1
+ import type { Charge } from './charge';
2
+ import type { CurrencyCode } from './money';
1
3
  import type { PaymentIntent } from './paymentIntent';
2
4
  /**
3
- * A dispute — the card network holding a settled payment's money while a
4
- * cardholder contests it.
5
+ * A dispute — the card network holding a payment's money while a cardholder
6
+ * contests it. Stripe's Dispute object (`dp_…`), Orbit's row.
5
7
  *
6
- * Published because `charge.dispute.created`, `charge.dispute.updated` and
7
- * `charge.dispute.closed` carry it as their `data.object`. It was route-local at first, with the webhook
8
- * carrying the PaymentIntent and the detail reachable only through
9
- * `GET /v1/payment_intents/:id/disputes`. That was inconsistent with the reason
10
- * those two events exist: `disputed` is a DEADLINE, and a merchant who has to
11
- * make a second call to learn when evidence is due is a merchant who can miss it
12
- * while their integration is working exactly as documented.
13
- *
14
- * It is also what the provider does. Stripe's `charge.dispute.created` carries
15
- * the dispute, not the charge — the parity was half-applied.
8
+ * `charge.dispute.created|updated|closed|funds_withdrawn|funds_reinstated`
9
+ * carry it as `data.object`. A dispute is a DEADLINE: `created` carries
10
+ * `evidence_details.due_by`, so a merchant never needs a second call to learn
11
+ * when their response is due.
16
12
  */
17
13
  /**
18
14
  * Where the dispute stands.
19
15
  *
20
16
  * `needs_response` and `under_review` are open; `won` and `lost` are terminal
21
- * and are the only two that say where the money ended up. There is deliberately
22
- * no `closed`: a merchant who is told a dispute "closed" without being told
23
- * which way has been given a notification and no information.
17
+ * and are the only two that say where the money ended up. The provider's
18
+ * `warning_*` inquiry states collapse onto the state they warn about. There is
19
+ * deliberately no `closed`: a merchant told a dispute "closed" without being
20
+ * told which way has been given a notification and no information.
24
21
  */
25
22
  export type DisputeStatus = 'needs_response' | 'under_review' | 'won' | 'lost';
23
+ /**
24
+ * Why the cardholder disputed — Stripe's enum. A network reason code the
25
+ * provider does not map to one of these reads `general`; the row keeps the
26
+ * provider's own word, so nothing is lost by the normalisation.
27
+ */
28
+ export declare const DISPUTE_REASONS: readonly ["bank_cannot_process", "check_returned", "credit_not_processed", "customer_initiated", "debit_not_authorized", "duplicate", "fraudulent", "general", "incorrect_account_details", "insufficient_funds", "noncompliant", "product_not_received", "product_unacceptable", "subscription_canceled", "unrecognized"];
29
+ export type DisputeReason = (typeof DISPUTE_REASONS)[number];
30
+ /** Map any provider reason onto {@link DisputeReason}: unknown or absent is `general`. */
31
+ export declare function normalizeDisputeReason(reason: string | null | undefined): DisputeReason;
32
+ /**
33
+ * The TEXT evidence fields — the network's names, spelled the way the rest of
34
+ * these contracts spell things, never a Peable paraphrase: a merchant assembling
35
+ * a defence is reading their acquirer's guidance, and renamed fields would make
36
+ * that guidance not apply.
37
+ *
38
+ * Declared ONCE. The route's schema is `satisfies Record<DisputeTextEvidenceField, …>`
39
+ * and the Stripe adapter's name map is a total `Record`, so a field added here
40
+ * does not compile until both carry it — the drift that would let a merchant
41
+ * send a field the network never sees.
42
+ */
43
+ export declare const DISPUTE_TEXT_EVIDENCE_FIELDS: readonly ["access_activity_log", "billing_address", "cancellation_policy_disclosure", "cancellation_rebuttal", "customer_email_address", "customer_name", "customer_purchase_ip", "duplicate_charge_explanation", "product_description", "refund_policy_disclosure", "refund_refusal_explanation", "service_date", "shipping_address", "shipping_carrier", "shipping_date", "shipping_tracking_number", "uncategorized_text"];
44
+ export type DisputeTextEvidenceField = (typeof DISPUTE_TEXT_EVIDENCE_FIELDS)[number];
45
+ /**
46
+ * The FILE evidence fields. Each names a `file_…` of purpose `dispute_evidence`
47
+ * uploaded by the same merchant; its bytes go to the provider when the response
48
+ * is submitted, and only then.
49
+ */
50
+ export declare const DISPUTE_FILE_EVIDENCE_FIELDS: readonly ["cancellation_policy", "customer_communication", "customer_signature", "duplicate_charge_documentation", "receipt", "refund_policy", "service_documentation", "shipping_documentation", "uncategorized_file"];
51
+ export type DisputeFileEvidenceField = (typeof DISPUTE_FILE_EVIDENCE_FIELDS)[number];
52
+ export type DisputeEvidenceField = DisputeTextEvidenceField | DisputeFileEvidenceField;
53
+ /**
54
+ * A merchant's evidence as the dispute holds it: every field, `null` when unset.
55
+ *
56
+ * Present ONLY on a live read — `GET /v1/disputes/:id` and the answers of
57
+ * `POST /v1/disputes/:id` and `…/close`. The text half is read from the
58
+ * provider, which is where a draft lives (Peable never stores it); the file half
59
+ * is the `file_…` ids the merchant attached. Lists and event payloads never
60
+ * carry it: an event is stored, and evidence carries a customer's name, email,
61
+ * address and correspondence.
62
+ */
63
+ export type DisputeEvidence = Record<DisputeEvidenceField, string | null>;
64
+ /**
65
+ * `evidence` on `POST /v1/disputes/:id`: any subset of the fields. A text field
66
+ * is the merchant's words; a file field is a `file_…`. An empty string UNSETS
67
+ * the field, as on Stripe.
68
+ */
69
+ export type DisputeEvidenceParams = Partial<Record<DisputeEvidenceField, string>>;
70
+ /** The response's bookkeeping — Stripe's `evidence_details`, plus when it was submitted. */
71
+ export interface DisputeEvidenceDetails {
72
+ /**
73
+ * When the network stops accepting evidence, Unix seconds. `null` once the
74
+ * dispute is closed — there is no deadline for a decision already made.
75
+ */
76
+ due_by: number | null;
77
+ /** Whether any evidence has been staged or submitted. */
78
+ has_evidence: boolean;
79
+ /** Whether the deadline passed without a submission. */
80
+ past_due: boolean;
81
+ /** `0` or `1`: a response is one-shot at the network. */
82
+ submission_count: number;
83
+ /** When the response reached the network, Unix seconds; `null` while it has not. */
84
+ submitted_at: number | null;
85
+ }
26
86
  export interface Dispute {
27
87
  id: string;
28
88
  object: 'dispute';
@@ -31,100 +91,47 @@ export interface Dispute {
31
91
  * `true` for a `production` merchant, `false` for every other environment.
32
92
  */
33
93
  livemode: boolean;
34
- /** The disputed payment's `pi_…`, or the {@link PaymentIntent} with `expand[]=payment_intent`. */
35
- payment_intent: string | PaymentIntent;
36
94
  /**
37
- * Amount in the currency's smallest unit as a canonical integer string —
38
- * the same encoding every other money field on these contracts uses.
39
- *
40
- * It is the DISPUTED amount, which may be less than the payment's: a
41
- * cardholder can contest part of a charge.
95
+ * The DISPUTED amount in the currency's smallest unit, as a canonical integer
96
+ * string. It may be less than the payment's: a cardholder can contest part of
97
+ * a charge.
42
98
  */
43
99
  amount: string;
44
- currency: string;
100
+ currency: CurrencyCode;
101
+ /** `ch_…`, or the {@link Charge} with `expand[]=charge`. */
102
+ charge: string | Charge | null;
103
+ /** The disputed payment's `pi_…`, or the {@link PaymentIntent} with `expand[]=payment_intent`. */
104
+ payment_intent: string | PaymentIntent;
45
105
  status: DisputeStatus;
106
+ reason: DisputeReason;
46
107
  /**
47
- * The network's reason, verbatim and unmapped.
48
- *
49
- * Deliberately an open string rather than a closed set. It is the one field a
50
- * merchant argues against, and a gateway paraphrase would be the words they
51
- * quote while the acquirer holds different ones. `null` when the network sent
52
- * none.
53
- */
54
- reason: string | null;
55
- /**
56
- * When the network stops accepting evidence, Unix seconds.
57
- *
58
- * `null` once the dispute is closed — there is no deadline for a decision
59
- * already made, and a closed dispute still showing one would put a response
60
- * in front of a merchant that can no longer be given.
61
- */
62
- evidence_due_at: number | null;
63
- /**
64
- * When the merchant's response reached the network, Unix seconds. `null` while
65
- * none has been submitted.
66
- *
67
- * The timestamp and nothing else: the evidence itself is forwarded to the
68
- * acquirer and never stored here. What it SAID is readable there, by someone
69
- * with their own authorization — this gateway does not keep a copy of a
70
- * customer's name, address and correspondence.
71
- *
72
- * Together with `evidenceDueAt` it answers the only two questions a merchant
73
- * has about a live dispute: whether they still owe a response, and by when.
108
+ * Whether the disputed payment can still be refunded. Once it has been in
109
+ * full, no further funds are withdrawn because of this dispute.
74
110
  */
75
- evidence_submitted_at: number | null;
111
+ is_charge_refundable: boolean;
112
+ evidence_details: DisputeEvidenceDetails;
113
+ /** Live reads only — see {@link DisputeEvidence}. */
114
+ evidence?: DisputeEvidence;
115
+ metadata: Record<string, string>;
76
116
  created: number;
77
117
  updated_at: number;
78
118
  }
79
119
  /**
80
- * A merchant's response to a dispute — the whole field set, declared once.
120
+ * `POST /v1/disputes/:id`.
81
121
  *
82
- * ## Why this is the wire contract and not four private copies
83
- *
84
- * It was four: the backend's provider port, the route's zod schema, the Stripe
85
- * adapter's name map, and the SDK's params type. Every one of them is the SAME
86
- * list, and the cost of them drifting is specific and silent — a field added to
87
- * three of the four is one a merchant sends, the gateway accepts, and the
88
- * network never sees. They find out when the dispute is decided against them.
89
- *
90
- * So the set lives here, with the wire contracts, and the other three are
91
- * pinned to it: `provider.ts` aliases it, `routes/disputes.ts` declares its
92
- * schema `satisfies Record<keyof DisputeEvidence, …>`, and the Stripe adapter's
93
- * map is a total `Record<keyof DisputeEvidence, …>`. Adding a field here makes
94
- * all three fail to compile until they carry it.
95
- *
96
- * ## What is not here
97
- *
98
- * The field NAMES are the network's, spelled the way the rest of these
99
- * contracts spell things. They are not a Peable vocabulary and are not mapped
100
- * to one: a merchant assembling a defence is reading their acquirer's guidance,
101
- * and a gateway that renamed the fields would make that guidance not apply.
102
- *
103
- * FILE attachments are deliberately absent. They need the provider's upload
104
- * API, a size and type policy, and somewhere for the bytes to live on the way
105
- * through — and offering half of that would let a merchant submit a defence
106
- * missing the receipt it rests on.
107
- *
108
- * None of it is ever stored. It carries a customer's name, their email, a
109
- * billing address and correspondence; it is forwarded to the acquirer and
110
- * forgotten. What is recorded is `evidenceSubmittedAt` and nothing more.
122
+ * `submit: false` (the default) STAGES the evidence: text goes to the provider
123
+ * as a draft, files are attached by id. `submit: true` uploads the attached
124
+ * files to the provider and submits everything — ONCE; a second submission is
125
+ * refused, because the network takes one response.
111
126
  */
112
- export interface DisputeEvidence {
113
- product_description?: string;
114
- customer_name?: string;
115
- customer_email_address?: string;
116
- customer_purchase_ip?: string;
117
- billing_address?: string;
118
- shipping_address?: string;
119
- shipping_carrier?: string;
120
- shipping_date?: string;
121
- shipping_tracking_number?: string;
122
- service_date?: string;
123
- access_activity_log?: string;
124
- cancellation_policy_disclosure?: string;
125
- cancellation_rebuttal?: string;
126
- duplicate_charge_explanation?: string;
127
- refund_policy_disclosure?: string;
128
- refund_refusal_explanation?: string;
129
- uncategorized_text?: string;
127
+ export interface UpdateDisputeParams {
128
+ evidence?: DisputeEvidenceParams;
129
+ /** A PATCH (`""` unsets a key). */
130
+ metadata?: Record<string, string>;
131
+ submit?: boolean;
132
+ }
133
+ /** `GET /v1/disputes`. */
134
+ export interface ListDisputesParams {
135
+ payment_intent?: string;
136
+ charge?: string;
130
137
  }