@owlmeans/payment 0.1.18-rc.8 → 0.1.18
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.
- package/README.md +95 -57
- package/agent-meta/manifest.json +10 -3
- package/agent-meta/skills/entitlements/SKILL.md +159 -0
- package/agent-meta/skills/payment/SKILL.md +491 -28
- package/build/advertise.d.ts +2 -0
- package/build/advertise.d.ts.map +1 -0
- package/build/advertise.js +10 -0
- package/build/advertise.js.map +1 -0
- package/build/checkout/consts.d.ts +2 -0
- package/build/checkout/consts.d.ts.map +1 -0
- package/build/checkout/consts.js +2 -0
- package/build/checkout/consts.js.map +1 -0
- package/build/checkout/index.d.ts +6 -0
- package/build/checkout/index.d.ts.map +1 -0
- package/build/checkout/index.js +4 -0
- package/build/checkout/index.js.map +1 -0
- package/build/checkout/narrow/types.d.ts +25 -0
- package/build/checkout/narrow/types.d.ts.map +1 -0
- package/build/checkout/narrow/types.js +2 -0
- package/build/checkout/narrow/types.js.map +1 -0
- package/build/checkout/narrow.d.ts +8 -0
- package/build/checkout/narrow.d.ts.map +1 -0
- package/build/checkout/narrow.js +47 -0
- package/build/checkout/narrow.js.map +1 -0
- package/build/checkout/protocols.d.ts +8 -0
- package/build/checkout/protocols.d.ts.map +1 -0
- package/build/checkout/protocols.js +28 -0
- package/build/checkout/protocols.js.map +1 -0
- package/build/checkout/types.d.ts +29 -0
- package/build/checkout/types.d.ts.map +1 -0
- package/build/checkout/types.js +2 -0
- package/build/checkout/types.js.map +1 -0
- package/build/consts.d.ts +242 -25
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +377 -24
- package/build/consts.js.map +1 -1
- package/build/consts.local.d.ts +20 -0
- package/build/consts.local.d.ts.map +1 -0
- package/build/consts.local.js +23 -0
- package/build/consts.local.js.map +1 -0
- package/build/consumer/cancel.d.ts +11 -0
- package/build/consumer/cancel.d.ts.map +1 -0
- package/build/consumer/cancel.js +33 -0
- package/build/consumer/cancel.js.map +1 -0
- package/build/consumer/consts.d.ts +20 -0
- package/build/consumer/consts.d.ts.map +1 -0
- package/build/consumer/consts.js +39 -0
- package/build/consumer/consts.js.map +1 -0
- package/build/consumer/consts.local.d.ts +7 -0
- package/build/consumer/consts.local.d.ts.map +1 -0
- package/build/consumer/consts.local.js +13 -0
- package/build/consumer/consts.local.js.map +1 -0
- package/build/consumer/copy/types.d.ts +56 -0
- package/build/consumer/copy/types.d.ts.map +1 -0
- package/build/consumer/copy/types.js +2 -0
- package/build/consumer/copy/types.js.map +1 -0
- package/build/consumer/copy.d.ts +22 -0
- package/build/consumer/copy.d.ts.map +1 -0
- package/build/consumer/copy.js +87 -0
- package/build/consumer/copy.js.map +1 -0
- package/build/consumer/deadline/types.d.ts +30 -0
- package/build/consumer/deadline/types.d.ts.map +1 -0
- package/build/consumer/deadline/types.js +2 -0
- package/build/consumer/deadline/types.js.map +1 -0
- package/build/consumer/deadline.d.ts +12 -0
- package/build/consumer/deadline.d.ts.map +1 -0
- package/build/consumer/deadline.js +44 -0
- package/build/consumer/deadline.js.map +1 -0
- package/build/consumer/fifo/types.d.ts +17 -0
- package/build/consumer/fifo/types.d.ts.map +1 -0
- package/build/consumer/fifo/types.js +2 -0
- package/build/consumer/fifo/types.js.map +1 -0
- package/build/consumer/fifo.d.ts +4 -0
- package/build/consumer/fifo.d.ts.map +1 -0
- package/build/consumer/fifo.js +34 -0
- package/build/consumer/fifo.js.map +1 -0
- package/build/consumer/index.d.ts +18 -0
- package/build/consumer/index.d.ts.map +1 -0
- package/build/consumer/index.js +11 -0
- package/build/consumer/index.js.map +1 -0
- package/build/consumer/policy/types.d.ts +20 -0
- package/build/consumer/policy/types.d.ts.map +1 -0
- package/build/consumer/policy/types.js +2 -0
- package/build/consumer/policy/types.js.map +1 -0
- package/build/consumer/policy.d.ts +17 -0
- package/build/consumer/policy.d.ts.map +1 -0
- package/build/consumer/policy.js +91 -0
- package/build/consumer/policy.js.map +1 -0
- package/build/consumer/protocols.d.ts +20 -0
- package/build/consumer/protocols.d.ts.map +1 -0
- package/build/consumer/protocols.js +70 -0
- package/build/consumer/protocols.js.map +1 -0
- package/build/consumer/refund/types.d.ts +27 -0
- package/build/consumer/refund/types.d.ts.map +1 -0
- package/build/consumer/refund/types.js +2 -0
- package/build/consumer/refund/types.js.map +1 -0
- package/build/consumer/refund.d.ts +9 -0
- package/build/consumer/refund.d.ts.map +1 -0
- package/build/consumer/refund.js +112 -0
- package/build/consumer/refund.js.map +1 -0
- package/build/consumer/refusal.d.ts +10 -0
- package/build/consumer/refusal.d.ts.map +1 -0
- package/build/consumer/refusal.js +35 -0
- package/build/consumer/refusal.js.map +1 -0
- package/build/consumer/revive/types.d.ts +19 -0
- package/build/consumer/revive/types.d.ts.map +1 -0
- package/build/consumer/revive/types.js +2 -0
- package/build/consumer/revive/types.js.map +1 -0
- package/build/consumer/revive.d.ts +13 -0
- package/build/consumer/revive.d.ts.map +1 -0
- package/build/consumer/revive.js +43 -0
- package/build/consumer/revive.js.map +1 -0
- package/build/consumer/types.d.ts +196 -0
- package/build/consumer/types.d.ts.map +1 -0
- package/build/consumer/types.js +2 -0
- package/build/consumer/types.js.map +1 -0
- package/build/consumer/types.local.d.ts +11 -0
- package/build/consumer/types.local.d.ts.map +1 -0
- package/build/consumer/types.local.js +2 -0
- package/build/consumer/types.local.js.map +1 -0
- package/build/countries.d.ts +3 -0
- package/build/countries.d.ts.map +1 -0
- package/build/countries.js +4 -0
- package/build/countries.js.map +1 -0
- package/build/entitled.d.ts +14 -0
- package/build/entitled.d.ts.map +1 -0
- package/build/entitled.js +17 -0
- package/build/entitled.js.map +1 -0
- package/build/entitlement/types.d.ts +29 -0
- package/build/entitlement/types.d.ts.map +1 -0
- package/build/entitlement/types.js +2 -0
- package/build/entitlement/types.js.map +1 -0
- package/build/entitlement.d.ts +12 -0
- package/build/entitlement.d.ts.map +1 -0
- package/build/entitlement.js +64 -0
- package/build/entitlement.js.map +1 -0
- package/build/errors.d.ts +189 -0
- package/build/errors.d.ts.map +1 -1
- package/build/errors.js +361 -0
- package/build/errors.js.map +1 -1
- package/build/estimate/types.d.ts +34 -0
- package/build/estimate/types.d.ts.map +1 -0
- package/build/estimate/types.js +2 -0
- package/build/estimate/types.js.map +1 -0
- package/build/estimate.d.ts +11 -0
- package/build/estimate.d.ts.map +1 -0
- package/build/estimate.js +76 -0
- package/build/estimate.js.map +1 -0
- package/build/i18n/consumer-rights/be.json +131 -0
- package/build/i18n/consumer-rights/de.json +131 -0
- package/build/i18n/consumer-rights/en.json +131 -0
- package/build/i18n/consumer-rights/es.json +131 -0
- package/build/i18n/consumer-rights/fr.json +131 -0
- package/build/i18n/consumer-rights/pl.json +131 -0
- package/build/i18n/consumer-rights/ru.json +131 -0
- package/build/i18n/consumer-rights/uk.json +131 -0
- package/build/i18n/errors/be.json +30 -0
- package/build/i18n/errors/de.json +30 -0
- package/build/i18n/errors/en.json +30 -0
- package/build/i18n/errors/es.json +30 -0
- package/build/i18n/errors/fr.json +30 -0
- package/build/i18n/errors/pl.json +30 -0
- package/build/i18n/errors/ru.json +30 -0
- package/build/i18n/errors/uk.json +30 -0
- package/build/i18n/fr.json +39 -0
- package/build/i18n.js +53 -8
- package/build/i18n.js.map +1 -1
- package/build/index.d.ts +19 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +12 -1
- package/build/index.js.map +1 -1
- package/build/limit/types.d.ts +44 -0
- package/build/limit/types.d.ts.map +1 -0
- package/build/limit/types.js +2 -0
- package/build/limit/types.js.map +1 -0
- package/build/limit.d.ts +23 -0
- package/build/limit.d.ts.map +1 -0
- package/build/limit.js +97 -0
- package/build/limit.js.map +1 -0
- package/build/model/checkout.d.ts +2 -3
- package/build/model/checkout.d.ts.map +1 -1
- package/build/model/checkout.js +12 -7
- package/build/model/checkout.js.map +1 -1
- package/build/model/consts.d.ts +15 -0
- package/build/model/consts.d.ts.map +1 -0
- package/build/model/consts.js +193 -0
- package/build/model/consts.js.map +1 -0
- package/build/model/consts.local.d.ts +35 -0
- package/build/model/consts.local.d.ts.map +1 -0
- package/build/model/consts.local.js +65 -0
- package/build/model/consts.local.js.map +1 -0
- package/build/model/consumer.d.ts +23 -0
- package/build/model/consumer.d.ts.map +1 -0
- package/build/model/consumer.js +272 -0
- package/build/model/consumer.js.map +1 -0
- package/build/model/estimate.d.ts +5 -0
- package/build/model/estimate.d.ts.map +1 -0
- package/build/model/estimate.js +69 -0
- package/build/model/estimate.js.map +1 -0
- package/build/model/index.d.ts +7 -2
- package/build/model/index.d.ts.map +1 -1
- package/build/model/index.js +7 -2
- package/build/model/index.js.map +1 -1
- package/build/model/limit.d.ts +7 -0
- package/build/model/limit.d.ts.map +1 -0
- package/build/model/limit.js +32 -0
- package/build/model/limit.js.map +1 -0
- package/build/model/plan.d.ts.map +1 -1
- package/build/model/plan.js +24 -11
- package/build/model/plan.js.map +1 -1
- package/build/model/portal.d.ts +4 -0
- package/build/model/portal.d.ts.map +1 -0
- package/build/model/portal.js +22 -0
- package/build/model/portal.js.map +1 -0
- package/build/model/pricing.d.ts +4 -0
- package/build/model/pricing.d.ts.map +1 -0
- package/build/model/pricing.js +31 -0
- package/build/model/pricing.js.map +1 -0
- package/build/model/view.d.ts +3 -0
- package/build/model/view.d.ts.map +1 -0
- package/build/model/view.js +15 -0
- package/build/model/view.js.map +1 -0
- package/build/pricing/types.d.ts +13 -0
- package/build/pricing/types.d.ts.map +1 -0
- package/build/pricing/types.js +2 -0
- package/build/pricing/types.js.map +1 -0
- package/build/pricing.d.ts +13 -0
- package/build/pricing.d.ts.map +1 -0
- package/build/pricing.js +74 -0
- package/build/pricing.js.map +1 -0
- package/build/promo/types.d.ts +14 -0
- package/build/promo/types.d.ts.map +1 -0
- package/build/promo/types.js +2 -0
- package/build/promo/types.js.map +1 -0
- package/build/promo.d.ts +7 -0
- package/build/promo.d.ts.map +1 -0
- package/build/promo.js +25 -0
- package/build/promo.js.map +1 -0
- package/build/regions/types.d.ts +32 -0
- package/build/regions/types.d.ts.map +1 -0
- package/build/regions/types.js +2 -0
- package/build/regions/types.js.map +1 -0
- package/build/regions.d.ts +15 -0
- package/build/regions.d.ts.map +1 -0
- package/build/regions.js +43 -0
- package/build/regions.js.map +1 -0
- package/build/service.d.ts.map +1 -1
- package/build/service.js +24 -9
- package/build/service.js.map +1 -1
- package/build/types.d.ts +619 -52
- package/build/types.d.ts.map +1 -1
- package/build/types.local.d.ts +4 -0
- package/build/types.local.d.ts.map +1 -0
- package/build/types.local.js +2 -0
- package/build/types.local.js.map +1 -0
- package/build/view/types.d.ts +33 -0
- package/build/view/types.d.ts.map +1 -0
- package/build/view/types.js +2 -0
- package/build/view/types.js.map +1 -0
- package/build/view.d.ts +13 -0
- package/build/view.d.ts.map +1 -0
- package/build/view.js +78 -0
- package/build/view.js.map +1 -0
- package/package.json +14 -12
- package/src/advertise.ts +13 -0
- package/src/checkout/consts.ts +1 -0
- package/src/checkout/index.ts +5 -0
- package/src/checkout/narrow/types.ts +27 -0
- package/src/checkout/narrow.ts +63 -0
- package/src/checkout/protocols.ts +39 -0
- package/src/checkout/types.ts +27 -0
- package/src/consts.local.ts +34 -0
- package/src/consts.ts +417 -24
- package/src/consumer/cancel.ts +40 -0
- package/src/consumer/consts.local.ts +16 -0
- package/src/consumer/consts.ts +46 -0
- package/src/consumer/copy/types.ts +54 -0
- package/src/consumer/copy.ts +121 -0
- package/src/consumer/deadline/types.ts +30 -0
- package/src/consumer/deadline.ts +61 -0
- package/src/consumer/fifo/types.ts +17 -0
- package/src/consumer/fifo.ts +42 -0
- package/src/consumer/index.ts +17 -0
- package/src/consumer/policy/types.ts +20 -0
- package/src/consumer/policy.ts +101 -0
- package/src/consumer/protocols.ts +116 -0
- package/src/consumer/refund/types.ts +27 -0
- package/src/consumer/refund.ts +136 -0
- package/src/consumer/refusal.ts +35 -0
- package/src/consumer/revive/types.ts +23 -0
- package/src/consumer/revive.ts +82 -0
- package/src/consumer/types.local.ts +10 -0
- package/src/consumer/types.ts +189 -0
- package/src/countries.ts +5 -0
- package/src/entitled.ts +20 -0
- package/src/entitlement/types.ts +29 -0
- package/src/entitlement.ts +86 -0
- package/src/errors.ts +445 -0
- package/src/estimate/types.ts +34 -0
- package/src/estimate.ts +90 -0
- package/src/i18n/consumer-rights/be.json +131 -0
- package/src/i18n/consumer-rights/de.json +131 -0
- package/src/i18n/consumer-rights/en.json +131 -0
- package/src/i18n/consumer-rights/es.json +131 -0
- package/src/i18n/consumer-rights/fr.json +131 -0
- package/src/i18n/consumer-rights/pl.json +131 -0
- package/src/i18n/consumer-rights/ru.json +131 -0
- package/src/i18n/consumer-rights/uk.json +131 -0
- package/src/i18n/errors/be.json +30 -0
- package/src/i18n/errors/de.json +30 -0
- package/src/i18n/errors/en.json +30 -0
- package/src/i18n/errors/es.json +30 -0
- package/src/i18n/errors/fr.json +30 -0
- package/src/i18n/errors/pl.json +30 -0
- package/src/i18n/errors/ru.json +30 -0
- package/src/i18n/errors/uk.json +30 -0
- package/src/i18n/fr.json +39 -0
- package/src/i18n.ts +58 -8
- package/src/index.ts +19 -2
- package/src/limit/types.ts +41 -0
- package/src/limit.ts +132 -0
- package/src/model/checkout.ts +12 -7
- package/src/model/consts.local.ts +83 -0
- package/src/model/consts.ts +206 -0
- package/src/model/consumer.ts +294 -0
- package/src/model/estimate.ts +73 -0
- package/src/model/index.ts +7 -2
- package/src/model/limit.ts +36 -0
- package/src/model/plan.ts +25 -12
- package/src/model/portal.ts +25 -0
- package/src/model/pricing.ts +34 -0
- package/src/model/view.ts +17 -0
- package/src/pricing/types.ts +13 -0
- package/src/pricing.ts +110 -0
- package/src/promo/types.ts +18 -0
- package/src/promo.ts +46 -0
- package/src/regions/types.ts +42 -0
- package/src/regions.ts +85 -0
- package/src/service.ts +32 -15
- package/src/types.local.ts +5 -0
- package/src/types.ts +669 -49
- package/src/view/types.ts +39 -0
- package/src/view.ts +119 -0
- package/tests/checkout-narrow.spec.ts +75 -0
- package/tests/consumer-copy.spec.ts +378 -0
- package/tests/consumer-deadline.spec.ts +85 -0
- package/tests/consumer-fifo.spec.ts +61 -0
- package/tests/consumer-protocols.spec.ts +103 -0
- package/tests/consumer-refund.spec.ts +134 -0
- package/tests/consumer-regions.spec.ts +140 -0
- package/tests/consumer-wire.spec.ts +170 -0
- package/tests/contract.spec.ts +183 -0
- package/tests/entitlement.spec.ts +103 -0
- package/tests/errors.spec.ts +168 -0
- package/tests/estimate.spec.ts +139 -0
- package/tests/limit.spec.ts +117 -0
- package/tests/pricing.spec.ts +30 -0
- package/tests/tsconfig.json +11 -0
- package/tests/view.spec.ts +130 -0
- package/tsconfig.json +6 -1
- package/build/.gitkeep +0 -0
- package/src/model/subscription.ts +0 -63
- package/src/model/utils.ts +0 -32
- package/src/modules.ts +0 -43
package/src/consts.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { JSONSchemaType } from 'ajv'
|
|
2
|
+
import type { PricingPolicy } from './types.js'
|
|
2
3
|
|
|
3
4
|
export enum ProductType {
|
|
4
5
|
Simple = 'simple',
|
|
@@ -6,6 +7,11 @@ export enum ProductType {
|
|
|
6
7
|
Consumable = 'consumable'
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
export enum CheckoutPricingMode {
|
|
11
|
+
Quantity = 'quantity',
|
|
12
|
+
Amount = 'amount',
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
export enum PaymentEntityType {
|
|
10
16
|
Product = 'product',
|
|
11
17
|
Plan = 'plan',
|
|
@@ -38,12 +44,212 @@ export enum SubscriptionStatus {
|
|
|
38
44
|
Trial = 'trial',
|
|
39
45
|
Canceled = 'canceled',
|
|
40
46
|
Expired = 'expired',
|
|
47
|
+
/** Revoked until resumed: unpaid, paused, or collection paused. */
|
|
41
48
|
Suspended = 'suspended',
|
|
42
49
|
Blocked = 'blocked',
|
|
43
50
|
Ended = 'ended',
|
|
44
|
-
Free = 'free',
|
|
45
51
|
Active = 'active',
|
|
46
|
-
|
|
52
|
+
/** Payment failed and is being retried: still entitled, flagged. */
|
|
53
|
+
PastDue = 'past-due',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The statuses that grant a plan's capabilities and limits. */
|
|
57
|
+
export const ENTITLING_STATUSES: readonly SubscriptionStatus[] = Object.freeze([
|
|
58
|
+
SubscriptionStatus.Active, SubscriptionStatus.Trial, SubscriptionStatus.PastDue,
|
|
59
|
+
])
|
|
60
|
+
|
|
61
|
+
/** The statuses a subscription never leaves on its own. */
|
|
62
|
+
export const TERMINAL_STATUSES: readonly SubscriptionStatus[] = Object.freeze([
|
|
63
|
+
SubscriptionStatus.Canceled, SubscriptionStatus.Expired, SubscriptionStatus.Ended,
|
|
64
|
+
SubscriptionStatus.Blocked,
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
/** How a limit's counter renews. */
|
|
68
|
+
export enum LimitKind {
|
|
69
|
+
/** Renews on the calendar UTC or subscription-anchored `LimitWindow`. */
|
|
70
|
+
Window = 'window',
|
|
71
|
+
/** Never renews: the counter belongs to the entity and survives plan changes. */
|
|
72
|
+
Lifetime = 'lifetime',
|
|
73
|
+
/** A held count (`+1` on acquire, `-1` on release), reconciled against reality. */
|
|
74
|
+
Occupancy = 'occupancy',
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Calendar UTC windows, or a seven-day window anchored at subscription creation. */
|
|
78
|
+
export enum LimitWindow {
|
|
79
|
+
Day = 'day',
|
|
80
|
+
Month = 'month',
|
|
81
|
+
SubscriptionWeek = 'subscription-week',
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const SUBSCRIPTION_WEEK_MS = 7 * 24 * 60 * 60 * 1000
|
|
85
|
+
export const SUBSCRIPTION_WEEK_PREFIX = 'subscription-week:'
|
|
86
|
+
|
|
87
|
+
/** Which paygate portal flow a portal link opens. */
|
|
88
|
+
export enum PortalFlow {
|
|
89
|
+
Manage = 'manage',
|
|
90
|
+
Cancel = 'cancel',
|
|
91
|
+
Update = 'update',
|
|
92
|
+
Change = 'change',
|
|
93
|
+
PaymentMethod = 'payment-method',
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Whether a price's amount includes tax, or tax is added on top — never inferred, always declared. */
|
|
97
|
+
export enum TaxBehavior {
|
|
98
|
+
Exclusive = 'exclusive',
|
|
99
|
+
Inclusive = 'inclusive',
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** What a price estimate says about tax at one billing country. */
|
|
103
|
+
export enum TaxEstimateStatus {
|
|
104
|
+
/** Tax is due and its rate(s) are in `TaxEstimate.rates`. */
|
|
105
|
+
Taxed = 'taxed',
|
|
106
|
+
/** No tax because the buyer's tax id shifts liability to them (EU/GB reverse charge). */
|
|
107
|
+
ReverseCharge = 'reverse-charge',
|
|
108
|
+
/** No tax for any other reason (not registered there, exempt, zero-rated). */
|
|
109
|
+
None = 'none',
|
|
110
|
+
/** The gateway could not resolve a rate from what it was given; the real total shows at checkout. */
|
|
111
|
+
AtCheckout = 'at-checkout',
|
|
112
|
+
/** No country was given and none could be inferred from the entity's paygate customer. */
|
|
113
|
+
LocationRequired = 'location-required',
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** A tax type a rate carries, collapsed from Stripe's finer `tax_type` for display. */
|
|
117
|
+
export enum TaxType {
|
|
118
|
+
Vat = 'vat',
|
|
119
|
+
Gst = 'gst',
|
|
120
|
+
SalesTax = 'sales-tax',
|
|
121
|
+
/** Any other Stripe `tax_type` (excise, lease, tourism, …). */
|
|
122
|
+
Tax = 'tax',
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** The paygate alias of subscriptions the application grants itself (a free plan, a comp). */
|
|
126
|
+
export const INTERNAL_PAYGATE = 'internal'
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Where a billing country sits for consumer rights and the charge currency: inside the declared
|
|
130
|
+
* consumer-rights territories (`ConsumerRightsPolicy.countries`) or outside them.
|
|
131
|
+
*/
|
|
132
|
+
export enum ConsumerRegion {
|
|
133
|
+
Eu = 'eu',
|
|
134
|
+
Other = 'other',
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** What a purchase — a window of the right of withdrawal — was. */
|
|
138
|
+
export enum PurchaseKind {
|
|
139
|
+
/** A paid one-time checkout of an amount or quantity plan (prepaid credits). */
|
|
140
|
+
TopUp = 'top-up',
|
|
141
|
+
/** The FIRST invoice of a subscription. Renewals are not purchases. */
|
|
142
|
+
Subscription = 'subscription',
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** What a consumer expressly requested. */
|
|
146
|
+
export enum ConsentKind {
|
|
147
|
+
/** Start performing now, before the withdrawal period of a credit purchase ends. */
|
|
148
|
+
Performance = 'performance',
|
|
149
|
+
/** Start a subscription's services now — given before its checkout. */
|
|
150
|
+
SubscriptionStart = 'subscription-start',
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** What a consumer declared through a statutory function. */
|
|
154
|
+
export enum DeclarationKind {
|
|
155
|
+
Withdrawal = 'withdrawal',
|
|
156
|
+
Cancellation = 'cancellation',
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Where a declaration was made: signed in, or through the public page without a login. */
|
|
160
|
+
export enum DeclarationChannel {
|
|
161
|
+
InApp = 'in-app',
|
|
162
|
+
Public = 'public',
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export enum CancellationKind {
|
|
166
|
+
Ordinary = 'ordinary',
|
|
167
|
+
/** For cause; carries a reason. */
|
|
168
|
+
Extraordinary = 'extraordinary',
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export enum WithdrawalStatus {
|
|
172
|
+
/** Recorded; nothing is disclosed yet (a public declaration always answers this). */
|
|
173
|
+
Received = 'received',
|
|
174
|
+
Processing = 'processing',
|
|
175
|
+
Refunded = 'refunded',
|
|
176
|
+
/** Recorded, and left to an operator: no usage meter, or automatic refunds are off. */
|
|
177
|
+
Review = 'review',
|
|
178
|
+
Failed = 'failed',
|
|
179
|
+
/** Received after the withdrawal period ended; recorded, not executed. */
|
|
180
|
+
Expired = 'expired',
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export enum CancellationStatus {
|
|
184
|
+
Received = 'received',
|
|
185
|
+
Scheduled = 'scheduled',
|
|
186
|
+
AlreadyScheduled = 'already-scheduled',
|
|
187
|
+
/** Recorded and left to an operator — an extraordinary cancellation (for cause). */
|
|
188
|
+
Review = 'review',
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export enum WithdrawalUnavailableReason {
|
|
192
|
+
NotInScope = 'not-in-scope',
|
|
193
|
+
Expired = 'expired',
|
|
194
|
+
Withdrawn = 'withdrawn',
|
|
195
|
+
/** Fully performed after consent: the right of withdrawal has expired, nothing would be reimbursed. */
|
|
196
|
+
Performed = 'performed',
|
|
197
|
+
Unknown = 'unknown',
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export enum CancellationUnavailableReason {
|
|
201
|
+
NoSubscription = 'no-subscription',
|
|
202
|
+
Ended = 'ended',
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export const ConsumerRegionSchema: JSONSchemaType<ConsumerRegion> = {
|
|
206
|
+
type: 'string',
|
|
207
|
+
enum: Object.values(ConsumerRegion),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const PurchaseKindSchema: JSONSchemaType<PurchaseKind> = {
|
|
211
|
+
type: 'string',
|
|
212
|
+
enum: Object.values(PurchaseKind),
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const ConsentKindSchema: JSONSchemaType<ConsentKind> = {
|
|
216
|
+
type: 'string',
|
|
217
|
+
enum: Object.values(ConsentKind),
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export const DeclarationKindSchema: JSONSchemaType<DeclarationKind> = {
|
|
221
|
+
type: 'string',
|
|
222
|
+
enum: Object.values(DeclarationKind),
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export const DeclarationChannelSchema: JSONSchemaType<DeclarationChannel> = {
|
|
226
|
+
type: 'string',
|
|
227
|
+
enum: Object.values(DeclarationChannel),
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const CancellationKindSchema: JSONSchemaType<CancellationKind> = {
|
|
231
|
+
type: 'string',
|
|
232
|
+
enum: Object.values(CancellationKind),
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export const WithdrawalStatusSchema: JSONSchemaType<WithdrawalStatus> = {
|
|
236
|
+
type: 'string',
|
|
237
|
+
enum: Object.values(WithdrawalStatus),
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export const CancellationStatusSchema: JSONSchemaType<CancellationStatus> = {
|
|
241
|
+
type: 'string',
|
|
242
|
+
enum: Object.values(CancellationStatus),
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export const WithdrawalUnavailableReasonSchema: JSONSchemaType<WithdrawalUnavailableReason> = {
|
|
246
|
+
type: 'string',
|
|
247
|
+
enum: Object.values(WithdrawalUnavailableReason),
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export const CancellationUnavailableReasonSchema: JSONSchemaType<CancellationUnavailableReason> = {
|
|
251
|
+
type: 'string',
|
|
252
|
+
enum: Object.values(CancellationUnavailableReason),
|
|
47
253
|
}
|
|
48
254
|
|
|
49
255
|
export const ProductTypeSchema: JSONSchemaType<ProductType> = {
|
|
@@ -51,6 +257,11 @@ export const ProductTypeSchema: JSONSchemaType<ProductType> = {
|
|
|
51
257
|
enum: Object.values(ProductType)
|
|
52
258
|
}
|
|
53
259
|
|
|
260
|
+
export const CheckoutPricingModeSchema: JSONSchemaType<CheckoutPricingMode> = {
|
|
261
|
+
type: 'string',
|
|
262
|
+
enum: Object.values(CheckoutPricingMode),
|
|
263
|
+
}
|
|
264
|
+
|
|
54
265
|
export const PaymentEntityTypeSchema: JSONSchemaType<PaymentEntityType> = {
|
|
55
266
|
type: 'string',
|
|
56
267
|
enum: Object.values(PaymentEntityType)
|
|
@@ -71,6 +282,36 @@ export const SubscriptionStatusSchema: JSONSchemaType<SubscriptionStatus> = {
|
|
|
71
282
|
enum: Object.values(SubscriptionStatus)
|
|
72
283
|
}
|
|
73
284
|
|
|
285
|
+
export const LimitKindSchema: JSONSchemaType<LimitKind> = {
|
|
286
|
+
type: 'string',
|
|
287
|
+
enum: Object.values(LimitKind)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export const LimitWindowSchema: JSONSchemaType<LimitWindow> = {
|
|
291
|
+
type: 'string',
|
|
292
|
+
enum: Object.values(LimitWindow)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const PortalFlowSchema: JSONSchemaType<PortalFlow> = {
|
|
296
|
+
type: 'string',
|
|
297
|
+
enum: Object.values(PortalFlow)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export const TaxBehaviorSchema: JSONSchemaType<TaxBehavior> = {
|
|
301
|
+
type: 'string',
|
|
302
|
+
enum: Object.values(TaxBehavior)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export const TaxEstimateStatusSchema: JSONSchemaType<TaxEstimateStatus> = {
|
|
306
|
+
type: 'string',
|
|
307
|
+
enum: Object.values(TaxEstimateStatus)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export const TaxTypeSchema: JSONSchemaType<TaxType> = {
|
|
311
|
+
type: 'string',
|
|
312
|
+
enum: Object.values(TaxType)
|
|
313
|
+
}
|
|
314
|
+
|
|
74
315
|
export const ProductTitleSchema: JSONSchemaType<string> = {
|
|
75
316
|
type: 'string', minLength: 1, maxLength: 128
|
|
76
317
|
}
|
|
@@ -92,30 +333,182 @@ export const PLAN_RECORD_PREFIX = PLAN_RECORD_TYPE
|
|
|
92
333
|
export const L10N_RECORD_TYPE = 'l10n'
|
|
93
334
|
export const L10N_RECORD_PREFIX = L10N_RECORD_TYPE
|
|
94
335
|
|
|
336
|
+
/** A singleton record: at most one per configuration, at this fixed id. */
|
|
337
|
+
export const PRICING_POLICY_RECORD_TYPE = 'pricing-policy'
|
|
338
|
+
export const PRICING_POLICY_RECORD_ID = PRICING_POLICY_RECORD_TYPE
|
|
339
|
+
|
|
340
|
+
/** A singleton record: the declared `ConsumerRightsPolicy`, at this fixed id. */
|
|
341
|
+
export const CONSUMER_RIGHTS_RECORD_TYPE = 'consumer-rights-policy'
|
|
342
|
+
export const CONSUMER_RIGHTS_RECORD_ID = CONSUMER_RIGHTS_RECORD_TYPE
|
|
343
|
+
|
|
344
|
+
/** The i18n resource (library tier, `lib` namespace) holding the consumer-rights legal copy. */
|
|
345
|
+
export const CONSUMER_RIGHTS_RESOURCE = 'payment-consumer-rights'
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* The version of the legal copy this package ships. Bumped on ANY change to a
|
|
349
|
+
* `payment-consumer-rights` bundle, so a consent recorded against an older wording is told apart.
|
|
350
|
+
*/
|
|
351
|
+
export const CONSUMER_RIGHTS_COPY_VERSION = '2026-10-04.2'
|
|
352
|
+
|
|
95
353
|
export const DEFAULT_ALIAS = 'payment'
|
|
96
354
|
|
|
97
355
|
export const PAYMENT_SERVICE = DEFAULT_ALIAS
|
|
98
356
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
357
|
+
/**
|
|
358
|
+
* ISO 3166-1 alpha-2 country code → lowercase ISO 4217 currency code — the currency circulating in
|
|
359
|
+
* that country, for the "≈ local total" line of a price estimate. Not a Stripe list: Stripe Tax can
|
|
360
|
+
* compute a rate for a country this map has no entry for (or none at all, `location-required`), and
|
|
361
|
+
* `currencyOfCountry` returning `null` just means the estimate carries no local-currency line, never
|
|
362
|
+
* a rejected request.
|
|
363
|
+
*
|
|
364
|
+
* A territory that circulates another country's currency (Ecuador, Kosovo, Puerto Rico, …) is
|
|
365
|
+
* mapped to that currency, not left out.
|
|
366
|
+
*/
|
|
367
|
+
export const COUNTRY_CURRENCIES: Readonly<Record<string, string>> = Object.freeze({
|
|
368
|
+
AD: 'eur', AE: 'aed', AF: 'afn', AG: 'xcd', AI: 'xcd', AL: 'all', AM: 'amd', AO: 'aoa',
|
|
369
|
+
AR: 'ars', AS: 'usd', AT: 'eur', AU: 'aud', AW: 'awg', AX: 'eur', AZ: 'azn',
|
|
370
|
+
BA: 'bam', BB: 'bbd', BD: 'bdt', BE: 'eur', BF: 'xof', BG: 'bgn', BH: 'bhd', BI: 'bif',
|
|
371
|
+
BJ: 'xof', BL: 'eur', BM: 'bmd', BN: 'bnd', BO: 'bob', BQ: 'usd', BR: 'brl', BS: 'bsd',
|
|
372
|
+
BT: 'btn', BW: 'bwp', BY: 'byn', BZ: 'bzd',
|
|
373
|
+
CA: 'cad', CD: 'cdf', CF: 'xaf', CG: 'xaf', CH: 'chf', CI: 'xof', CK: 'nzd', CL: 'clp',
|
|
374
|
+
CM: 'xaf', CN: 'cny', CO: 'cop', CR: 'crc', CU: 'cup', CV: 'cve', CW: 'ang', CY: 'eur',
|
|
375
|
+
CZ: 'czk',
|
|
376
|
+
DE: 'eur', DJ: 'djf', DK: 'dkk', DM: 'xcd', DO: 'dop', DZ: 'dzd',
|
|
377
|
+
EC: 'usd', EE: 'eur', EG: 'egp', EH: 'mad', ER: 'ern', ES: 'eur', ET: 'etb',
|
|
378
|
+
FI: 'eur', FJ: 'fjd', FK: 'fkp', FM: 'usd', FO: 'dkk', FR: 'eur',
|
|
379
|
+
GA: 'xaf', GB: 'gbp', GD: 'xcd', GE: 'gel', GF: 'eur', GG: 'gbp', GH: 'ghs', GI: 'gip',
|
|
380
|
+
GL: 'dkk', GM: 'gmd', GN: 'gnf', GP: 'eur', GQ: 'xaf', GR: 'eur', GT: 'gtq', GU: 'usd',
|
|
381
|
+
GW: 'xof', GY: 'gyd',
|
|
382
|
+
HK: 'hkd', HN: 'hnl', HR: 'eur', HT: 'htg', HU: 'huf',
|
|
383
|
+
ID: 'idr', IE: 'eur', IL: 'ils', IM: 'gbp', IN: 'inr', IQ: 'iqd', IR: 'irr', IS: 'isk',
|
|
384
|
+
IT: 'eur',
|
|
385
|
+
JE: 'gbp', JM: 'jmd', JO: 'jod', JP: 'jpy',
|
|
386
|
+
KE: 'kes', KG: 'kgs', KH: 'khr', KI: 'aud', KM: 'kmf', KN: 'xcd', KP: 'kpw', KR: 'krw',
|
|
387
|
+
KW: 'kwd', KY: 'kyd', KZ: 'kzt',
|
|
388
|
+
LA: 'lak', LB: 'lbp', LC: 'xcd', LI: 'chf', LK: 'lkr', LR: 'lrd', LS: 'lsl', LT: 'eur',
|
|
389
|
+
LU: 'eur', LV: 'eur', LY: 'lyd',
|
|
390
|
+
MA: 'mad', MC: 'eur', MD: 'mdl', ME: 'eur', MF: 'eur', MG: 'mga', MH: 'usd', MK: 'mkd',
|
|
391
|
+
ML: 'xof', MM: 'mmk', MN: 'mnt', MO: 'mop', MQ: 'eur', MR: 'mru', MS: 'xcd', MT: 'eur',
|
|
392
|
+
MU: 'mur', MV: 'mvr', MW: 'mwk', MX: 'mxn', MY: 'myr', MZ: 'mzn',
|
|
393
|
+
NA: 'nad', NC: 'xpf', NE: 'xof', NG: 'ngn', NI: 'nio', NL: 'eur', NO: 'nok', NP: 'npr',
|
|
394
|
+
NR: 'aud', NU: 'nzd', NZ: 'nzd',
|
|
395
|
+
OM: 'omr',
|
|
396
|
+
PA: 'pab', PE: 'pen', PF: 'xpf', PG: 'pgk', PH: 'php', PK: 'pkr', PL: 'pln', PR: 'usd',
|
|
397
|
+
PS: 'ils', PT: 'eur', PW: 'usd', PY: 'pyg',
|
|
398
|
+
QA: 'qar',
|
|
399
|
+
RE: 'eur', RO: 'ron', RS: 'rsd', RU: 'rub', RW: 'rwf',
|
|
400
|
+
SA: 'sar', SB: 'sbd', SC: 'scr', SD: 'sdg', SE: 'sek', SG: 'sgd', SH: 'shp', SI: 'eur',
|
|
401
|
+
SK: 'eur', SL: 'sle', SM: 'eur', SN: 'xof', SO: 'sos', SR: 'srd', SS: 'ssp', ST: 'stn',
|
|
402
|
+
SV: 'usd', SX: 'ang', SY: 'syp', SZ: 'szl',
|
|
403
|
+
TC: 'usd', TD: 'xaf', TG: 'xof', TH: 'thb', TJ: 'tjs', TK: 'nzd', TL: 'usd', TM: 'tmt',
|
|
404
|
+
TN: 'tnd', TO: 'top', TR: 'try', TT: 'ttd', TV: 'aud', TW: 'twd', TZ: 'tzs',
|
|
405
|
+
UA: 'uah', UG: 'ugx', US: 'usd', UY: 'uyu', UZ: 'uzs',
|
|
406
|
+
VA: 'eur', VC: 'xcd', VE: 'ves', VG: 'usd', VI: 'usd', VN: 'vnd', VU: 'vuv',
|
|
407
|
+
WF: 'xpf', WS: 'wst',
|
|
408
|
+
YE: 'yer', YT: 'eur',
|
|
409
|
+
ZA: 'zar', ZM: 'zmw', ZW: 'zwl',
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
/** Every country code `COUNTRY_CURRENCIES` names, sorted. */
|
|
413
|
+
export const COUNTRY_CODES: readonly string[] = Object.freeze(Object.keys(COUNTRY_CURRENCIES).sort())
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* A permissive ISO 3166-1 alpha-2 shape (two uppercase letters) — not restricted to
|
|
417
|
+
* `COUNTRY_CURRENCIES`, so a country this map cannot name a currency for is still a valid request;
|
|
418
|
+
* it only loses the local-currency line of its estimate.
|
|
419
|
+
*/
|
|
420
|
+
export const CountrySchema: JSONSchemaType<string> = {
|
|
421
|
+
type: 'string', pattern: '^[A-Z]{2}$',
|
|
121
422
|
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* The gate alias a paid capability is asserted under.
|
|
426
|
+
*
|
|
427
|
+
* Distinct from `paymentGate.base` (a ROUTE id) and from the gateway service alias — three
|
|
428
|
+
* different things that would otherwise all be called "payment gate".
|
|
429
|
+
*/
|
|
430
|
+
export const ENTITLEMENT_GATE = 'entitlement-gate'
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* The gate alias a plan limit is asserted under (`limit:<key>[>=n]`).
|
|
434
|
+
*
|
|
435
|
+
* A separate alias from `ENTITLEMENT_GATE` because the framework collects an entrypoint's gates
|
|
436
|
+
* per gate SERVICE: a capability requirement and a limit requirement under one alias would hide
|
|
437
|
+
* each other.
|
|
438
|
+
*/
|
|
439
|
+
export const LIMIT_GATE = 'limit-gate'
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* The capability scope that carries FEATURE flags.
|
|
443
|
+
*
|
|
444
|
+
* Kept apart from `renewable`, which carries numeric quotas that are consumed or counted. Merging
|
|
445
|
+
* them would make "has one production slot left" and "may remove the platform credit" the same
|
|
446
|
+
* number, and the first purchase that spent the quota would take the feature with it.
|
|
447
|
+
*/
|
|
448
|
+
export const CAPABILITY_FEATURE_SCOPE = 'feature'
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* The scope RESERVED for limit parameters (`limit:<key>[>=n]`).
|
|
452
|
+
*
|
|
453
|
+
* Never a declarable capability scope: a plan's limits live in `ProductPlan.limits`, and a
|
|
454
|
+
* capability predicate refuses any parameter under this scope, so a limit requirement can never be
|
|
455
|
+
* satisfied by a capability grant.
|
|
456
|
+
*/
|
|
457
|
+
export const CAPABILITY_LIMIT_SCOPE = 'limit'
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Preserves today's fixed behavior for every consumer that declares no policy: automatic tax and
|
|
461
|
+
* tax-id collection stay on for every checkout (`taxOptions` before this policy existed), no
|
|
462
|
+
* `behavior` is forced onto a synced price, no Adaptive Pricing, and the estimate endpoints are off
|
|
463
|
+
* (a new capability, opt-in only).
|
|
464
|
+
*/
|
|
465
|
+
export const DEFAULT_PRICING_POLICY: PricingPolicy = Object.freeze({
|
|
466
|
+
tax: Object.freeze({ automatic: true, collectTaxId: true, estimate: false }),
|
|
467
|
+
currency: Object.freeze({ estimate: false }),
|
|
468
|
+
}) as PricingPolicy
|
|
469
|
+
|
|
470
|
+
/** The window key a lifetime limit's counter lives under. */
|
|
471
|
+
export const LIFETIME_WINDOW = 'lifetime'
|
|
472
|
+
|
|
473
|
+
/** The window key an occupancy limit's counter lives under. */
|
|
474
|
+
export const OCCUPANCY_WINDOW = 'occupancy'
|
|
475
|
+
|
|
476
|
+
/** The 27 EU member states (ISO 3166-1 alpha-2; Greece is `GR`, as Stripe reports it). */
|
|
477
|
+
export const EU_COUNTRIES: readonly string[] = Object.freeze([
|
|
478
|
+
'AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GR', 'HR', 'HU',
|
|
479
|
+
'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK',
|
|
480
|
+
])
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* The member states plus the parts of them that carry their own ISO code — Åland (FI) and the
|
|
484
|
+
* French outermost regions (French Guiana, Guadeloupe, Martinique, Réunion, Mayotte, Saint-Martin).
|
|
485
|
+
* EU consumer law applies there even where EU VAT does not; the Canary Islands, Azores and Madeira
|
|
486
|
+
* share their state's code. Scope beyond the member states is a question for the lawyer.
|
|
487
|
+
*/
|
|
488
|
+
export const EU_CONSUMER_TERRITORIES: readonly string[] = Object.freeze([
|
|
489
|
+
...EU_COUNTRIES, 'AX', 'GF', 'GP', 'MQ', 'RE', 'YT', 'MF',
|
|
490
|
+
])
|
|
491
|
+
|
|
492
|
+
/** The EEA members outside the EU. */
|
|
493
|
+
export const EEA_EXTRA: readonly string[] = Object.freeze(['IS', 'LI', 'NO'])
|
|
494
|
+
|
|
495
|
+
/** The territories whose consumers have the right of withdrawal by default: the EU's and the EEA's. */
|
|
496
|
+
export const CONSUMER_RIGHTS_TERRITORIES: readonly string[] = Object.freeze([
|
|
497
|
+
...EU_CONSUMER_TERRITORIES, ...EEA_EXTRA,
|
|
498
|
+
])
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Country → the language of its legal copy, only where that is unambiguous (Belgium, Luxembourg,
|
|
502
|
+
* Switzerland, Canada … are absent and fall back to the policy's default). An application widens
|
|
503
|
+
* or overrides it with `ConsumerRightsPolicy.languages`.
|
|
504
|
+
*/
|
|
505
|
+
export const COUNTRY_LANGUAGES: Readonly<Record<string, string>> = Object.freeze({
|
|
506
|
+
DE: 'de', AT: 'de', LI: 'de',
|
|
507
|
+
FR: 'fr', GF: 'fr', GP: 'fr', MQ: 'fr', RE: 'fr', YT: 'fr', MF: 'fr', MC: 'fr',
|
|
508
|
+
PL: 'pl',
|
|
509
|
+
ES: 'es',
|
|
510
|
+
IE: 'en', MT: 'en', GB: 'en', US: 'en', AU: 'en', NZ: 'en',
|
|
511
|
+
UA: 'uk',
|
|
512
|
+
BY: 'be',
|
|
513
|
+
RU: 'ru',
|
|
514
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ConsumerRightsError } from '../errors.js'
|
|
2
|
+
import type { BillingInterval } from './types.js'
|
|
3
|
+
|
|
4
|
+
/** `anchor` moved by `months` calendar months (UTC), its day clamped to the target month's end. */
|
|
5
|
+
const addMonths = (anchor: Date, months: number): Date => {
|
|
6
|
+
const year = anchor.getUTCFullYear()
|
|
7
|
+
const month = anchor.getUTCMonth() + months
|
|
8
|
+
const lastDay = new Date(Date.UTC(year, month + 1, 0)).getUTCDate()
|
|
9
|
+
|
|
10
|
+
return new Date(Date.UTC(
|
|
11
|
+
year, month, Math.min(anchor.getUTCDate(), lastDay),
|
|
12
|
+
anchor.getUTCHours(), anchor.getUTCMinutes(), anchor.getUTCSeconds(), anchor.getUTCMilliseconds(),
|
|
13
|
+
))
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* When an ordinary cancellation takes effect. No requested date, or one on or before the current
|
|
18
|
+
* period's end, ends the contract at `periodEnd`; a later date ends it at the first period
|
|
19
|
+
* boundary on or after that date — whole intervals stepped from `periodEnd`, each step measured
|
|
20
|
+
* from `periodEnd` itself so a 31st stays the 31st where the month has one.
|
|
21
|
+
*
|
|
22
|
+
* @throws ConsumerRightsError (`cancellation:date`) for an invalid date.
|
|
23
|
+
*/
|
|
24
|
+
export const cancellationEffectiveAt = (periodEnd: Date, interval: BillingInterval, requested?: Date | null): Date => {
|
|
25
|
+
if (Number.isNaN(periodEnd.getTime()) || (requested != null && Number.isNaN(requested.getTime()))) {
|
|
26
|
+
throw new ConsumerRightsError('cancellation:date')
|
|
27
|
+
}
|
|
28
|
+
if (requested == null || requested.getTime() <= periodEnd.getTime()) {
|
|
29
|
+
return periodEnd
|
|
30
|
+
}
|
|
31
|
+
const step = interval === 'year' ? 12 : 1
|
|
32
|
+
let count = 1
|
|
33
|
+
let candidate = addMonths(periodEnd, step)
|
|
34
|
+
while (candidate.getTime() < requested.getTime()) {
|
|
35
|
+
count += 1
|
|
36
|
+
candidate = addMonths(periodEnd, step * count)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return candidate
|
|
40
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ConsentKind } from '../consts.js'
|
|
2
|
+
import { PerformanceConsentRequired, SubscriptionStartRequired } from '../errors.js'
|
|
3
|
+
|
|
4
|
+
export const COUNTRY = /^[A-Z]{2}$/
|
|
5
|
+
|
|
6
|
+
export const CURRENCY = /^[a-z]{3}$/
|
|
7
|
+
|
|
8
|
+
export const CONSENT_CONTEXT = /^[a-z][a-z0-9-]{0,63}$/
|
|
9
|
+
|
|
10
|
+
/** The marker or type name a refusal of each express request carries in its message or type. */
|
|
11
|
+
export const CONSENT_REFUSAL_MARKERS: Array<[string, ConsentKind]> = [
|
|
12
|
+
['performance-consent-required', ConsentKind.Performance],
|
|
13
|
+
[PerformanceConsentRequired.typeName, ConsentKind.Performance],
|
|
14
|
+
['subscription-start-required', ConsentKind.SubscriptionStart],
|
|
15
|
+
[SubscriptionStartRequired.typeName, ConsentKind.SubscriptionStart],
|
|
16
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ConsumerRightsMechanisms, ConsumerRightsPolicy } from '../types.js'
|
|
2
|
+
import { CONSUMER_RIGHTS_TERRITORIES } from '../consts.js'
|
|
3
|
+
|
|
4
|
+
export const DAY_MS = 86_400_000
|
|
5
|
+
|
|
6
|
+
/** Every mechanism off: declaring a policy changes nothing until the application switches one on. */
|
|
7
|
+
export const NO_CONSUMER_RIGHTS_MECHANISMS: ConsumerRightsMechanisms = Object.freeze({
|
|
8
|
+
countryLock: false,
|
|
9
|
+
checkoutTerms: false,
|
|
10
|
+
performanceConsent: false,
|
|
11
|
+
subscriptionStart: false,
|
|
12
|
+
withdrawal: false,
|
|
13
|
+
automaticRefunds: false,
|
|
14
|
+
cancellation: false,
|
|
15
|
+
purchaseConfirmation: false,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* What a declaration leaves out: the EU and EEA territories, an unknown country protected, 14 days
|
|
20
|
+
* ending at the end of a UTC day, a weekend end moved to Monday, five margin days, every mechanism
|
|
21
|
+
* off, English as the default language, start requests usable for an hour.
|
|
22
|
+
*
|
|
23
|
+
* Five margin days because a period ending on a public holiday runs to the next working day (EU
|
|
24
|
+
* Reg. 1182/71 art. 3(4)) and holiday clusters (24–26 December plus a weekend, Maundy Thursday to
|
|
25
|
+
* Easter Monday) need up to five; no per-country holiday calendar is kept, so a generous margin
|
|
26
|
+
* keeps the withdrawal function open for the whole statutory period everywhere.
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_CONSUMER_RIGHTS: Readonly<Omit<ConsumerRightsPolicy, 'textVersion' | 'links'>> = Object.freeze({
|
|
29
|
+
countries: [...CONSUMER_RIGHTS_TERRITORIES],
|
|
30
|
+
unknownCountry: 'protect',
|
|
31
|
+
withdrawalDays: 14,
|
|
32
|
+
deadline: Object.freeze({ weekendRollover: true, marginDays: 5 }),
|
|
33
|
+
mechanisms: NO_CONSUMER_RIGHTS_MECHANISMS,
|
|
34
|
+
defaultLanguage: 'en',
|
|
35
|
+
renewalOpensWindow: false,
|
|
36
|
+
startRequestTtlSeconds: 3600,
|
|
37
|
+
exemptBusinesses: false,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export const CONSUMER_RIGHTS_API_PATH = '/consumer-rights'
|
|
41
|
+
|
|
42
|
+
export const CONSUMER_RIGHTS_PUBLIC_PATH = '/public/consumer-rights'
|
|
43
|
+
|
|
44
|
+
export const CONSUMER_RIGHTS_WITHDRAWAL_SCREEN_PATH = '/legal/withdraw'
|
|
45
|
+
|
|
46
|
+
export const CONSUMER_RIGHTS_CANCELLATION_SCREEN_PATH = '/legal/cancel'
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ConsentKind } from '../../consts.js'
|
|
2
|
+
import type { ProductPlan } from '../../types.js'
|
|
3
|
+
import type { ConsentStatement, CopyTree, CopyValues, LegalLabels } from '../types.js'
|
|
4
|
+
|
|
5
|
+
/** The consumer-rights legal copy of any language, and the texts rendered from it. */
|
|
6
|
+
export interface ConsumerCopyHelper {
|
|
7
|
+
/**
|
|
8
|
+
* The consumer-rights copy of one language — the `payment-consumer-rights` bundles as registered
|
|
9
|
+
* (the package's own, merged under any application override made with
|
|
10
|
+
* `addI18nApp(lng, 'payment-consumer-rights', data, { ns: LIB_NAMESPACE })`), key by key over the
|
|
11
|
+
* English ones so a missing key still reads. Any language, independent of the active i18next
|
|
12
|
+
* language and without draining a bundle: this is what a server renders e-mails and paygate
|
|
13
|
+
* texts from, and what a dialog shows in the billing country's language.
|
|
14
|
+
*/
|
|
15
|
+
consumerRightsCopy: (lng: string) => CopyTree
|
|
16
|
+
/**
|
|
17
|
+
* One text of the copy at a dot path (`withdrawal.function`), its `{{name}}` placeholders filled.
|
|
18
|
+
* Values are inserted as given — escape them for the medium (HTML) before passing them in.
|
|
19
|
+
*
|
|
20
|
+
* With a `context`, the variant `<path>_<context>` is read first and the base `path` only where the
|
|
21
|
+
* copy has no such variant — the i18next context suffix (`performance-consent.request_included`).
|
|
22
|
+
*
|
|
23
|
+
* @throws ConsumerRightsError `copy:<path>` when the path is not a text, `copy:<path>:<name>` when a
|
|
24
|
+
* placeholder has no value — a legal text is never sent with a hole in it.
|
|
25
|
+
*/
|
|
26
|
+
consumerText: (lng: string, path: string, vars?: CopyValues, context?: string) => string
|
|
27
|
+
/** The placeholders a text of the copy expects, in order of first appearance. */
|
|
28
|
+
placeholdersOf: (text: string) => string[]
|
|
29
|
+
/**
|
|
30
|
+
* The statutory button labels of one language — the withdrawal function (CRD Art. 11a; § 356a
|
|
31
|
+
* BGB; L221-21) and the cancellation function (§ 312k BGB; L215-1-1). Shown in the language of the
|
|
32
|
+
* billing country, whatever the interface language.
|
|
33
|
+
*/
|
|
34
|
+
legalLabelsOf: (lng: string) => LegalLabels
|
|
35
|
+
/**
|
|
36
|
+
* The copy variant of a plan's subscription start statement — the one place it is decided.
|
|
37
|
+
*
|
|
38
|
+
* `'units'` exactly when the plan's `withdrawal.components` declare a `units` part: the statement
|
|
39
|
+
* then names the split (the services pro rata by time, the right of withdrawal expiring for the
|
|
40
|
+
* included units used) — the `_units` texts. Otherwise (no components, or `time` parts only: the
|
|
41
|
+
* whole price pro rata by the days elapsed) `undefined`, the base texts.
|
|
42
|
+
*/
|
|
43
|
+
startContextOf: (plan: Pick<ProductPlan, 'withdrawal'> | null | undefined) => 'units' | undefined
|
|
44
|
+
/**
|
|
45
|
+
* The express request of one consent kind in one language, rendered — what the dialog shows, the
|
|
46
|
+
* server records and the confirmation e-mail repeats verbatim. `trader` names the business;
|
|
47
|
+
* `plan` (the plan's title) is required for a subscription start; `context` picks the
|
|
48
|
+
* `_<context>` variant of each text that has one — the policy's `consentContext` for the
|
|
49
|
+
* performance consent, `startContextOf(plan)` for a subscription start.
|
|
50
|
+
*/
|
|
51
|
+
consentStatementOf: (
|
|
52
|
+
lng: string, kind: ConsentKind, vars: { trader: string, plan?: string, context?: string },
|
|
53
|
+
) => ConsentStatement
|
|
54
|
+
}
|