@owlmeans/payment 0.1.18-rc.9 → 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 +7 -55
- package/build/entitlement.d.ts.map +1 -1
- package/build/entitlement.js +55 -86
- package/build/entitlement.js.map +1 -1
- 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 +18 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +11 -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 +67 -103
- 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 +18 -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 +38 -25
- 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/view.spec.ts +130 -0
- 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/tests/entitlement.spec.d.ts +0 -2
- package/tests/entitlement.spec.d.ts.map +0 -1
- package/tests/entitlement.spec.js +0 -72
- package/tests/entitlement.spec.js.map +0 -1
package/src/pricing.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { PaymentError } from './errors.js'
|
|
2
|
+
import type { AmountCheckoutPolicy, QuantityCheckoutPolicy } from './types.js'
|
|
3
|
+
import type { CheckoutPricingHelper } from './pricing/types.js'
|
|
4
|
+
|
|
5
|
+
const integer = (value: number): boolean => Number.isSafeInteger(value)
|
|
6
|
+
|
|
7
|
+
export const createCheckoutPricingHelper = (): CheckoutPricingHelper => {
|
|
8
|
+
const assertAmountCheckoutPolicy = (
|
|
9
|
+
policy: AmountCheckoutPolicy,
|
|
10
|
+
): AmountCheckoutPolicy => {
|
|
11
|
+
if (
|
|
12
|
+
!integer(policy.minimumMinor)
|
|
13
|
+
|| !integer(policy.maximumMinor)
|
|
14
|
+
|| !integer(policy.defaultMinor)
|
|
15
|
+
|| !integer(policy.fixedMinor)
|
|
16
|
+
|| !integer(policy.rateBps)
|
|
17
|
+
|| policy.minimumMinor < 0
|
|
18
|
+
|| policy.minimumMinor > policy.defaultMinor
|
|
19
|
+
|| policy.defaultMinor > policy.maximumMinor
|
|
20
|
+
|| policy.fixedMinor < 0
|
|
21
|
+
|| policy.rateBps < 0
|
|
22
|
+
|| policy.rateBps >= 10_000
|
|
23
|
+
|| policy.currency.trim().length !== 3
|
|
24
|
+
) {
|
|
25
|
+
throw new PaymentError('checkout-policy:amount')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const presets = new Set(policy.presetsMinor)
|
|
29
|
+
if (
|
|
30
|
+
presets.size !== policy.presetsMinor.length
|
|
31
|
+
|| policy.presetsMinor.some(value =>
|
|
32
|
+
!integer(value) || value < policy.minimumMinor || value > policy.maximumMinor
|
|
33
|
+
)
|
|
34
|
+
) {
|
|
35
|
+
throw new PaymentError('checkout-policy:presets')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return policy
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const assertQuantityCheckoutPolicy = (
|
|
42
|
+
policy: QuantityCheckoutPolicy,
|
|
43
|
+
): QuantityCheckoutPolicy => {
|
|
44
|
+
if (
|
|
45
|
+
!integer(policy.minimum)
|
|
46
|
+
|| !integer(policy.maximum)
|
|
47
|
+
|| !integer(policy.default)
|
|
48
|
+
|| policy.minimum < 1
|
|
49
|
+
|| policy.minimum > policy.default
|
|
50
|
+
|| policy.default > policy.maximum
|
|
51
|
+
) {
|
|
52
|
+
throw new PaymentError('checkout-policy:quantity')
|
|
53
|
+
}
|
|
54
|
+
return policy
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const assertCheckoutAmount = (
|
|
58
|
+
policy: AmountCheckoutPolicy, amountMinor: number,
|
|
59
|
+
): number => {
|
|
60
|
+
assertAmountCheckoutPolicy(policy)
|
|
61
|
+
if (!integer(amountMinor)) {
|
|
62
|
+
throw new PaymentError('checkout-amount:integer')
|
|
63
|
+
}
|
|
64
|
+
if (amountMinor < policy.minimumMinor) {
|
|
65
|
+
throw new PaymentError('checkout-amount:minimum')
|
|
66
|
+
}
|
|
67
|
+
if (amountMinor > policy.maximumMinor) {
|
|
68
|
+
throw new PaymentError('checkout-amount:maximum')
|
|
69
|
+
}
|
|
70
|
+
return amountMinor
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const chargeAmountMinor = (
|
|
74
|
+
amountMinor: number, policy: Pick<AmountCheckoutPolicy, 'fixedMinor' | 'rateBps'>,
|
|
75
|
+
): number => {
|
|
76
|
+
const adjusted = amountMinor + policy.fixedMinor
|
|
77
|
+
const numerator = adjusted * 10_000
|
|
78
|
+
if (
|
|
79
|
+
!integer(amountMinor) || !integer(policy.fixedMinor) || !integer(policy.rateBps)
|
|
80
|
+
|| amountMinor < 0 || policy.fixedMinor < 0 || policy.rateBps < 0 || policy.rateBps >= 10_000
|
|
81
|
+
|| !integer(adjusted) || !integer(numerator)
|
|
82
|
+
) {
|
|
83
|
+
throw new PaymentError('checkout-charge')
|
|
84
|
+
}
|
|
85
|
+
const charge = Math.ceil(numerator / (10_000 - policy.rateBps))
|
|
86
|
+
if (!integer(charge)) throw new PaymentError('checkout-charge')
|
|
87
|
+
return charge
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { assertAmountCheckoutPolicy, assertQuantityCheckoutPolicy, assertCheckoutAmount, chargeAmountMinor }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const checkoutPricingHelper = createCheckoutPricingHelper()
|
|
94
|
+
|
|
95
|
+
/** @deprecated compat:factory-refactor — use `checkoutPricingHelper.assertAmountCheckoutPolicy(…)` */
|
|
96
|
+
export const assertAmountCheckoutPolicy = (policy: AmountCheckoutPolicy): AmountCheckoutPolicy =>
|
|
97
|
+
checkoutPricingHelper.assertAmountCheckoutPolicy(policy)
|
|
98
|
+
|
|
99
|
+
/** @deprecated compat:factory-refactor — use `checkoutPricingHelper.assertQuantityCheckoutPolicy(…)` */
|
|
100
|
+
export const assertQuantityCheckoutPolicy = (policy: QuantityCheckoutPolicy): QuantityCheckoutPolicy =>
|
|
101
|
+
checkoutPricingHelper.assertQuantityCheckoutPolicy(policy)
|
|
102
|
+
|
|
103
|
+
/** @deprecated compat:factory-refactor — use `checkoutPricingHelper.assertCheckoutAmount(…)` */
|
|
104
|
+
export const assertCheckoutAmount = (policy: AmountCheckoutPolicy, amountMinor: number): number =>
|
|
105
|
+
checkoutPricingHelper.assertCheckoutAmount(policy, amountMinor)
|
|
106
|
+
|
|
107
|
+
/** @deprecated compat:factory-refactor — use `checkoutPricingHelper.chargeAmountMinor(…)` */
|
|
108
|
+
export const chargeAmountMinor = (
|
|
109
|
+
amountMinor: number, policy: Pick<AmountCheckoutPolicy, 'fixedMinor' | 'rateBps'>,
|
|
110
|
+
): number => checkoutPricingHelper.chargeAmountMinor(amountMinor, policy)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PromoDeclaration, PromoView } from '../types.js'
|
|
2
|
+
|
|
3
|
+
/** Whether a promo is in force, and how a UI inscribes it. */
|
|
4
|
+
export interface PromoHelper {
|
|
5
|
+
/**
|
|
6
|
+
* Whether a grant behind a promo is in force.
|
|
7
|
+
*
|
|
8
|
+
* No promo ⇒ always. Otherwise while `at < until`, or — with `grandfather` — for a subscription
|
|
9
|
+
* created before `until`. `at === until` is already over.
|
|
10
|
+
*/
|
|
11
|
+
promoActive: (
|
|
12
|
+
promo: PromoDeclaration | null | undefined, subscribedAt: Date | null | undefined, at?: Date,
|
|
13
|
+
) => boolean
|
|
14
|
+
/** What a UI needs to inscribe a promo: `undefined` when the grant has none. */
|
|
15
|
+
promoViewOf: (
|
|
16
|
+
promo: PromoDeclaration | null | undefined, subscribedAt: Date | null | undefined, at?: Date,
|
|
17
|
+
) => PromoView | undefined
|
|
18
|
+
}
|
package/src/promo.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { PromoDeclaration, PromoView } from './types.js'
|
|
2
|
+
import type { PromoHelper } from './promo/types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A date as epoch milliseconds. A declaration read back from JSON carries an ISO string where the
|
|
6
|
+
* type says `Date`; an unparseable value is `NaN`, which fails every comparison — so a broken
|
|
7
|
+
* promo is never in force.
|
|
8
|
+
*/
|
|
9
|
+
const timeOf = (value: Date | string | number | null | undefined): number =>
|
|
10
|
+
value == null ? Number.NaN : value instanceof Date ? value.getTime() : new Date(value).getTime()
|
|
11
|
+
|
|
12
|
+
export const createPromoHelper = (): PromoHelper => {
|
|
13
|
+
const grandfathers = (promo: PromoDeclaration, subscribedAt: Date | null | undefined): boolean =>
|
|
14
|
+
promo.grandfather === true && timeOf(subscribedAt) < timeOf(promo.until)
|
|
15
|
+
|
|
16
|
+
const promoActive = (
|
|
17
|
+
promo: PromoDeclaration | null | undefined,
|
|
18
|
+
subscribedAt: Date | null | undefined,
|
|
19
|
+
at: Date = new Date(),
|
|
20
|
+
): boolean => {
|
|
21
|
+
if (promo == null) {
|
|
22
|
+
return true
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return timeOf(at) < timeOf(promo.until) || grandfathers(promo, subscribedAt)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const promoViewOf = (
|
|
29
|
+
promo: PromoDeclaration | null | undefined,
|
|
30
|
+
subscribedAt: Date | null | undefined,
|
|
31
|
+
at: Date = new Date(),
|
|
32
|
+
): PromoView | undefined => promo == null ? undefined : {
|
|
33
|
+
until: new Date(timeOf(promo.until)),
|
|
34
|
+
grandfathered: grandfathers(promo, subscribedAt),
|
|
35
|
+
active: promoActive(promo, subscribedAt, at),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return { promoActive, promoViewOf }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const promoHelper = createPromoHelper()
|
|
42
|
+
|
|
43
|
+
/** @deprecated compat:factory-refactor — use `promoHelper.promoActive(…)` */
|
|
44
|
+
export const promoActive = (
|
|
45
|
+
promo: PromoDeclaration | null | undefined, subscribedAt: Date | null | undefined, at?: Date,
|
|
46
|
+
): boolean => promoHelper.promoActive(promo, subscribedAt, at)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ConsumerRegion } from '../consts.js'
|
|
2
|
+
import type { ConsumerRightsPolicy } from '../types.js'
|
|
3
|
+
import type { RegionPolicy } from '../types.local.js'
|
|
4
|
+
|
|
5
|
+
/** The consumer region of a billing country and what it decides: scope, currency and legal language. */
|
|
6
|
+
export interface ConsumerRegionHelper {
|
|
7
|
+
/** A member state of the EU (not a territory with its own code). */
|
|
8
|
+
isEuCountry: (country: string | null | undefined) => boolean
|
|
9
|
+
/** A member state of the EEA: the EU's 27 plus Iceland, Liechtenstein and Norway. */
|
|
10
|
+
isEeaCountry: (country: string | null | undefined) => boolean
|
|
11
|
+
/**
|
|
12
|
+
* The consumer region of a billing country: `Eu` inside the policy's territories (the default
|
|
13
|
+
* ones without a policy), `Other` outside, `null` when the country is unknown.
|
|
14
|
+
*/
|
|
15
|
+
regionOf: (country: string | null | undefined, policy?: RegionPolicy) => ConsumerRegion | null
|
|
16
|
+
/**
|
|
17
|
+
* Whether a buyer has the consumer rights: a known country decides by the policy's territories;
|
|
18
|
+
* without one, a known region decides; with neither, `unknownCountry: 'protect'` (the default) puts
|
|
19
|
+
* the buyer in scope — the safe side.
|
|
20
|
+
*/
|
|
21
|
+
inScope: (
|
|
22
|
+
region: ConsumerRegion | null | undefined, country: string | null | undefined,
|
|
23
|
+
policy?: Pick<ConsumerRightsPolicy, 'countries' | 'unknownCountry'> | null,
|
|
24
|
+
) => boolean
|
|
25
|
+
/**
|
|
26
|
+
* The currency a region is charged in (lowercase ISO 4217): the policy's currency for the region,
|
|
27
|
+
* an unknown region reading as `Eu`; `fallback` when the policy names none.
|
|
28
|
+
*/
|
|
29
|
+
chargeCurrencyOf: (
|
|
30
|
+
region: ConsumerRegion | null | undefined, policy: Pick<ConsumerRightsPolicy, 'currencies'> | null | undefined,
|
|
31
|
+
fallback: string,
|
|
32
|
+
) => string
|
|
33
|
+
/**
|
|
34
|
+
* The language of a billing country's legal copy: the policy's own map, then
|
|
35
|
+
* `COUNTRY_LANGUAGES`, then `fallback`, then the policy's default language, then `en`.
|
|
36
|
+
*/
|
|
37
|
+
billingLanguageOf: (
|
|
38
|
+
country: string | null | undefined,
|
|
39
|
+
policy?: Pick<ConsumerRightsPolicy, 'languages' | 'defaultLanguage'> | null,
|
|
40
|
+
fallback?: string,
|
|
41
|
+
) => string
|
|
42
|
+
}
|
package/src/regions.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ConsumerRegion, CONSUMER_RIGHTS_TERRITORIES, COUNTRY_LANGUAGES, EEA_EXTRA, EU_COUNTRIES } from './consts.js'
|
|
2
|
+
import type { ConsumerRightsPolicy } from './types.js'
|
|
3
|
+
import type { RegionPolicy } from './types.local.js'
|
|
4
|
+
import type { ConsumerRegionHelper } from './regions/types.js'
|
|
5
|
+
|
|
6
|
+
const codeOf = (country: string | null | undefined): string | null =>
|
|
7
|
+
country == null || country.trim() === '' ? null : country.trim().toUpperCase()
|
|
8
|
+
|
|
9
|
+
export const createConsumerRegionHelper = (): ConsumerRegionHelper => {
|
|
10
|
+
const isEuCountry = (country: string | null | undefined): boolean =>
|
|
11
|
+
EU_COUNTRIES.includes(codeOf(country) ?? '')
|
|
12
|
+
|
|
13
|
+
const isEeaCountry = (country: string | null | undefined): boolean => {
|
|
14
|
+
const code = codeOf(country) ?? ''
|
|
15
|
+
|
|
16
|
+
return EU_COUNTRIES.includes(code) || EEA_EXTRA.includes(code)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const regionOf = (country: string | null | undefined, policy?: RegionPolicy): ConsumerRegion | null => {
|
|
20
|
+
const code = codeOf(country)
|
|
21
|
+
if (code == null) {
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return (policy?.countries ?? CONSUMER_RIGHTS_TERRITORIES).includes(code) ? ConsumerRegion.Eu : ConsumerRegion.Other
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const inScope = (
|
|
29
|
+
region: ConsumerRegion | null | undefined, country: string | null | undefined,
|
|
30
|
+
policy?: Pick<ConsumerRightsPolicy, 'countries' | 'unknownCountry'> | null,
|
|
31
|
+
): boolean => {
|
|
32
|
+
const code = codeOf(country)
|
|
33
|
+
if (code != null) {
|
|
34
|
+
return (policy?.countries ?? CONSUMER_RIGHTS_TERRITORIES).includes(code)
|
|
35
|
+
}
|
|
36
|
+
if (region != null) {
|
|
37
|
+
return region === ConsumerRegion.Eu
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (policy?.unknownCountry ?? 'protect') === 'protect'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const chargeCurrencyOf = (
|
|
44
|
+
region: ConsumerRegion | null | undefined, policy: Pick<ConsumerRightsPolicy, 'currencies'> | null | undefined,
|
|
45
|
+
fallback: string,
|
|
46
|
+
): string => (policy?.currencies?.[region ?? ConsumerRegion.Eu] ?? fallback).toLowerCase()
|
|
47
|
+
|
|
48
|
+
const billingLanguageOf = (
|
|
49
|
+
country: string | null | undefined,
|
|
50
|
+
policy?: Pick<ConsumerRightsPolicy, 'languages' | 'defaultLanguage'> | null,
|
|
51
|
+
fallback?: string,
|
|
52
|
+
): string => {
|
|
53
|
+
const code = codeOf(country)
|
|
54
|
+
const mapped = code == null ? undefined : policy?.languages?.[code] ?? COUNTRY_LANGUAGES[code]
|
|
55
|
+
|
|
56
|
+
return mapped ?? fallback ?? policy?.defaultLanguage ?? 'en'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { isEuCountry, isEeaCountry, regionOf, inScope, chargeCurrencyOf, billingLanguageOf }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const consumerRegionHelper = createConsumerRegionHelper()
|
|
63
|
+
|
|
64
|
+
/** @deprecated compat:factory-refactor — use `consumerRegionHelper.regionOf(…)` */
|
|
65
|
+
export const regionOf = (country: string | null | undefined, policy?: RegionPolicy): ConsumerRegion | null =>
|
|
66
|
+
consumerRegionHelper.regionOf(country, policy)
|
|
67
|
+
|
|
68
|
+
/** @deprecated compat:factory-refactor — use `consumerRegionHelper.inScope(…)` */
|
|
69
|
+
export const inScope = (
|
|
70
|
+
region: ConsumerRegion | null | undefined, country: string | null | undefined,
|
|
71
|
+
policy?: Pick<ConsumerRightsPolicy, 'countries' | 'unknownCountry'> | null,
|
|
72
|
+
): boolean => consumerRegionHelper.inScope(region, country, policy)
|
|
73
|
+
|
|
74
|
+
/** @deprecated compat:factory-refactor — use `consumerRegionHelper.chargeCurrencyOf(…)` */
|
|
75
|
+
export const chargeCurrencyOf = (
|
|
76
|
+
region: ConsumerRegion | null | undefined, policy: Pick<ConsumerRightsPolicy, 'currencies'> | null | undefined,
|
|
77
|
+
fallback: string,
|
|
78
|
+
): string => consumerRegionHelper.chargeCurrencyOf(region, policy, fallback)
|
|
79
|
+
|
|
80
|
+
/** @deprecated compat:factory-refactor — use `consumerRegionHelper.billingLanguageOf(…)` */
|
|
81
|
+
export const billingLanguageOf = (
|
|
82
|
+
country: string | null | undefined,
|
|
83
|
+
policy?: Pick<ConsumerRightsPolicy, 'languages' | 'defaultLanguage'> | null,
|
|
84
|
+
fallback?: string,
|
|
85
|
+
): string => consumerRegionHelper.billingLanguageOf(country, policy, fallback)
|
package/src/service.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { createService } from '@owlmeans/context'
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type { Localization, PaymentService } from './types.js'
|
|
1
|
+
import { createService, type ConfigRecord } from '@owlmeans/context'
|
|
2
|
+
import { CONSUMER_RIGHTS_RECORD_ID, DEFAULT_ALIAS, PaymentEntityType, PLAN_RECORD_PREFIX, PRICING_POLICY_RECORD_ID, PRODUCT_RECORD_TYPE, PLAN_RECORD_TYPE, PRODUCT_RECORD_PREFIX, DEFAULT_PRICING_POLICY } from './consts.js'
|
|
3
|
+
import type { ConsumerRightsPolicy, Localization, PaymentService, PricingPolicy, Product, ProductPlan } from './types.js'
|
|
5
4
|
import type { Config, Context } from './utils/types.js'
|
|
6
|
-
import { PLAN_RECORD_TYPE, PRODUCT_RECORD_PREFIX } from './consts.js'
|
|
7
5
|
import { PaymentIdentificationError, UnknownPlan, UnknownProduct } from './errors.js'
|
|
8
|
-
import type { Product, ProductPlan } from './types.js'
|
|
9
|
-
import { fromConfigRecord } from '@owlmeans/config'
|
|
10
6
|
import type { ResourceRecord } from '@owlmeans/resource'
|
|
11
7
|
import { l10nToId } from './helper.js'
|
|
12
8
|
import { DEFAULT_LNG } from '@owlmeans/i18n'
|
|
13
9
|
import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
|
|
14
10
|
import type { AuthCredentials } from '@owlmeans/auth'
|
|
11
|
+
import { configHelper } from '@owlmeans/config'
|
|
15
12
|
|
|
16
13
|
export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentService => {
|
|
17
14
|
const service: PaymentService = createService<PaymentService>(alias, {
|
|
@@ -19,9 +16,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
19
16
|
const context = service.assertCtx() as Context
|
|
20
17
|
const configRes = context.getConfigResource()
|
|
21
18
|
|
|
22
|
-
const result = fromConfigRecord<ConfigRecord, Product & ResourceRecord>(
|
|
23
|
-
await configRes.get(`${PRODUCT_RECORD_PREFIX}:${sku}`)
|
|
24
|
-
)
|
|
19
|
+
const result = configHelper.fromConfigRecord<ConfigRecord, Product & ResourceRecord>(await configRes.get(`${PRODUCT_RECORD_PREFIX}:${sku}`))
|
|
25
20
|
|
|
26
21
|
if (result == null) {
|
|
27
22
|
throw new UnknownProduct(sku)
|
|
@@ -36,7 +31,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
36
31
|
|
|
37
32
|
const { items } = await configRes.list({ recordType: PRODUCT_RECORD_TYPE })
|
|
38
33
|
|
|
39
|
-
return items.map(i => fromConfigRecord<ConfigRecord, Product & ResourceRecord>(i))
|
|
34
|
+
return items.map(i => configHelper.fromConfigRecord<ConfigRecord, Product & ResourceRecord>(i))
|
|
40
35
|
},
|
|
41
36
|
|
|
42
37
|
plans: async (productSku, duration) => {
|
|
@@ -48,7 +43,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
48
43
|
})
|
|
49
44
|
|
|
50
45
|
return result.items.map(
|
|
51
|
-
item => fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(item)
|
|
46
|
+
item => configHelper.fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(item)
|
|
52
47
|
)
|
|
53
48
|
},
|
|
54
49
|
|
|
@@ -62,7 +57,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
62
57
|
throw new UnknownPlan(planSku)
|
|
63
58
|
}
|
|
64
59
|
|
|
65
|
-
return fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(result)
|
|
60
|
+
return configHelper.fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(result)
|
|
66
61
|
},
|
|
67
62
|
|
|
68
63
|
allPlans: async productSku => {
|
|
@@ -74,7 +69,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
74
69
|
})
|
|
75
70
|
|
|
76
71
|
return items.map(
|
|
77
|
-
item => fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(item)
|
|
72
|
+
item => configHelper.fromConfigRecord<ConfigRecord, ProductPlan & ResourceRecord>(item)
|
|
78
73
|
)
|
|
79
74
|
},
|
|
80
75
|
|
|
@@ -100,7 +95,7 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
100
95
|
for (let id of idsToCheck) {
|
|
101
96
|
const l10n = await configRes.load(id)
|
|
102
97
|
if (l10n != null) {
|
|
103
|
-
return fromConfigRecord<ConfigRecord, Localization & ResourceRecord>(l10n)
|
|
98
|
+
return configHelper.fromConfigRecord<ConfigRecord, Localization & ResourceRecord>(l10n)
|
|
104
99
|
}
|
|
105
100
|
}
|
|
106
101
|
|
|
@@ -118,6 +113,28 @@ export const makePaymentService = (alias: string = DEFAULT_ALIAS): PaymentServic
|
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
return auth.profileId
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
pricingPolicy: async () => {
|
|
119
|
+
const context = service.assertCtx() as Context
|
|
120
|
+
const configRes = context.getConfigResource()
|
|
121
|
+
|
|
122
|
+
const record = await configRes.load(PRICING_POLICY_RECORD_ID)
|
|
123
|
+
|
|
124
|
+
return record == null
|
|
125
|
+
? DEFAULT_PRICING_POLICY
|
|
126
|
+
: configHelper.fromConfigRecord<ConfigRecord, PricingPolicy & ResourceRecord>(record)
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
consumerRightsPolicy: async () => {
|
|
130
|
+
const context = service.assertCtx() as Context
|
|
131
|
+
const configRes = context.getConfigResource()
|
|
132
|
+
|
|
133
|
+
const record = await configRes.load(CONSUMER_RIGHTS_RECORD_ID)
|
|
134
|
+
|
|
135
|
+
return record == null
|
|
136
|
+
? null
|
|
137
|
+
: configHelper.fromConfigRecord<ConfigRecord, ConsumerRightsPolicy & ResourceRecord>(record)
|
|
121
138
|
}
|
|
122
139
|
}, service => async () => {
|
|
123
140
|
service.initialized = true
|