@peable.to/shared-types 0.13.0 → 0.14.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.
@@ -1,2 +1,78 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DISPUTE_FILE_EVIDENCE_FIELDS = exports.DISPUTE_TEXT_EVIDENCE_FIELDS = exports.DISPUTE_REASONS = void 0;
4
+ exports.normalizeDisputeReason = normalizeDisputeReason;
5
+ /**
6
+ * Why the cardholder disputed — Stripe's enum. A network reason code the
7
+ * provider does not map to one of these reads `general`; the row keeps the
8
+ * provider's own word, so nothing is lost by the normalisation.
9
+ */
10
+ exports.DISPUTE_REASONS = [
11
+ 'bank_cannot_process',
12
+ 'check_returned',
13
+ 'credit_not_processed',
14
+ 'customer_initiated',
15
+ 'debit_not_authorized',
16
+ 'duplicate',
17
+ 'fraudulent',
18
+ 'general',
19
+ 'incorrect_account_details',
20
+ 'insufficient_funds',
21
+ 'noncompliant',
22
+ 'product_not_received',
23
+ 'product_unacceptable',
24
+ 'subscription_canceled',
25
+ 'unrecognized',
26
+ ];
27
+ /** Map any provider reason onto {@link DisputeReason}: unknown or absent is `general`. */
28
+ function normalizeDisputeReason(reason) {
29
+ return exports.DISPUTE_REASONS.includes(reason ?? '')
30
+ ? reason
31
+ : 'general';
32
+ }
33
+ /**
34
+ * The TEXT evidence fields — the network's names, spelled the way the rest of
35
+ * these contracts spell things, never a Peable paraphrase: a merchant assembling
36
+ * a defence is reading their acquirer's guidance, and renamed fields would make
37
+ * that guidance not apply.
38
+ *
39
+ * Declared ONCE. The route's schema is `satisfies Record<DisputeTextEvidenceField, …>`
40
+ * and the Stripe adapter's name map is a total `Record`, so a field added here
41
+ * does not compile until both carry it — the drift that would let a merchant
42
+ * send a field the network never sees.
43
+ */
44
+ exports.DISPUTE_TEXT_EVIDENCE_FIELDS = [
45
+ 'access_activity_log',
46
+ 'billing_address',
47
+ 'cancellation_policy_disclosure',
48
+ 'cancellation_rebuttal',
49
+ 'customer_email_address',
50
+ 'customer_name',
51
+ 'customer_purchase_ip',
52
+ 'duplicate_charge_explanation',
53
+ 'product_description',
54
+ 'refund_policy_disclosure',
55
+ 'refund_refusal_explanation',
56
+ 'service_date',
57
+ 'shipping_address',
58
+ 'shipping_carrier',
59
+ 'shipping_date',
60
+ 'shipping_tracking_number',
61
+ 'uncategorized_text',
62
+ ];
63
+ /**
64
+ * The FILE evidence fields. Each names a `file_…` of purpose `dispute_evidence`
65
+ * uploaded by the same merchant; its bytes go to the provider when the response
66
+ * is submitted, and only then.
67
+ */
68
+ exports.DISPUTE_FILE_EVIDENCE_FIELDS = [
69
+ 'cancellation_policy',
70
+ 'customer_communication',
71
+ 'customer_signature',
72
+ 'duplicate_charge_documentation',
73
+ 'receipt',
74
+ 'refund_policy',
75
+ 'service_documentation',
76
+ 'shipping_documentation',
77
+ 'uncategorized_file',
78
+ ];
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ // Files (`file_…`) — bytes a merchant gives Orbit for one stated purpose
3
+ // (ADR 0016: Orbit owns the object; the storage is Oxy Files, a binding that
4
+ // never reaches the wire). Stripe's Files API, with Oxy Files underneath.
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FILE_PURPOSE_POLICIES = exports.FILE_STATUSES = exports.FILE_TYPES = exports.FILE_PURPOSES = void 0;
7
+ /**
8
+ * What a file is FOR. Decided at upload and never changed: the purpose is what
9
+ * sets the type and size policy, who may see the bytes, and which objects may
10
+ * reference the file — a `business_logo` is public, a `dispute_evidence` file
11
+ * is private and can only be attached to the merchant's own disputes.
12
+ */
13
+ exports.FILE_PURPOSES = [
14
+ 'dispute_evidence',
15
+ 'business_logo',
16
+ 'identity_document',
17
+ 'additional_verification',
18
+ ];
19
+ /** The stored format, by its conventional extension (Stripe's `type`). */
20
+ exports.FILE_TYPES = ['pdf', 'png', 'jpg'];
21
+ /**
22
+ * Whether the bytes can be used right now.
23
+ *
24
+ * - `available` — Oxy serves the file; it can be shown or forwarded.
25
+ * - `withheld` — the file still exists and is not being served (its owner
26
+ * trashed it or made it private); it can come back.
27
+ * - `removed` — Oxy said the file is gone for good (deleted, taken down, lost).
28
+ * The object stays so whatever referenced it can say why it stopped working.
29
+ */
30
+ exports.FILE_STATUSES = ['available', 'withheld', 'removed'];
31
+ /**
32
+ * The upload policy of each purpose: the formats it takes, recognised from the
33
+ * bytes and never from a client's `Content-Type`, and the largest file.
34
+ *
35
+ * The provider's own ceilings, restated where the upload is parsed, so a file
36
+ * the card network would refuse is refused at upload — not at the one-shot
37
+ * dispute submission it was meant for.
38
+ */
39
+ exports.FILE_PURPOSE_POLICIES = Object.freeze({
40
+ dispute_evidence: { types: ['pdf', 'png', 'jpg'], max_bytes: 5 * 1024 * 1024 },
41
+ business_logo: { types: ['png', 'jpg'], max_bytes: 512 * 1024 },
42
+ identity_document: { types: ['pdf', 'png', 'jpg'], max_bytes: 10 * 1024 * 1024 },
43
+ additional_verification: { types: ['pdf', 'png', 'jpg'], max_bytes: 10 * 1024 * 1024 },
44
+ });
package/dist/cjs/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TAX_LOCATION_EVIDENCE_TYPES = exports.TAX_REGISTRATION_SCHEMES = exports.PEABLE_BILLING_AUTHORITY_KEYS = exports.canonicalBillingTaxQuote = exports.canonicalBillingAuthority = exports.BILLING_SUBSCRIPTION_STATUSES = exports.SOCIAL_SOURCE_REF_MAX_LENGTH = exports.SOCIAL_SOURCE_APP_MAX_LENGTH = exports.MERCHANT_ENVIRONMENTS = exports.verifyWebhook = exports.signWebhook = exports.SIMULATED_FAIRCOIN_OUTCOMES = exports.REQUEST_LOG_RETENTION_DAYS = exports.REQUEST_LOG_METHODS = exports.CATALOG_LIMITS = exports.COUPON_DURATIONS = exports.TRANSFORM_QUANTITY_ROUNDS = exports.PRICE_TAX_BEHAVIORS = exports.RECURRING_USAGE_TYPES = exports.RECURRING_INTERVALS = exports.PRICE_TYPES = exports.PRICE_TIERS_MODES = exports.PRICE_BILLING_SCHEMES = exports.WEBHOOK_SECRET_ROLL_MAX_EXPIRES_IN_SECONDS = exports.WEBHOOK_ENDPOINTS_MAX_PER_MERCHANT = exports.WEBHOOK_ENDPOINT_DISABLED_REASONS = exports.WEBHOOK_ENDPOINT_STATUSES = exports.PEABLE_API_VERSION = exports.SETUP_INTENT_STATUSES = exports.isValidStatusTransition = exports.canStillBePaid = exports.canExpire = exports.TERMINAL_STATUSES = exports.PAYMENT_INTENT_STATUSES = exports.PAYMENT_INTENT_SETUP_FUTURE_USAGES = exports.PAYMENT_INTENT_RAILS = exports.PAYMENT_INTENT_CAPTURE_METHODS = exports.PAYMENT_INTENT_CANCELLATION_REASONS = exports.MERCHANT_CANCELLATION_REASONS = exports.EXPIRABLE_STATUSES = exports.CARD_ONLY_STATUSES = exports.isCurrencyCode = exports.decimalsFor = exports.CURRENCY_DECIMALS = exports.CURRENCY_CODES = exports.isBaseUnitString = exports.UNITS_PER_COIN = void 0;
3
+ exports.verifyWebhook = exports.signWebhook = exports.SIMULATED_FAIRCOIN_OUTCOMES = exports.REQUEST_LOG_RETENTION_DAYS = exports.REQUEST_LOG_METHODS = exports.PAYMENT_METHOD_CURRENCIES = exports.PAYMENT_METHOD_CONFIGURATION_TYPES = exports.CARD_RAIL_PAYMENT_METHOD_TYPES = exports.FILE_TYPES = exports.FILE_STATUSES = exports.FILE_PURPOSE_POLICIES = exports.FILE_PURPOSES = exports.normalizeDisputeReason = exports.DISPUTE_TEXT_EVIDENCE_FIELDS = exports.DISPUTE_REASONS = exports.DISPUTE_FILE_EVIDENCE_FIELDS = exports.CATALOG_LIMITS = exports.COUPON_DURATIONS = exports.TRANSFORM_QUANTITY_ROUNDS = exports.PRICE_TAX_BEHAVIORS = exports.RECURRING_USAGE_TYPES = exports.RECURRING_INTERVALS = exports.PRICE_TYPES = exports.PRICE_TIERS_MODES = exports.PRICE_BILLING_SCHEMES = exports.WEBHOOK_SECRET_ROLL_MAX_EXPIRES_IN_SECONDS = exports.WEBHOOK_ENDPOINTS_MAX_PER_MERCHANT = exports.WEBHOOK_ENDPOINT_DISABLED_REASONS = exports.WEBHOOK_ENDPOINT_STATUSES = exports.PEABLE_API_VERSION = exports.SETUP_INTENT_STATUSES = exports.isValidStatusTransition = exports.canStillBePaid = exports.canExpire = exports.TERMINAL_STATUSES = exports.PAYMENT_INTENT_STATUSES = exports.PAYMENT_INTENT_SETUP_FUTURE_USAGES = exports.PAYMENT_INTENT_RAILS = exports.PAYMENT_INTENT_CAPTURE_METHODS = exports.PAYMENT_INTENT_CANCELLATION_REASONS = exports.MERCHANT_CANCELLATION_REASONS = exports.EXPIRABLE_STATUSES = exports.CARD_ONLY_STATUSES = exports.isCurrencyCode = exports.decimalsFor = exports.FIAT_CURRENCY_CODES = exports.CURRENCY_DECIMALS = exports.CURRENCY_CODES = exports.isBaseUnitString = exports.UNITS_PER_COIN = void 0;
4
+ exports.TAX_LOCATION_EVIDENCE_TYPES = exports.TAX_REGISTRATION_SCHEMES = exports.PEABLE_BILLING_AUTHORITY_KEYS = exports.canonicalBillingTaxQuote = exports.canonicalBillingAuthority = exports.BILLING_SUBSCRIPTION_STATUSES = exports.SOCIAL_SOURCE_REF_MAX_LENGTH = exports.SOCIAL_SOURCE_APP_MAX_LENGTH = exports.MERCHANT_ENVIRONMENTS = void 0;
4
5
  // @peable.to/shared-types — public API for the Peable Gateway contract.
5
6
  var money_1 = require("./money");
6
7
  Object.defineProperty(exports, "UNITS_PER_COIN", { enumerable: true, get: function () { return money_1.UNITS_PER_COIN; } });
7
8
  Object.defineProperty(exports, "isBaseUnitString", { enumerable: true, get: function () { return money_1.isBaseUnitString; } });
8
9
  Object.defineProperty(exports, "CURRENCY_CODES", { enumerable: true, get: function () { return money_1.CURRENCY_CODES; } });
9
10
  Object.defineProperty(exports, "CURRENCY_DECIMALS", { enumerable: true, get: function () { return money_1.CURRENCY_DECIMALS; } });
11
+ Object.defineProperty(exports, "FIAT_CURRENCY_CODES", { enumerable: true, get: function () { return money_1.FIAT_CURRENCY_CODES; } });
10
12
  Object.defineProperty(exports, "decimalsFor", { enumerable: true, get: function () { return money_1.decimalsFor; } });
11
13
  Object.defineProperty(exports, "isCurrencyCode", { enumerable: true, get: function () { return money_1.isCurrencyCode; } });
12
14
  var paymentIntent_1 = require("./paymentIntent");
@@ -41,6 +43,20 @@ Object.defineProperty(exports, "PRICE_TAX_BEHAVIORS", { enumerable: true, get: f
41
43
  Object.defineProperty(exports, "TRANSFORM_QUANTITY_ROUNDS", { enumerable: true, get: function () { return catalog_1.TRANSFORM_QUANTITY_ROUNDS; } });
42
44
  Object.defineProperty(exports, "COUPON_DURATIONS", { enumerable: true, get: function () { return catalog_1.COUPON_DURATIONS; } });
43
45
  Object.defineProperty(exports, "CATALOG_LIMITS", { enumerable: true, get: function () { return catalog_1.CATALOG_LIMITS; } });
46
+ var dispute_1 = require("./dispute");
47
+ Object.defineProperty(exports, "DISPUTE_FILE_EVIDENCE_FIELDS", { enumerable: true, get: function () { return dispute_1.DISPUTE_FILE_EVIDENCE_FIELDS; } });
48
+ Object.defineProperty(exports, "DISPUTE_REASONS", { enumerable: true, get: function () { return dispute_1.DISPUTE_REASONS; } });
49
+ Object.defineProperty(exports, "DISPUTE_TEXT_EVIDENCE_FIELDS", { enumerable: true, get: function () { return dispute_1.DISPUTE_TEXT_EVIDENCE_FIELDS; } });
50
+ Object.defineProperty(exports, "normalizeDisputeReason", { enumerable: true, get: function () { return dispute_1.normalizeDisputeReason; } });
51
+ var file_1 = require("./file");
52
+ Object.defineProperty(exports, "FILE_PURPOSES", { enumerable: true, get: function () { return file_1.FILE_PURPOSES; } });
53
+ Object.defineProperty(exports, "FILE_PURPOSE_POLICIES", { enumerable: true, get: function () { return file_1.FILE_PURPOSE_POLICIES; } });
54
+ Object.defineProperty(exports, "FILE_STATUSES", { enumerable: true, get: function () { return file_1.FILE_STATUSES; } });
55
+ Object.defineProperty(exports, "FILE_TYPES", { enumerable: true, get: function () { return file_1.FILE_TYPES; } });
56
+ var paymentMethodConfiguration_1 = require("./paymentMethodConfiguration");
57
+ Object.defineProperty(exports, "CARD_RAIL_PAYMENT_METHOD_TYPES", { enumerable: true, get: function () { return paymentMethodConfiguration_1.CARD_RAIL_PAYMENT_METHOD_TYPES; } });
58
+ Object.defineProperty(exports, "PAYMENT_METHOD_CONFIGURATION_TYPES", { enumerable: true, get: function () { return paymentMethodConfiguration_1.PAYMENT_METHOD_CONFIGURATION_TYPES; } });
59
+ Object.defineProperty(exports, "PAYMENT_METHOD_CURRENCIES", { enumerable: true, get: function () { return paymentMethodConfiguration_1.PAYMENT_METHOD_CURRENCIES; } });
44
60
  var requestLog_1 = require("./requestLog");
45
61
  Object.defineProperty(exports, "REQUEST_LOG_METHODS", { enumerable: true, get: function () { return requestLog_1.REQUEST_LOG_METHODS; } });
46
62
  Object.defineProperty(exports, "REQUEST_LOG_RETENTION_DAYS", { enumerable: true, get: function () { return requestLog_1.REQUEST_LOG_RETENTION_DAYS; } });
package/dist/cjs/money.js CHANGED
@@ -3,12 +3,16 @@
3
3
  // canonical integer string in the currency's SMALLEST unit — m⊜ for FairCoin,
4
4
  // cents for a fiat currency — so no float ever touches one.
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.UNITS_PER_COIN = exports.CURRENCY_DECIMALS = exports.CURRENCY_CODES = void 0;
6
+ exports.UNITS_PER_COIN = exports.FIAT_CURRENCY_CODES = exports.CURRENCY_DECIMALS = exports.CURRENCY_CODES = void 0;
7
7
  exports.isCurrencyCode = isCurrencyCode;
8
8
  exports.decimalsFor = decimalsFor;
9
9
  exports.isBaseUnitString = isBaseUnitString;
10
10
  /**
11
- * The currencies this gateway can denominate a payment in.
11
+ * The currencies this gateway can denominate a payment in: FairCoin on the
12
+ * chain rail, and on the card rail the fiat set the provider settles for the
13
+ * payment methods Orbit offers (cards, wallets, SEPA Debit, iDEAL, Bancontact,
14
+ * EPS). A local method narrows it further per payment
15
+ * (`PAYMENT_METHOD_CURRENCIES` in `paymentMethodConfiguration.ts`).
12
16
  *
13
17
  * A closed set, and the database mirrors it as a CHECK
14
18
  * (`db/schema/valueSets.ts`). ADR 0001 D4: the scale of an amount is a property
@@ -16,7 +20,21 @@ exports.isBaseUnitString = isBaseUnitString;
16
20
  * {@link CURRENCY_DECIMALS} exists and why {@link UNITS_PER_COIN} must never be
17
21
  * used to interpret a fiat amount.
18
22
  */
19
- exports.CURRENCY_CODES = ['FAIR', 'EUR', 'USD'];
23
+ exports.CURRENCY_CODES = [
24
+ 'FAIR',
25
+ 'EUR',
26
+ 'USD',
27
+ 'GBP',
28
+ 'CHF',
29
+ 'SEK',
30
+ 'NOK',
31
+ 'DKK',
32
+ 'PLN',
33
+ 'CZK',
34
+ 'CAD',
35
+ 'AUD',
36
+ 'JPY',
37
+ ];
20
38
  /** Whether an arbitrary string is a currency this gateway knows. */
21
39
  function isCurrencyCode(value) {
22
40
  return exports.CURRENCY_CODES.includes(value);
@@ -25,7 +43,7 @@ function isCurrencyCode(value) {
25
43
  * How many decimal places each currency's smallest unit represents — i.e. an
26
44
  * amount of `1` means `10^-decimals` of one whole unit.
27
45
  *
28
- * FairCoin's 8 is a consensus constant and is pinned against
46
+ * ISO 4217's minor units for every fiat member (JPY has none). FairCoin's 8 is a consensus constant and is pinned against
29
47
  * {@link UNITS_PER_COIN} by this package's own tests, so the two spellings of
30
48
  * one fact cannot drift.
31
49
  */
@@ -33,7 +51,21 @@ exports.CURRENCY_DECIMALS = Object.freeze({
33
51
  FAIR: 8,
34
52
  EUR: 2,
35
53
  USD: 2,
54
+ GBP: 2,
55
+ CHF: 2,
56
+ SEK: 2,
57
+ NOK: 2,
58
+ DKK: 2,
59
+ PLN: 2,
60
+ CZK: 2,
61
+ CAD: 2,
62
+ AUD: 2,
63
+ // A zero-decimal currency: an amount of `1` is one yen, never a hundredth of
64
+ // one. The provider counts JPY the same way, so the base unit crosses as is.
65
+ JPY: 0,
36
66
  });
67
+ /** The fiat members — every currency the card rail can denominate a payment in. */
68
+ exports.FIAT_CURRENCY_CODES = exports.CURRENCY_CODES.filter((code) => code !== 'FAIR');
37
69
  /** The decimal places of `currency`'s smallest unit. */
38
70
  function decimalsFor(currency) {
39
71
  return exports.CURRENCY_DECIMALS[currency];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PAYMENT_METHOD_CURRENCIES = exports.CARD_RAIL_PAYMENT_METHOD_TYPES = exports.PAYMENT_METHOD_CONFIGURATION_TYPES = void 0;
4
+ // Payment method breadth on the card rail — which methods a merchant's payers
5
+ // are offered (`pmc_…`), and the web domains wallets may run on (`pmd_…`).
6
+ // Both are Orbit's objects; the provider's configuration and domain are
7
+ // bindings behind them (ADR 0016). Card data and bank credentials are entered
8
+ // in the provider's hosted fields and never reach Peable (PCI SAQ-A).
9
+ const money_1 = require("./money");
10
+ /**
11
+ * The methods a configuration can turn on.
12
+ *
13
+ * `apple_pay` and `google_pay` are WALLETS over a card: a payment made with one
14
+ * is a `card` payment, so they appear here and never in a PaymentIntent's
15
+ * `payment_method_types`. `bizum` is listed because merchants ask for it and the
16
+ * honest answer is a reason, not an absence: the provider does not offer it.
17
+ *
18
+ * Deliberately absent: the provider's own consumer wallet (its brand on Orbit's
19
+ * checkout, `payment-provider-boundary.md`) and giropay (discontinued by the
20
+ * scheme).
21
+ */
22
+ exports.PAYMENT_METHOD_CONFIGURATION_TYPES = [
23
+ 'card',
24
+ 'apple_pay',
25
+ 'google_pay',
26
+ 'sepa_debit',
27
+ 'ideal',
28
+ 'bancontact',
29
+ 'eps',
30
+ 'bizum',
31
+ ];
32
+ /** The card-rail types a PaymentIntent can carry in `payment_method_types`. */
33
+ exports.CARD_RAIL_PAYMENT_METHOD_TYPES = [
34
+ 'card',
35
+ 'sepa_debit',
36
+ 'ideal',
37
+ 'bancontact',
38
+ 'eps',
39
+ ];
40
+ /**
41
+ * The currencies each method can take a payment in. A method is offered on a
42
+ * payment only when its currency is listed — so iDEAL never appears on a USD
43
+ * payment, and a payer is never shown a method that would fail.
44
+ */
45
+ exports.PAYMENT_METHOD_CURRENCIES = Object.freeze({
46
+ card: money_1.FIAT_CURRENCY_CODES,
47
+ apple_pay: money_1.FIAT_CURRENCY_CODES,
48
+ google_pay: money_1.FIAT_CURRENCY_CODES,
49
+ sepa_debit: ['EUR'],
50
+ ideal: ['EUR'],
51
+ bancontact: ['EUR'],
52
+ eps: ['EUR'],
53
+ bizum: ['EUR'],
54
+ });
@@ -1 +1,74 @@
1
- export {};
1
+ /**
2
+ * Why the cardholder disputed — Stripe's enum. A network reason code the
3
+ * provider does not map to one of these reads `general`; the row keeps the
4
+ * provider's own word, so nothing is lost by the normalisation.
5
+ */
6
+ export const DISPUTE_REASONS = [
7
+ 'bank_cannot_process',
8
+ 'check_returned',
9
+ 'credit_not_processed',
10
+ 'customer_initiated',
11
+ 'debit_not_authorized',
12
+ 'duplicate',
13
+ 'fraudulent',
14
+ 'general',
15
+ 'incorrect_account_details',
16
+ 'insufficient_funds',
17
+ 'noncompliant',
18
+ 'product_not_received',
19
+ 'product_unacceptable',
20
+ 'subscription_canceled',
21
+ 'unrecognized',
22
+ ];
23
+ /** Map any provider reason onto {@link DisputeReason}: unknown or absent is `general`. */
24
+ export function normalizeDisputeReason(reason) {
25
+ return DISPUTE_REASONS.includes(reason ?? '')
26
+ ? reason
27
+ : 'general';
28
+ }
29
+ /**
30
+ * The TEXT evidence fields — the network's names, spelled the way the rest of
31
+ * these contracts spell things, never a Peable paraphrase: a merchant assembling
32
+ * a defence is reading their acquirer's guidance, and renamed fields would make
33
+ * that guidance not apply.
34
+ *
35
+ * Declared ONCE. The route's schema is `satisfies Record<DisputeTextEvidenceField, …>`
36
+ * and the Stripe adapter's name map is a total `Record`, so a field added here
37
+ * does not compile until both carry it — the drift that would let a merchant
38
+ * send a field the network never sees.
39
+ */
40
+ export const DISPUTE_TEXT_EVIDENCE_FIELDS = [
41
+ 'access_activity_log',
42
+ 'billing_address',
43
+ 'cancellation_policy_disclosure',
44
+ 'cancellation_rebuttal',
45
+ 'customer_email_address',
46
+ 'customer_name',
47
+ 'customer_purchase_ip',
48
+ 'duplicate_charge_explanation',
49
+ 'product_description',
50
+ 'refund_policy_disclosure',
51
+ 'refund_refusal_explanation',
52
+ 'service_date',
53
+ 'shipping_address',
54
+ 'shipping_carrier',
55
+ 'shipping_date',
56
+ 'shipping_tracking_number',
57
+ 'uncategorized_text',
58
+ ];
59
+ /**
60
+ * The FILE evidence fields. Each names a `file_…` of purpose `dispute_evidence`
61
+ * uploaded by the same merchant; its bytes go to the provider when the response
62
+ * is submitted, and only then.
63
+ */
64
+ export const DISPUTE_FILE_EVIDENCE_FIELDS = [
65
+ 'cancellation_policy',
66
+ 'customer_communication',
67
+ 'customer_signature',
68
+ 'duplicate_charge_documentation',
69
+ 'receipt',
70
+ 'refund_policy',
71
+ 'service_documentation',
72
+ 'shipping_documentation',
73
+ 'uncategorized_file',
74
+ ];
@@ -0,0 +1,41 @@
1
+ // Files (`file_…`) — bytes a merchant gives Orbit for one stated purpose
2
+ // (ADR 0016: Orbit owns the object; the storage is Oxy Files, a binding that
3
+ // never reaches the wire). Stripe's Files API, with Oxy Files underneath.
4
+ /**
5
+ * What a file is FOR. Decided at upload and never changed: the purpose is what
6
+ * sets the type and size policy, who may see the bytes, and which objects may
7
+ * reference the file — a `business_logo` is public, a `dispute_evidence` file
8
+ * is private and can only be attached to the merchant's own disputes.
9
+ */
10
+ export const FILE_PURPOSES = [
11
+ 'dispute_evidence',
12
+ 'business_logo',
13
+ 'identity_document',
14
+ 'additional_verification',
15
+ ];
16
+ /** The stored format, by its conventional extension (Stripe's `type`). */
17
+ export const FILE_TYPES = ['pdf', 'png', 'jpg'];
18
+ /**
19
+ * Whether the bytes can be used right now.
20
+ *
21
+ * - `available` — Oxy serves the file; it can be shown or forwarded.
22
+ * - `withheld` — the file still exists and is not being served (its owner
23
+ * trashed it or made it private); it can come back.
24
+ * - `removed` — Oxy said the file is gone for good (deleted, taken down, lost).
25
+ * The object stays so whatever referenced it can say why it stopped working.
26
+ */
27
+ export const FILE_STATUSES = ['available', 'withheld', 'removed'];
28
+ /**
29
+ * The upload policy of each purpose: the formats it takes, recognised from the
30
+ * bytes and never from a client's `Content-Type`, and the largest file.
31
+ *
32
+ * The provider's own ceilings, restated where the upload is parsed, so a file
33
+ * the card network would refuse is refused at upload — not at the one-shot
34
+ * dispute submission it was meant for.
35
+ */
36
+ export const FILE_PURPOSE_POLICIES = Object.freeze({
37
+ dispute_evidence: { types: ['pdf', 'png', 'jpg'], max_bytes: 5 * 1024 * 1024 },
38
+ business_logo: { types: ['png', 'jpg'], max_bytes: 512 * 1024 },
39
+ identity_document: { types: ['pdf', 'png', 'jpg'], max_bytes: 10 * 1024 * 1024 },
40
+ additional_verification: { types: ['pdf', 'png', 'jpg'], max_bytes: 10 * 1024 * 1024 },
41
+ });
package/dist/esm/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  // @peable.to/shared-types — public API for the Peable Gateway contract.
2
- export { UNITS_PER_COIN, isBaseUnitString, CURRENCY_CODES, CURRENCY_DECIMALS, decimalsFor, isCurrencyCode, } from './money.js';
2
+ export { UNITS_PER_COIN, isBaseUnitString, CURRENCY_CODES, CURRENCY_DECIMALS, FIAT_CURRENCY_CODES, decimalsFor, isCurrencyCode, } from './money.js';
3
3
  export { CARD_ONLY_STATUSES, EXPIRABLE_STATUSES, MERCHANT_CANCELLATION_REASONS, PAYMENT_INTENT_CANCELLATION_REASONS, PAYMENT_INTENT_CAPTURE_METHODS, PAYMENT_INTENT_RAILS, PAYMENT_INTENT_SETUP_FUTURE_USAGES, PAYMENT_INTENT_STATUSES, TERMINAL_STATUSES, canExpire, canStillBePaid, isValidStatusTransition, } from './paymentIntent.js';
4
4
  export { SETUP_INTENT_STATUSES, } from './setupIntent.js';
5
5
  export { PEABLE_API_VERSION, } from './event.js';
6
6
  export { WEBHOOK_ENDPOINT_STATUSES, WEBHOOK_ENDPOINT_DISABLED_REASONS, WEBHOOK_ENDPOINTS_MAX_PER_MERCHANT, WEBHOOK_SECRET_ROLL_MAX_EXPIRES_IN_SECONDS, } from './webhookEndpoint.js';
7
7
  export { PRICE_BILLING_SCHEMES, PRICE_TIERS_MODES, PRICE_TYPES, RECURRING_INTERVALS, RECURRING_USAGE_TYPES, PRICE_TAX_BEHAVIORS, TRANSFORM_QUANTITY_ROUNDS, COUPON_DURATIONS, CATALOG_LIMITS, } from './catalog.js';
8
+ export { DISPUTE_FILE_EVIDENCE_FIELDS, DISPUTE_REASONS, DISPUTE_TEXT_EVIDENCE_FIELDS, normalizeDisputeReason, } from './dispute.js';
9
+ export { FILE_PURPOSES, FILE_PURPOSE_POLICIES, FILE_STATUSES, FILE_TYPES, } from './file.js';
10
+ export { CARD_RAIL_PAYMENT_METHOD_TYPES, PAYMENT_METHOD_CONFIGURATION_TYPES, PAYMENT_METHOD_CURRENCIES, } from './paymentMethodConfiguration.js';
8
11
  export { REQUEST_LOG_METHODS, REQUEST_LOG_RETENTION_DAYS, } from './requestLog.js';
9
12
  export { SIMULATED_FAIRCOIN_OUTCOMES, } from './testHelpers.js';
10
13
  export { signWebhook, verifyWebhook } from './webhookSigner.js';
package/dist/esm/money.js CHANGED
@@ -2,7 +2,11 @@
2
2
  // canonical integer string in the currency's SMALLEST unit — m⊜ for FairCoin,
3
3
  // cents for a fiat currency — so no float ever touches one.
4
4
  /**
5
- * The currencies this gateway can denominate a payment in.
5
+ * The currencies this gateway can denominate a payment in: FairCoin on the
6
+ * chain rail, and on the card rail the fiat set the provider settles for the
7
+ * payment methods Orbit offers (cards, wallets, SEPA Debit, iDEAL, Bancontact,
8
+ * EPS). A local method narrows it further per payment
9
+ * (`PAYMENT_METHOD_CURRENCIES` in `paymentMethodConfiguration.ts`).
6
10
  *
7
11
  * A closed set, and the database mirrors it as a CHECK
8
12
  * (`db/schema/valueSets.ts`). ADR 0001 D4: the scale of an amount is a property
@@ -10,7 +14,21 @@
10
14
  * {@link CURRENCY_DECIMALS} exists and why {@link UNITS_PER_COIN} must never be
11
15
  * used to interpret a fiat amount.
12
16
  */
13
- export const CURRENCY_CODES = ['FAIR', 'EUR', 'USD'];
17
+ export const CURRENCY_CODES = [
18
+ 'FAIR',
19
+ 'EUR',
20
+ 'USD',
21
+ 'GBP',
22
+ 'CHF',
23
+ 'SEK',
24
+ 'NOK',
25
+ 'DKK',
26
+ 'PLN',
27
+ 'CZK',
28
+ 'CAD',
29
+ 'AUD',
30
+ 'JPY',
31
+ ];
14
32
  /** Whether an arbitrary string is a currency this gateway knows. */
15
33
  export function isCurrencyCode(value) {
16
34
  return CURRENCY_CODES.includes(value);
@@ -19,7 +37,7 @@ export function isCurrencyCode(value) {
19
37
  * How many decimal places each currency's smallest unit represents — i.e. an
20
38
  * amount of `1` means `10^-decimals` of one whole unit.
21
39
  *
22
- * FairCoin's 8 is a consensus constant and is pinned against
40
+ * ISO 4217's minor units for every fiat member (JPY has none). FairCoin's 8 is a consensus constant and is pinned against
23
41
  * {@link UNITS_PER_COIN} by this package's own tests, so the two spellings of
24
42
  * one fact cannot drift.
25
43
  */
@@ -27,7 +45,21 @@ export const CURRENCY_DECIMALS = Object.freeze({
27
45
  FAIR: 8,
28
46
  EUR: 2,
29
47
  USD: 2,
48
+ GBP: 2,
49
+ CHF: 2,
50
+ SEK: 2,
51
+ NOK: 2,
52
+ DKK: 2,
53
+ PLN: 2,
54
+ CZK: 2,
55
+ CAD: 2,
56
+ AUD: 2,
57
+ // A zero-decimal currency: an amount of `1` is one yen, never a hundredth of
58
+ // one. The provider counts JPY the same way, so the base unit crosses as is.
59
+ JPY: 0,
30
60
  });
61
+ /** The fiat members — every currency the card rail can denominate a payment in. */
62
+ export const FIAT_CURRENCY_CODES = CURRENCY_CODES.filter((code) => code !== 'FAIR');
31
63
  /** The decimal places of `currency`'s smallest unit. */
32
64
  export function decimalsFor(currency) {
33
65
  return CURRENCY_DECIMALS[currency];
@@ -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,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
  }
@@ -44,8 +44,10 @@ export interface BillingObservation {
44
44
  * Refunds are not PaymentIntent statuses: `refund.created|updated|failed` carry
45
45
  * the Refund, and `charge.refunded` the Charge whose `amount_refunded` moved.
46
46
  * A dispute is the network's process beside a payment that stays `succeeded`:
47
- * `charge.dispute.created` is a DEADLINE (evidence is due), `closed` carries
48
- * the outcome.
47
+ * `charge.dispute.created` is a DEADLINE (evidence is due), `updated` follows
48
+ * a staged or submitted response, `closed` carries the outcome, and
49
+ * `funds_withdrawn` / `funds_reinstated` the money's movement — never the
50
+ * evidence, which is not stored and so is never in an event.
49
51
  */
50
52
  export interface WebhookEventPayload {
51
53
  'payment_intent.created': PaymentIntent;
@@ -59,6 +61,10 @@ export interface WebhookEventPayload {
59
61
  'charge.dispute.created': Dispute;
60
62
  'charge.dispute.updated': Dispute;
61
63
  'charge.dispute.closed': Dispute;
64
+ /** The network took the disputed amount from the balance. */
65
+ 'charge.dispute.funds_withdrawn': Dispute;
66
+ /** The network returned it (the dispute was won, or withdrawn by the cardholder). */
67
+ 'charge.dispute.funds_reinstated': Dispute;
62
68
  'refund.created': Refund;
63
69
  'refund.updated': Refund;
64
70
  'refund.failed': Refund;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * What a file is FOR. Decided at upload and never changed: the purpose is what
3
+ * sets the type and size policy, who may see the bytes, and which objects may
4
+ * reference the file — a `business_logo` is public, a `dispute_evidence` file
5
+ * is private and can only be attached to the merchant's own disputes.
6
+ */
7
+ export declare const FILE_PURPOSES: readonly ["dispute_evidence", "business_logo", "identity_document", "additional_verification"];
8
+ export type FilePurpose = (typeof FILE_PURPOSES)[number];
9
+ /** The stored format, by its conventional extension (Stripe's `type`). */
10
+ export declare const FILE_TYPES: readonly ["pdf", "png", "jpg"];
11
+ export type FileType = (typeof FILE_TYPES)[number];
12
+ /**
13
+ * Whether the bytes can be used right now.
14
+ *
15
+ * - `available` — Oxy serves the file; it can be shown or forwarded.
16
+ * - `withheld` — the file still exists and is not being served (its owner
17
+ * trashed it or made it private); it can come back.
18
+ * - `removed` — Oxy said the file is gone for good (deleted, taken down, lost).
19
+ * The object stays so whatever referenced it can say why it stopped working.
20
+ */
21
+ export declare const FILE_STATUSES: readonly ["available", "withheld", "removed"];
22
+ export type FileStatus = (typeof FILE_STATUSES)[number];
23
+ /**
24
+ * The upload policy of each purpose: the formats it takes, recognised from the
25
+ * bytes and never from a client's `Content-Type`, and the largest file.
26
+ *
27
+ * The provider's own ceilings, restated where the upload is parsed, so a file
28
+ * the card network would refuse is refused at upload — not at the one-shot
29
+ * dispute submission it was meant for.
30
+ */
31
+ export declare const FILE_PURPOSE_POLICIES: Readonly<Record<FilePurpose, {
32
+ readonly types: readonly FileType[];
33
+ readonly max_bytes: number;
34
+ }>>;
35
+ /**
36
+ * The API's `file` object. Named `FileObject` rather than `File` so it never
37
+ * shadows the DOM's `File` in a browser that imports both.
38
+ */
39
+ export interface FileObject {
40
+ /** `file_…`. */
41
+ id: string;
42
+ object: 'file';
43
+ /** The mode of the merchant that uploaded it (ADR 0017). */
44
+ livemode: boolean;
45
+ purpose: FilePurpose;
46
+ /** The name it was uploaded with, if it had a printable one. */
47
+ filename: string | null;
48
+ /** Bytes. */
49
+ size: number;
50
+ type: FileType;
51
+ status: FileStatus;
52
+ /**
53
+ * A public URL for a `business_logo` that is `available` — what a payer
54
+ * surface renders. `null` for every other purpose: evidence and identity
55
+ * documents are never addressable by URL, and their bytes leave Peable only
56
+ * to the provider they are submitted to.
57
+ */
58
+ url: string | null;
59
+ created: number;
60
+ }
61
+ /**
62
+ * `POST /v1/files` — exactly one source:
63
+ *
64
+ * - the bytes, as `multipart/form-data` with a `purpose` field and a `file`
65
+ * part (the SDK's `files.create({ purpose, file })` builds it);
66
+ * - `oxy_file`: as JSON, an Oxy file the caller already owns (their
67
+ * application's, or an account they are or act for). Its ownership is
68
+ * checked once, here, and never again.
69
+ */
70
+ export interface CreateFileFromOxyParams {
71
+ purpose: FilePurpose;
72
+ /** Oxy file id. */
73
+ oxy_file: string;
74
+ }
75
+ /** `GET /v1/files`. */
76
+ export interface ListFilesParams {
77
+ purpose?: FilePurpose;
78
+ }
@@ -1,4 +1,4 @@
1
- export { UNITS_PER_COIN, isBaseUnitString, type CurrencyCode, CURRENCY_CODES, CURRENCY_DECIMALS, decimalsFor, isCurrencyCode, } from './money';
1
+ export { UNITS_PER_COIN, isBaseUnitString, type CurrencyCode, type FiatCurrencyCode, CURRENCY_CODES, CURRENCY_DECIMALS, FIAT_CURRENCY_CODES, decimalsFor, isCurrencyCode, } from './money';
2
2
  export type { NetworkType } from './network';
3
3
  export type { ExpandParams, List, ListParams, RangeQuery } from './list';
4
4
  export { type Address, type AddressParams, type CancelPaymentIntentParams, type CapturePaymentIntentParams, type ConfirmPaymentIntentParams, type CreatePaymentIntentParams, type PaymentIntent, type PaymentIntentCancellationReason, type PaymentIntentCaptureMethod, type PaymentIntentFaircoin, type PaymentIntentLastPaymentError, type PaymentIntentNextAction, type PaymentIntentRail, type PaymentIntentSetupFutureUsage, type PaymentIntentStatus, type PaymentMethodType, type Shipping, type ShippingParams, type UpdatePaymentIntentParams, CARD_ONLY_STATUSES, EXPIRABLE_STATUSES, MERCHANT_CANCELLATION_REASONS, PAYMENT_INTENT_CANCELLATION_REASONS, PAYMENT_INTENT_CAPTURE_METHODS, PAYMENT_INTENT_RAILS, PAYMENT_INTENT_SETUP_FUTURE_USAGES, PAYMENT_INTENT_STATUSES, TERMINAL_STATUSES, canExpire, canStillBePaid, isValidStatusTransition, } from './paymentIntent';
@@ -9,7 +9,9 @@ export { type CancelSetupIntentParams, type CreateSetupIntentParams, type SetupI
9
9
  export { PEABLE_API_VERSION, type WebhookEventType, type WebhookEvent, type WebhookEventPayload, type WebhookEventRequest, type BillingObservation, } from './event';
10
10
  export { WEBHOOK_ENDPOINT_STATUSES, WEBHOOK_ENDPOINT_DISABLED_REASONS, WEBHOOK_ENDPOINTS_MAX_PER_MERCHANT, WEBHOOK_SECRET_ROLL_MAX_EXPIRES_IN_SECONDS, type WebhookEnabledEvent, type WebhookEndpoint, type WebhookEndpointStatus, type WebhookEndpointDisabledReason, type DeletedWebhookEndpoint, type CreateWebhookEndpointParams, type UpdateWebhookEndpointParams, type RollWebhookEndpointSecretParams, } from './webhookEndpoint';
11
11
  export { PRICE_BILLING_SCHEMES, PRICE_TIERS_MODES, PRICE_TYPES, RECURRING_INTERVALS, RECURRING_USAGE_TYPES, PRICE_TAX_BEHAVIORS, TRANSFORM_QUANTITY_ROUNDS, COUPON_DURATIONS, CATALOG_LIMITS, type PriceBillingScheme, type PriceTiersMode, type PriceType, type RecurringInterval, type RecurringUsageType, type PriceTaxBehavior, type TransformQuantityRound, type CouponDuration, type ProductMarketingFeature, type Product, type DeletedProduct, type PriceTier, type PriceTransformQuantity, type PriceRecurring, type Price, type Coupon, type DeletedCoupon, type PromotionCodeRestrictions, type PromotionCode, type RecurringParams, type PriceTierParams, type PriceDataParams, type DefaultPriceDataParams, type CreateProductParams, type UpdateProductParams, type ProductListParams, type CreatePriceParams, type UpdatePriceParams, type PriceListParams, type CreateCouponParams, type UpdateCouponParams, type CouponListParams, type CreatePromotionCodeParams, type UpdatePromotionCodeParams, type PromotionCodeListParams, } from './catalog';
12
- export type { Dispute, DisputeEvidence, DisputeStatus } from './dispute';
12
+ export { DISPUTE_FILE_EVIDENCE_FIELDS, DISPUTE_REASONS, DISPUTE_TEXT_EVIDENCE_FIELDS, normalizeDisputeReason, type Dispute, type DisputeEvidence, type DisputeEvidenceDetails, type DisputeEvidenceField, type DisputeEvidenceParams, type DisputeFileEvidenceField, type DisputeReason, type DisputeStatus, type DisputeTextEvidenceField, type ListDisputesParams, type UpdateDisputeParams, } from './dispute';
13
+ export { FILE_PURPOSES, FILE_PURPOSE_POLICIES, FILE_STATUSES, FILE_TYPES, type CreateFileFromOxyParams, type FileObject, type FilePurpose, type FileStatus, type FileType, type ListFilesParams, } from './file';
14
+ export { CARD_RAIL_PAYMENT_METHOD_TYPES, PAYMENT_METHOD_CONFIGURATION_TYPES, PAYMENT_METHOD_CURRENCIES, type CardRailPaymentMethodType, type CreatePaymentMethodConfigurationParams, type CreatePaymentMethodDomainParams, type ListPaymentMethodDomainsParams, type PaymentMethodConfiguration, type PaymentMethodConfigurationEntry, type PaymentMethodConfigurationEntryParams, type PaymentMethodConfigurationType, type PaymentMethodDisplayPreference, type PaymentMethodDomain, type PaymentMethodDomainWalletStatus, type PaymentMethodUnavailableReason, type UpdatePaymentMethodConfigurationParams, type UpdatePaymentMethodDomainParams, } from './paymentMethodConfiguration';
13
15
  export type { CapabilityStatus, ConnectedAccount, ConnectedAccountFaircoin, ConnectedAccountFaircoinStatus, ConnectedAccountRail, Refund, RefundOrigin, RefundStatus, Settlement, Transfer, TransferReversal, TransferReversalStatus, TransferStatus, TransferWithReversal, } from './settlement';
14
16
  export type { WebhookDelivery, WebhookDeliveryStatus, } from './webhookDelivery';
15
17
  export { REQUEST_LOG_METHODS, REQUEST_LOG_RETENTION_DAYS, type RequestLog, type RequestLogListParams, type RequestLogMethod, type RequestLogStatusFilter, } from './requestLog';
@@ -35,6 +35,13 @@ export interface Merchant {
35
35
  * Absent again once Oxy reports the file deleted or taken down.
36
36
  */
37
37
  avatar_file_id?: string;
38
+ /**
39
+ * The wide wordmark payer surfaces show in their header: a `file_…` of
40
+ * purpose `business_logo`. Beside the avatar, never instead of it — the avatar
41
+ * is the square identity image (Stripe's branding `icon`), the logo is the
42
+ * wordmark (Stripe's branding `logo`). Absent when unset.
43
+ */
44
+ business_logo?: string;
38
45
  description?: string;
39
46
  created: number;
40
47
  updated_at: number;
@@ -3,5 +3,7 @@ export interface MerchantDisplay {
3
3
  name: string;
4
4
  /** Resolved server-side via the canonical media chokepoint; `null` when the merchant has no avatar. */
5
5
  avatar_url: string | null;
6
+ /** The merchant's `business_logo`, resolved; `null` when unset or not being served. */
7
+ logo_url: string | null;
6
8
  description: string | null;
7
9
  }
@@ -1,5 +1,9 @@
1
1
  /**
2
- * The currencies this gateway can denominate a payment in.
2
+ * The currencies this gateway can denominate a payment in: FairCoin on the
3
+ * chain rail, and on the card rail the fiat set the provider settles for the
4
+ * payment methods Orbit offers (cards, wallets, SEPA Debit, iDEAL, Bancontact,
5
+ * EPS). A local method narrows it further per payment
6
+ * (`PAYMENT_METHOD_CURRENCIES` in `paymentMethodConfiguration.ts`).
3
7
  *
4
8
  * A closed set, and the database mirrors it as a CHECK
5
9
  * (`db/schema/valueSets.ts`). ADR 0001 D4: the scale of an amount is a property
@@ -7,7 +11,7 @@
7
11
  * {@link CURRENCY_DECIMALS} exists and why {@link UNITS_PER_COIN} must never be
8
12
  * used to interpret a fiat amount.
9
13
  */
10
- export declare const CURRENCY_CODES: readonly ["FAIR", "EUR", "USD"];
14
+ export declare const CURRENCY_CODES: readonly ["FAIR", "EUR", "USD", "GBP", "CHF", "SEK", "NOK", "DKK", "PLN", "CZK", "CAD", "AUD", "JPY"];
11
15
  export type CurrencyCode = (typeof CURRENCY_CODES)[number];
12
16
  /** Whether an arbitrary string is a currency this gateway knows. */
13
17
  export declare function isCurrencyCode(value: string): value is CurrencyCode;
@@ -15,11 +19,15 @@ export declare function isCurrencyCode(value: string): value is CurrencyCode;
15
19
  * How many decimal places each currency's smallest unit represents — i.e. an
16
20
  * amount of `1` means `10^-decimals` of one whole unit.
17
21
  *
18
- * FairCoin's 8 is a consensus constant and is pinned against
22
+ * ISO 4217's minor units for every fiat member (JPY has none). FairCoin's 8 is a consensus constant and is pinned against
19
23
  * {@link UNITS_PER_COIN} by this package's own tests, so the two spellings of
20
24
  * one fact cannot drift.
21
25
  */
22
26
  export declare const CURRENCY_DECIMALS: Readonly<Record<CurrencyCode, number>>;
27
+ /** A currency the card rail can denominate a payment in. */
28
+ export type FiatCurrencyCode = Exclude<CurrencyCode, 'FAIR'>;
29
+ /** The fiat members — every currency the card rail can denominate a payment in. */
30
+ export declare const FIAT_CURRENCY_CODES: readonly FiatCurrencyCode[];
23
31
  /** The decimal places of `currency`'s smallest unit. */
24
32
  export declare function decimalsFor(currency: CurrencyCode): number;
25
33
  export declare const UNITS_PER_COIN = 100000000n;
@@ -1,3 +1,4 @@
1
+ import type { CardRailPaymentMethodType } from './paymentMethodConfiguration';
1
2
  import type { Charge } from './charge';
2
3
  import type { Customer } from './customer';
3
4
  import type { CurrencyCode } from './money';
@@ -85,8 +86,12 @@ export declare const PAYMENT_INTENT_CAPTURE_METHODS: readonly PaymentIntentCaptu
85
86
  /** Card only: keep the payment method on the customer for later (ADR 0016). */
86
87
  export type PaymentIntentSetupFutureUsage = 'on_session' | 'off_session';
87
88
  export declare const PAYMENT_INTENT_SETUP_FUTURE_USAGES: readonly PaymentIntentSetupFutureUsage[];
88
- /** The payment method types a rail accepts. One per rail today. */
89
- export type PaymentMethodType = 'card' | 'faircoin';
89
+ /**
90
+ * The payment method types a PaymentIntent can carry: `faircoin` on the chain
91
+ * rail, and on the card rail the card and the local methods the provider
92
+ * collects in its hosted fields. Wallets (Apple Pay, Google Pay) are `card`.
93
+ */
94
+ export type PaymentMethodType = 'faircoin' | CardRailPaymentMethodType;
90
95
  /** Why the last attempt failed. Peable's words, never the provider's raw prose. */
91
96
  export interface PaymentIntentLastPaymentError {
92
97
  /** `card_error` for a declined card; `payment_error` for a FairCoin underpayment. */
@@ -176,7 +181,16 @@ export interface PaymentIntent {
176
181
  status: PaymentIntentStatus;
177
182
  /** Which rail moves this payment. Decides which fields below are populated. */
178
183
  rail: PaymentIntentRail;
184
+ /**
185
+ * What the payer may pay with: `['faircoin']` on the chain; on the card rail
186
+ * the types named at creation, or those the configuration offers in this
187
+ * currency.
188
+ */
179
189
  payment_method_types: PaymentMethodType[];
190
+ /** The `pmc_…` whose methods this card payment offers; `null` on the chain and when types were named. */
191
+ payment_method_configuration_details: {
192
+ id: string;
193
+ } | null;
180
194
  /**
181
195
  * Amount in the currency's SMALLEST unit as a canonical integer string.
182
196
  * Never a float, and never interpretable without `currency`: m⊜ (10^-8 FAIR)
@@ -236,8 +250,13 @@ export interface CreatePaymentIntentParams {
236
250
  network?: NetworkType;
237
251
  /** Defaults to `'FAIR'` on the faircoin rail; required on the card rail. */
238
252
  currency?: CurrencyCode;
239
- /** Must name the rail's own type: `['card']` or `['faircoin']`. */
253
+ /**
254
+ * `['faircoin']` on the chain rail. On the card rail, any of the card-rail
255
+ * types the provider offers in `currency` — instead of a configuration.
256
+ */
240
257
  payment_method_types?: PaymentMethodType[];
258
+ /** Card only: the `pmc_…` whose methods to offer. Defaults to the merchant's default configuration. */
259
+ payment_method_configuration?: string;
241
260
  /** `cus_…`. */
242
261
  customer?: string;
243
262
  /** `pm_…`, card only. A saved payment method of `customer`. */
@@ -0,0 +1,116 @@
1
+ import { type FiatCurrencyCode } from './money';
2
+ /**
3
+ * The methods a configuration can turn on.
4
+ *
5
+ * `apple_pay` and `google_pay` are WALLETS over a card: a payment made with one
6
+ * is a `card` payment, so they appear here and never in a PaymentIntent's
7
+ * `payment_method_types`. `bizum` is listed because merchants ask for it and the
8
+ * honest answer is a reason, not an absence: the provider does not offer it.
9
+ *
10
+ * Deliberately absent: the provider's own consumer wallet (its brand on Orbit's
11
+ * checkout, `payment-provider-boundary.md`) and giropay (discontinued by the
12
+ * scheme).
13
+ */
14
+ export declare const PAYMENT_METHOD_CONFIGURATION_TYPES: readonly ["card", "apple_pay", "google_pay", "sepa_debit", "ideal", "bancontact", "eps", "bizum"];
15
+ export type PaymentMethodConfigurationType = (typeof PAYMENT_METHOD_CONFIGURATION_TYPES)[number];
16
+ /** The card-rail types a PaymentIntent can carry in `payment_method_types`. */
17
+ export declare const CARD_RAIL_PAYMENT_METHOD_TYPES: readonly ["card", "sepa_debit", "ideal", "bancontact", "eps"];
18
+ export type CardRailPaymentMethodType = (typeof CARD_RAIL_PAYMENT_METHOD_TYPES)[number];
19
+ /**
20
+ * Why a method cannot be offered whatever the merchant chooses.
21
+ *
22
+ * - `provider_unsupported` — the processing provider does not offer it;
23
+ * - `requires_card` — a wallet rides on card payments, and card is off.
24
+ */
25
+ export type PaymentMethodUnavailableReason = 'provider_unsupported' | 'requires_card';
26
+ /**
27
+ * The currencies each method can take a payment in. A method is offered on a
28
+ * payment only when its currency is listed — so iDEAL never appears on a USD
29
+ * payment, and a payer is never shown a method that would fail.
30
+ */
31
+ export declare const PAYMENT_METHOD_CURRENCIES: Readonly<Record<PaymentMethodConfigurationType, readonly FiatCurrencyCode[]>>;
32
+ /** The merchant's choice (`none`: Orbit's default) and what is in effect. */
33
+ export interface PaymentMethodDisplayPreference {
34
+ preference: 'on' | 'off' | 'none';
35
+ value: 'on' | 'off';
36
+ }
37
+ export interface PaymentMethodConfigurationEntry {
38
+ /** Whether payers can be offered this method: it is on and nothing prevents it. */
39
+ available: boolean;
40
+ display_preference: PaymentMethodDisplayPreference;
41
+ /** Why it cannot be offered whatever the merchant chooses; `null` when it can. */
42
+ unavailable_reason: PaymentMethodUnavailableReason | null;
43
+ /** The payment currencies it is offered on. */
44
+ currencies: readonly FiatCurrencyCode[];
45
+ }
46
+ export type PaymentMethodConfiguration = {
47
+ /** `pmc_…`. */
48
+ id: string;
49
+ object: 'payment_method_configuration';
50
+ livemode: boolean;
51
+ name: string;
52
+ /** An inactive configuration cannot be named on a new PaymentIntent. The default is always active. */
53
+ active: boolean;
54
+ /** The configuration a PaymentIntent uses when it names none. Exactly one per merchant. */
55
+ is_default: boolean;
56
+ created: number;
57
+ updated_at: number;
58
+ } & {
59
+ [K in PaymentMethodConfigurationType]: PaymentMethodConfigurationEntry;
60
+ };
61
+ /** One method's setting in a create or update. */
62
+ export interface PaymentMethodConfigurationEntryParams {
63
+ display_preference: {
64
+ preference: 'on' | 'off' | 'none';
65
+ };
66
+ }
67
+ /** `POST /v1/payment_method_configurations`. */
68
+ export type CreatePaymentMethodConfigurationParams = {
69
+ name?: string;
70
+ } & {
71
+ [K in PaymentMethodConfigurationType]?: PaymentMethodConfigurationEntryParams;
72
+ };
73
+ /** `POST /v1/payment_method_configurations/:id`. */
74
+ export type UpdatePaymentMethodConfigurationParams = CreatePaymentMethodConfigurationParams & {
75
+ active?: boolean;
76
+ };
77
+ /** A wallet's standing on one domain. */
78
+ export interface PaymentMethodDomainWalletStatus {
79
+ /** `active`: the wallet can run on pages served from this domain. */
80
+ status: 'active' | 'inactive';
81
+ /** Present while `inactive`: what the wallet's verification said, in Peable's words. */
82
+ status_details: {
83
+ error_message: string;
84
+ } | null;
85
+ }
86
+ /**
87
+ * A web domain the merchant serves payment pages from (`pmd_…`), registered so
88
+ * Apple Pay and Google Pay run there. Peable's own hosted checkout needs none:
89
+ * its domain is Peable's.
90
+ */
91
+ export interface PaymentMethodDomain {
92
+ id: string;
93
+ object: 'payment_method_domain';
94
+ livemode: boolean;
95
+ /** Lowercase hostname, no scheme, port or path. */
96
+ domain_name: string;
97
+ /** Whether wallets may run on it at all. */
98
+ enabled: boolean;
99
+ apple_pay: PaymentMethodDomainWalletStatus;
100
+ google_pay: PaymentMethodDomainWalletStatus;
101
+ created: number;
102
+ }
103
+ /** `POST /v1/payment_method_domains`. */
104
+ export interface CreatePaymentMethodDomainParams {
105
+ domain_name: string;
106
+ enabled?: boolean;
107
+ }
108
+ /** `POST /v1/payment_method_domains/:id`. */
109
+ export interface UpdatePaymentMethodDomainParams {
110
+ enabled?: boolean;
111
+ }
112
+ /** `GET /v1/payment_method_domains`. */
113
+ export interface ListPaymentMethodDomainsParams {
114
+ domain_name?: string;
115
+ enabled?: boolean;
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peable.to/shared-types",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Shared TypeScript types for the Peable Gateway — the wire contract shared by the backend, SDK, and frontend.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",