@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/entitlement.ts
CHANGED
|
@@ -1,122 +1,86 @@
|
|
|
1
|
-
import { gate } from '@owlmeans/entrypoint'
|
|
2
|
-
import type { CommonEntrypointOptions } from '@owlmeans/entrypoint'
|
|
3
1
|
import type { PermissionSet } from '@owlmeans/auth'
|
|
2
|
+
import { CAPABILITY_LIMIT_SCOPE } from './consts.js'
|
|
3
|
+
import type { EntitlementParam } from './types.js'
|
|
4
|
+
import type { EntitlementParamHelper } from './entitlement/types.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export const createEntitlementParamHelper = (): EntitlementParamHelper => {
|
|
7
|
+
const parseEntitlementParam = (param: string): EntitlementParam => {
|
|
8
|
+
const [scoped, floor] = param.split('>=')
|
|
9
|
+
const colon = scoped.indexOf(':')
|
|
10
|
+
const parsed: EntitlementParam = colon >= 0
|
|
11
|
+
? { scope: scoped.slice(0, colon), permission: scoped.slice(colon + 1) }
|
|
12
|
+
: { permission: scoped }
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* number, and the first purchase that spent the quota would take the feature with it.
|
|
19
|
-
*/
|
|
20
|
-
export const CAPABILITY_FEATURE_SCOPE = 'feature'
|
|
21
|
-
|
|
22
|
-
export interface EntitlementParam {
|
|
23
|
-
/** Capability scope. Absent means any scope. */
|
|
24
|
-
scope?: string
|
|
25
|
-
/** The permission key inside `PermissionSet.permissions`. */
|
|
26
|
-
permission: string
|
|
27
|
-
/** Numeric floor: `credits>=100` passes when the held value is a number at least that big. */
|
|
28
|
-
atLeast?: number
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Grammar: `[<scope>:]<permission>[>=<n>]`.
|
|
33
|
-
*
|
|
34
|
-
* `feature:branding--whitelabel` · `renewable:credits>=100` · `production--standalone`
|
|
35
|
-
*
|
|
36
|
-
* `@` is deliberately not part of it: that is `@owlmeans/iam`'s resource-selector syntax, and a
|
|
37
|
-
* grammar that reused it would make two different things look the same in a route declaration.
|
|
38
|
-
*/
|
|
39
|
-
export const parseEntitlementParam = (param: string): EntitlementParam => {
|
|
40
|
-
const [scoped, floor] = param.split('>=')
|
|
41
|
-
const colon = scoped.indexOf(':')
|
|
42
|
-
const parsed: EntitlementParam = colon >= 0
|
|
43
|
-
? { scope: scoped.slice(0, colon), permission: scoped.slice(colon + 1) }
|
|
44
|
-
: { permission: scoped }
|
|
45
|
-
|
|
46
|
-
if (floor != null) {
|
|
47
|
-
const value = Number.parseFloat(floor)
|
|
48
|
-
if (!Number.isNaN(value)) {
|
|
49
|
-
parsed.atLeast = value
|
|
14
|
+
if (floor != null) {
|
|
15
|
+
const value = Number.parseFloat(floor)
|
|
16
|
+
if (!Number.isNaN(value)) {
|
|
17
|
+
parsed.atLeast = value
|
|
18
|
+
}
|
|
50
19
|
}
|
|
51
|
-
}
|
|
52
20
|
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export const formatEntitlementParam = (parsed: EntitlementParam): string =>
|
|
57
|
-
`${parsed.scope != null ? `${parsed.scope}:` : ''}${parsed.permission}` +
|
|
58
|
-
`${parsed.atLeast != null ? `>=${parsed.atLeast}` : ''}`
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Whether a union of capability sets satisfies one requirement.
|
|
62
|
-
*
|
|
63
|
-
* Pure, and shared by the server gate and the browser: the UI has to know whether to render a
|
|
64
|
-
* control disabled, and a second implementation of the rule for the client would eventually
|
|
65
|
-
* disagree with the one that actually refuses.
|
|
66
|
-
*
|
|
67
|
-
* A malformed parameter answers `false` rather than throwing. A gate that crashed on a typo would
|
|
68
|
-
* take down the endpoint it guards, which is a strictly worse failure than refusing it.
|
|
69
|
-
*/
|
|
70
|
-
export const hasEntitlement = (
|
|
71
|
-
capabilities: PermissionSet[] | undefined, param: string
|
|
72
|
-
): boolean => {
|
|
73
|
-
if (capabilities == null || capabilities.length < 1) {
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
const { scope, permission, atLeast } = parseEntitlementParam(param)
|
|
77
|
-
if (permission === '') {
|
|
78
|
-
return false
|
|
21
|
+
return parsed
|
|
79
22
|
}
|
|
80
23
|
|
|
81
|
-
|
|
82
|
-
|
|
24
|
+
const formatEntitlementParam = (parsed: EntitlementParam): string =>
|
|
25
|
+
`${parsed.scope != null ? `${parsed.scope}:` : ''}${parsed.permission}` +
|
|
26
|
+
`${parsed.atLeast != null ? `>=${parsed.atLeast}` : ''}`
|
|
27
|
+
|
|
28
|
+
const hasEntitlement = (
|
|
29
|
+
capabilities: PermissionSet[] | undefined, param: string
|
|
30
|
+
): boolean => {
|
|
31
|
+
if (capabilities == null || capabilities.length < 1) {
|
|
83
32
|
return false
|
|
84
33
|
}
|
|
85
|
-
|
|
86
|
-
|
|
34
|
+
if (typeof param !== 'string') {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
const { scope, permission, atLeast } = parseEntitlementParam(param)
|
|
38
|
+
// A limit is never a capability: its room is counted, not granted.
|
|
39
|
+
if (permission === '' || scope === CAPABILITY_LIMIT_SCOPE) {
|
|
87
40
|
return false
|
|
88
41
|
}
|
|
89
42
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
43
|
+
return capabilities.some(set => {
|
|
44
|
+
if (scope != null && set.scope !== scope) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
const held = set.permissions?.[permission]
|
|
48
|
+
if (held == null || held === false) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A floor is a numeric question: a boolean flag, however true, does not answer it.
|
|
53
|
+
return atLeast != null ? typeof held === 'number' && held >= atLeast : true
|
|
54
|
+
})
|
|
55
|
+
}
|
|
94
56
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
57
|
+
const entitlementList = (capabilities: PermissionSet[] | undefined): string[] => {
|
|
58
|
+
const list: string[] = []
|
|
59
|
+
for (const set of capabilities ?? []) {
|
|
60
|
+
for (const [permission, value] of Object.entries(set.permissions ?? {})) {
|
|
61
|
+
if (value == null || value === false) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
list.push(formatEntitlementParam({ scope: set.scope, permission }))
|
|
102
65
|
}
|
|
103
|
-
list.push(formatEntitlementParam({ scope: set.scope, permission }))
|
|
104
66
|
}
|
|
67
|
+
|
|
68
|
+
return [...new Set(list)]
|
|
105
69
|
}
|
|
106
70
|
|
|
107
|
-
return
|
|
71
|
+
return { parseEntitlementParam, formatEntitlementParam, hasEntitlement, entitlementList }
|
|
108
72
|
}
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
74
|
+
export const entitlementParamHelper = createEntitlementParamHelper()
|
|
75
|
+
|
|
76
|
+
/** @deprecated compat:factory-refactor — use `entitlementParamHelper.parseEntitlementParam(…)` */
|
|
77
|
+
export const parseEntitlementParam = (param: string): EntitlementParam =>
|
|
78
|
+
entitlementParamHelper.parseEntitlementParam(param)
|
|
79
|
+
|
|
80
|
+
/** @deprecated compat:factory-refactor — use `entitlementParamHelper.formatEntitlementParam(…)` */
|
|
81
|
+
export const formatEntitlementParam = (parsed: EntitlementParam): string =>
|
|
82
|
+
entitlementParamHelper.formatEntitlementParam(parsed)
|
|
83
|
+
|
|
84
|
+
/** @deprecated compat:factory-refactor — use `entitlementParamHelper.hasEntitlement(…)` */
|
|
85
|
+
export const hasEntitlement = (capabilities: PermissionSet[] | undefined, param: string): boolean =>
|
|
86
|
+
entitlementParamHelper.hasEntitlement(capabilities, param)
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
import { ResilientError } from '@owlmeans/error'
|
|
3
|
+
import { AuthForbidden } from '@owlmeans/auth'
|
|
4
|
+
import { BILLING_COUNTRY_LOCKED_MARKER, CANCELLATION_UNAVAILABLE_MARKER, CAPABILITY_REQUIRED_MARKER, CHECKOUT_LIMIT_MARKER, LIMIT_EXHAUSTED_FIELDS, LIMIT_EXHAUSTED_MARKER, PERFORMANCE_CONSENT_MARKER, SUBSCRIPTION_START_MARKER, WITHDRAWAL_UNAVAILABLE_MARKER } from './consts.local.js'
|
|
5
|
+
import type { BillingCountryLockedDetails, CheckoutLimitExceededDetails, LimitExhaustedDetails, PerformanceConsentRequiredDetails } from './types.js'
|
|
3
6
|
|
|
4
7
|
export class PaymentError extends ResilientError {
|
|
5
8
|
public static override typeName: string = 'PaymentError'
|
|
@@ -36,6 +39,22 @@ export class PaygateMappingError extends PaygateError {
|
|
|
36
39
|
}
|
|
37
40
|
}
|
|
38
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A webhook delivery whose signature is missing or verifies against no known secret. The sender is
|
|
44
|
+
* refused as a client error (400): a forged or unsigned POST at a public hook is not a server fault,
|
|
45
|
+
* and answering 500 would page operators for it and invite the sender to retry it.
|
|
46
|
+
*/
|
|
47
|
+
export class PaygateSignatureError extends PaygateError {
|
|
48
|
+
public static override typeName: string = `${PaygateError.typeName}Signature`
|
|
49
|
+
/** Read by the HTTP boundary (`server-api` `declaredErrorStatus`). */
|
|
50
|
+
public static httpStatus: number = 400
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
super('signature')
|
|
54
|
+
this.type = PaygateSignatureError.typeName
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
export class ProductError extends PaymentError {
|
|
40
59
|
public static override typeName: string = `${PaymentError.typeName}Product`
|
|
41
60
|
|
|
@@ -90,13 +109,439 @@ export class UnknownSubscription extends SubscriptionError {
|
|
|
90
109
|
}
|
|
91
110
|
}
|
|
92
111
|
|
|
112
|
+
export class LimitUnknown extends ProductError {
|
|
113
|
+
public static override typeName: string = `${ProductError.typeName}LimitUnknown`
|
|
114
|
+
|
|
115
|
+
constructor(message: string = 'error') {
|
|
116
|
+
super(`limit-unknown:${message}`)
|
|
117
|
+
this.type = LimitUnknown.typeName
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export class LimitMisdeclared extends ProductError {
|
|
122
|
+
public static override typeName: string = `${ProductError.typeName}LimitMisdeclared`
|
|
123
|
+
|
|
124
|
+
constructor(message: string = 'error') {
|
|
125
|
+
super(`limit-misdeclared:${message}`)
|
|
126
|
+
this.type = LimitMisdeclared.typeName
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export class PlanRequired extends ProductError {
|
|
131
|
+
public static override typeName: string = `${ProductError.typeName}PlanRequired`
|
|
132
|
+
|
|
133
|
+
constructor(message: string = 'error') {
|
|
134
|
+
super(`plan-required:${message}`)
|
|
135
|
+
this.type = PlanRequired.typeName
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export class PlanRankConflict extends ProductError {
|
|
140
|
+
public static override typeName: string = `${ProductError.typeName}PlanRankConflict`
|
|
141
|
+
|
|
142
|
+
constructor(message: string = 'error') {
|
|
143
|
+
super(`plan-rank-conflict:${message}`)
|
|
144
|
+
this.type = PlanRankConflict.typeName
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export class WebhookSetupError extends PaygateError {
|
|
149
|
+
public static override typeName: string = `${PaygateError.typeName}WebhookSetup`
|
|
150
|
+
|
|
151
|
+
constructor(message: string = 'error') {
|
|
152
|
+
super(`webhook-setup:${message}`)
|
|
153
|
+
this.type = WebhookSetupError.typeName
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* The entity has nothing the requested portal flow can act on — no paygate customer, no entitling
|
|
159
|
+
* subscription, no plan or item to change to. The entity's own state, not a fault: an HTTP boundary
|
|
160
|
+
* that honours `httpStatus` (`@owlmeans/server-api`) answers 409 Conflict.
|
|
161
|
+
*/
|
|
162
|
+
export class PortalUnavailable extends PaygateError {
|
|
163
|
+
public static override typeName: string = `${PaygateError.typeName}PortalUnavailable`
|
|
164
|
+
public static httpStatus = 409
|
|
165
|
+
|
|
166
|
+
constructor(message: string = 'error') {
|
|
167
|
+
super(`portal-unavailable:${message}`)
|
|
168
|
+
this.type = PortalUnavailable.typeName
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The entity's plan does not allow the request.
|
|
174
|
+
*
|
|
175
|
+
* Extends `AuthForbidden` so an HTTP boundary answers 403, never 500. Only `type` and `message`
|
|
176
|
+
* survive a marshal round trip, so each subclass packs its fields into the message and rebuilds
|
|
177
|
+
* them in `finalizeUnmarshal()`.
|
|
178
|
+
*/
|
|
179
|
+
export class EntitlementRefusal extends AuthForbidden {
|
|
180
|
+
public static override typeName: string = 'EntitlementRefusal'
|
|
181
|
+
|
|
182
|
+
constructor(message: string = 'error') {
|
|
183
|
+
super(`entitlement:${message}`)
|
|
184
|
+
this.type = EntitlementRefusal.typeName
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** None of the capability parameters (OR'd) is granted. Message marker `capability-required:<a|b>`. */
|
|
189
|
+
export class CapabilityRequired extends EntitlementRefusal {
|
|
190
|
+
public static override typeName: string = `${EntitlementRefusal.typeName}CapabilityRequired`
|
|
191
|
+
|
|
192
|
+
/** The refused parameters, as the gate took them. */
|
|
193
|
+
public params: string[] = []
|
|
194
|
+
|
|
195
|
+
constructor(params: string | string[] = []) {
|
|
196
|
+
const list = (Array.isArray(params) ? params : [params]).filter(param => param !== '')
|
|
197
|
+
super(`${CAPABILITY_REQUIRED_MARKER}${list.join('|')}`)
|
|
198
|
+
this.type = CapabilityRequired.typeName
|
|
199
|
+
this.applyFields()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private applyFields(): void {
|
|
203
|
+
const at = this.message.indexOf(CAPABILITY_REQUIRED_MARKER)
|
|
204
|
+
const packed = at < 0 ? '' : this.message.slice(at + CAPABILITY_REQUIRED_MARKER.length)
|
|
205
|
+
this.params = packed.split('|').filter(param => param !== '')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
override finalizeUnmarshal(): void {
|
|
209
|
+
this.applyFields()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A limit has no room left. Message marker `limit-exhausted:<key>:<used>/<limit>[:<resetsAt ISO>]`.
|
|
216
|
+
*/
|
|
217
|
+
export class LimitExhausted extends EntitlementRefusal {
|
|
218
|
+
public static override typeName: string = `${EntitlementRefusal.typeName}LimitExhausted`
|
|
219
|
+
|
|
220
|
+
public limitKey: string = ''
|
|
221
|
+
public used: number = 0
|
|
222
|
+
public limit: number = 0
|
|
223
|
+
public resetsAt?: Date
|
|
224
|
+
|
|
225
|
+
/** The packed marker body of a refusal. */
|
|
226
|
+
static encode(details: LimitExhaustedDetails): string {
|
|
227
|
+
const resetsAt = details.resetsAt != null && !Number.isNaN(details.resetsAt.getTime())
|
|
228
|
+
? `:${details.resetsAt.toISOString()}` : ''
|
|
229
|
+
return `${details.key}:${details.used}/${details.limit}${resetsAt}`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
constructor(details: LimitExhaustedDetails)
|
|
233
|
+
/** A packed marker body (`LimitExhausted.encode`) or a marshaled message — the error registry's path. */
|
|
234
|
+
constructor(message: string)
|
|
235
|
+
constructor(details: LimitExhaustedDetails | string = 'error') {
|
|
236
|
+
super(`${LIMIT_EXHAUSTED_MARKER}${typeof details === 'string' ? details : LimitExhausted.encode(details)}`)
|
|
237
|
+
this.type = LimitExhausted.typeName
|
|
238
|
+
this.applyFields()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private applyFields(): void {
|
|
242
|
+
const at = this.message.indexOf(LIMIT_EXHAUSTED_MARKER)
|
|
243
|
+
const match = at < 0 ? null
|
|
244
|
+
: LIMIT_EXHAUSTED_FIELDS.exec(this.message.slice(at + LIMIT_EXHAUSTED_MARKER.length))
|
|
245
|
+
if (match == null) {
|
|
246
|
+
return
|
|
247
|
+
}
|
|
248
|
+
const [, key, used, limit, resetsAt] = match
|
|
249
|
+
this.limitKey = key
|
|
250
|
+
this.used = Number(used) || 0
|
|
251
|
+
this.limit = Number(limit) || 0
|
|
252
|
+
const date = resetsAt != null ? new Date(resetsAt) : null
|
|
253
|
+
this.resetsAt = date != null && !Number.isNaN(date.getTime()) ? date : undefined
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
override finalizeUnmarshal(): void {
|
|
257
|
+
this.applyFields()
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* A consumer-rights fault — a misdeclared policy (`policy:<field>`), a missing usage meter or
|
|
263
|
+
* mailer, a copy template without a value (`copy:<path>`). Declares no status: 500.
|
|
264
|
+
*/
|
|
265
|
+
export class ConsumerRightsError extends PaymentError {
|
|
266
|
+
public static override typeName: string = `${PaymentError.typeName}ConsumerRights`
|
|
267
|
+
|
|
268
|
+
constructor(message: string = 'error') {
|
|
269
|
+
super(`consumer-rights:${message}`)
|
|
270
|
+
this.type = ConsumerRightsError.typeName
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* The consumer's own position refuses the request — never a fault. Deliberately NOT an
|
|
276
|
+
* `AuthForbidden`: an HTTP boundary tests that family first, and a 403 would hide the declared
|
|
277
|
+
* 428/409 a client acts on.
|
|
278
|
+
*/
|
|
279
|
+
export class ConsumerRightsRefusal extends ConsumerRightsError {
|
|
280
|
+
public static override typeName: string = `${ConsumerRightsError.typeName}Refusal`
|
|
281
|
+
|
|
282
|
+
constructor(message: string = 'error') {
|
|
283
|
+
super(message)
|
|
284
|
+
this.type = ConsumerRightsRefusal.typeName
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The packed body after the LAST marker occurrence — a registry rebuild doubles the prefix. */
|
|
289
|
+
const packedAfter = (message: string, marker: string): string | null => {
|
|
290
|
+
const at = message.lastIndexOf(marker)
|
|
291
|
+
|
|
292
|
+
return at < 0 ? null : message.slice(at + marker.length)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const dateOf = (value: string | undefined): Date | undefined => {
|
|
296
|
+
const date = value != null && value !== '' ? new Date(value) : null
|
|
297
|
+
|
|
298
|
+
return date != null && !Number.isNaN(date.getTime()) ? date : undefined
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const isoOf = (date: Date | undefined): string =>
|
|
302
|
+
date != null && !Number.isNaN(date.getTime()) ? `:${date.toISOString()}` : ''
|
|
303
|
+
|
|
304
|
+
const decode = (value: string): string => {
|
|
305
|
+
try {
|
|
306
|
+
return decodeURIComponent(value)
|
|
307
|
+
} catch {
|
|
308
|
+
return value
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Billed work would spend credits of a purchase still inside its withdrawal window, and the
|
|
314
|
+
* consumer has not expressly requested performance. HTTP 428. Marker
|
|
315
|
+
* `performance-consent-required:<pending>[:<deadline ISO>]`.
|
|
316
|
+
*/
|
|
317
|
+
export class PerformanceConsentRequired extends ConsumerRightsRefusal {
|
|
318
|
+
public static override typeName: string = `${ConsumerRightsRefusal.typeName}PerformanceConsentRequired`
|
|
319
|
+
public static httpStatus = 428
|
|
320
|
+
|
|
321
|
+
public pending: number = 0
|
|
322
|
+
public deadline?: Date
|
|
323
|
+
|
|
324
|
+
static encode(details: PerformanceConsentRequiredDetails): string {
|
|
325
|
+
return `${Math.max(0, Math.floor(details.pending))}${isoOf(details.deadline)}`
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
constructor(details: PerformanceConsentRequiredDetails)
|
|
329
|
+
/** A packed marker body (`encode`) or a marshaled message — the error registry's path. */
|
|
330
|
+
constructor(message: string)
|
|
331
|
+
constructor(details: PerformanceConsentRequiredDetails | string = 'error') {
|
|
332
|
+
super(`${PERFORMANCE_CONSENT_MARKER}${typeof details === 'string' ? details : PerformanceConsentRequired.encode(details)}`)
|
|
333
|
+
this.type = PerformanceConsentRequired.typeName
|
|
334
|
+
this.applyFields()
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private applyFields(): void {
|
|
338
|
+
const match = /^(\d+)(?::(.+))?$/.exec(packedAfter(this.message, PERFORMANCE_CONSENT_MARKER) ?? '')
|
|
339
|
+
if (match == null) {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
this.pending = Number(match[1])
|
|
343
|
+
this.deadline = dateOf(match[2])
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
override finalizeUnmarshal(): void {
|
|
347
|
+
this.applyFields()
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* A subscription checkout needs a fresh express start request bound to this plan. HTTP 428.
|
|
353
|
+
* Marker `subscription-start-required:<encodeURIComponent(planSku)>`.
|
|
354
|
+
*/
|
|
355
|
+
export class SubscriptionStartRequired extends ConsumerRightsRefusal {
|
|
356
|
+
public static override typeName: string = `${ConsumerRightsRefusal.typeName}SubscriptionStartRequired`
|
|
357
|
+
public static httpStatus = 428
|
|
358
|
+
|
|
359
|
+
public planSku: string = ''
|
|
360
|
+
|
|
361
|
+
static encode(planSku: string): string {
|
|
362
|
+
return encodeURIComponent(planSku)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** `planSku` as is, or a packed marker body / marshaled message (the registry's path). */
|
|
366
|
+
constructor(planSku: string | { planSku: string } = 'error') {
|
|
367
|
+
super(`${SUBSCRIPTION_START_MARKER}${typeof planSku === 'string' ? SubscriptionStartRequired.encodeLoose(planSku) : SubscriptionStartRequired.encode(planSku.planSku)}`)
|
|
368
|
+
this.type = SubscriptionStartRequired.typeName
|
|
369
|
+
this.applyFields()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** A string that already is a marker (the registry path) passes through unencoded. */
|
|
373
|
+
private static encodeLoose(value: string): string {
|
|
374
|
+
return value.includes(SUBSCRIPTION_START_MARKER) ? value : SubscriptionStartRequired.encode(value)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private applyFields(): void {
|
|
378
|
+
const packed = packedAfter(this.message, SUBSCRIPTION_START_MARKER)
|
|
379
|
+
if (packed != null) {
|
|
380
|
+
this.planSku = decode(packed)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
override finalizeUnmarshal(): void {
|
|
385
|
+
this.applyFields()
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The entity's billing country is fixed and the request declared another. HTTP 409. Marker
|
|
391
|
+
* `billing-country-locked:<country>[:<requested>]`.
|
|
392
|
+
*/
|
|
393
|
+
export class BillingCountryLocked extends ConsumerRightsRefusal {
|
|
394
|
+
public static override typeName: string = `${ConsumerRightsRefusal.typeName}BillingCountryLocked`
|
|
395
|
+
public static httpStatus = 409
|
|
396
|
+
|
|
397
|
+
public country: string = ''
|
|
398
|
+
public requested?: string
|
|
399
|
+
|
|
400
|
+
static encode(details: BillingCountryLockedDetails): string {
|
|
401
|
+
return details.requested != null && details.requested !== ''
|
|
402
|
+
? `${details.country}:${details.requested}` : details.country
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
constructor(details: BillingCountryLockedDetails)
|
|
406
|
+
constructor(message: string)
|
|
407
|
+
constructor(details: BillingCountryLockedDetails | string = 'error') {
|
|
408
|
+
super(`${BILLING_COUNTRY_LOCKED_MARKER}${typeof details === 'string' ? details : BillingCountryLocked.encode(details)}`)
|
|
409
|
+
this.type = BillingCountryLocked.typeName
|
|
410
|
+
this.applyFields()
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private applyFields(): void {
|
|
414
|
+
const match = /^([A-Za-z]{2})(?::([A-Za-z]{2}))?$/.exec(packedAfter(this.message, BILLING_COUNTRY_LOCKED_MARKER) ?? '')
|
|
415
|
+
if (match == null) {
|
|
416
|
+
return
|
|
417
|
+
}
|
|
418
|
+
this.country = match[1]
|
|
419
|
+
this.requested = match[2]
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
override finalizeUnmarshal(): void {
|
|
423
|
+
this.applyFields()
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The named purchase cannot be withdrawn from (`WithdrawalUnavailableReason`). HTTP 409. Marker
|
|
429
|
+
* `withdrawal-unavailable:<reason>`. Never raised on the public page, which discloses nothing.
|
|
430
|
+
*/
|
|
431
|
+
export class WithdrawalUnavailable extends ConsumerRightsRefusal {
|
|
432
|
+
public static override typeName: string = `${ConsumerRightsRefusal.typeName}WithdrawalUnavailable`
|
|
433
|
+
public static httpStatus = 409
|
|
434
|
+
|
|
435
|
+
public reason: string = ''
|
|
436
|
+
|
|
437
|
+
constructor(reason: string = 'error') {
|
|
438
|
+
super(`${WITHDRAWAL_UNAVAILABLE_MARKER}${reason}`)
|
|
439
|
+
this.type = WithdrawalUnavailable.typeName
|
|
440
|
+
this.applyFields()
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private applyFields(): void {
|
|
444
|
+
this.reason = /^[\w-]*/.exec(packedAfter(this.message, WITHDRAWAL_UNAVAILABLE_MARKER) ?? '')?.[0] ?? ''
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
override finalizeUnmarshal(): void {
|
|
448
|
+
this.applyFields()
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Nothing to cancel in-app (`CancellationUnavailableReason`). HTTP 409. Marker
|
|
454
|
+
* `cancellation-unavailable:<reason>`.
|
|
455
|
+
*/
|
|
456
|
+
export class CancellationUnavailable extends ConsumerRightsRefusal {
|
|
457
|
+
public static override typeName: string = `${ConsumerRightsRefusal.typeName}CancellationUnavailable`
|
|
458
|
+
public static httpStatus = 409
|
|
459
|
+
|
|
460
|
+
public reason: string = ''
|
|
461
|
+
|
|
462
|
+
constructor(reason: string = 'error') {
|
|
463
|
+
super(`${CANCELLATION_UNAVAILABLE_MARKER}${reason}`)
|
|
464
|
+
this.type = CancellationUnavailable.typeName
|
|
465
|
+
this.applyFields()
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private applyFields(): void {
|
|
469
|
+
this.reason = /^[\w-]*/.exec(packedAfter(this.message, CANCELLATION_UNAVAILABLE_MARKER) ?? '')?.[0] ?? ''
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
override finalizeUnmarshal(): void {
|
|
473
|
+
this.applyFields()
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* The amount is above what this entity may buy now (a narrowing of the plan's policy). HTTP 409.
|
|
479
|
+
* Marker `checkout-limit-exceeded:<encodeURIComponent(reason)>:<maximumMinor>:<currency>[:<ISO>]`.
|
|
480
|
+
* A payment refusal, not a consumer-rights one.
|
|
481
|
+
*/
|
|
482
|
+
export class CheckoutLimitExceeded extends PaymentError {
|
|
483
|
+
public static override typeName: string = `${PaymentError.typeName}CheckoutLimitExceeded`
|
|
484
|
+
public static httpStatus = 409
|
|
485
|
+
|
|
486
|
+
public reason: string = ''
|
|
487
|
+
public maximumMinor: number = 0
|
|
488
|
+
public currency: string = ''
|
|
489
|
+
public resetsAt?: Date
|
|
490
|
+
|
|
491
|
+
static encode(details: CheckoutLimitExceededDetails): string {
|
|
492
|
+
return `${encodeURIComponent(details.reason)}:${Math.max(0, Math.floor(details.maximumMinor))}`
|
|
493
|
+
+ `:${details.currency.toLowerCase()}${isoOf(details.resetsAt)}`
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
constructor(details: CheckoutLimitExceededDetails)
|
|
497
|
+
constructor(message: string)
|
|
498
|
+
constructor(details: CheckoutLimitExceededDetails | string = 'error') {
|
|
499
|
+
super(`${CHECKOUT_LIMIT_MARKER}${typeof details === 'string' ? details : CheckoutLimitExceeded.encode(details)}`)
|
|
500
|
+
this.type = CheckoutLimitExceeded.typeName
|
|
501
|
+
this.applyFields()
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private applyFields(): void {
|
|
505
|
+
const match = /^([^:]*):(\d+):([a-z]{3})(?::(.+))?$/.exec(packedAfter(this.message, CHECKOUT_LIMIT_MARKER) ?? '')
|
|
506
|
+
if (match == null) {
|
|
507
|
+
return
|
|
508
|
+
}
|
|
509
|
+
this.reason = decode(match[1])
|
|
510
|
+
this.maximumMinor = Number(match[2])
|
|
511
|
+
this.currency = match[3]
|
|
512
|
+
this.resetsAt = dateOf(match[4])
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
override finalizeUnmarshal(): void {
|
|
516
|
+
this.applyFields()
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
93
520
|
ResilientError.registerErrorClass(PaymentError)
|
|
94
521
|
ResilientError.registerErrorClass(PaygateError)
|
|
95
522
|
ResilientError.registerErrorClass(UnknownPaygate)
|
|
96
523
|
ResilientError.registerErrorClass(PaygateMappingError)
|
|
524
|
+
ResilientError.registerErrorClass(PaygateSignatureError)
|
|
97
525
|
ResilientError.registerErrorClass(ProductError)
|
|
98
526
|
ResilientError.registerErrorClass(UnknownProduct)
|
|
99
527
|
ResilientError.registerErrorClass(UnknownPlan)
|
|
100
528
|
ResilientError.registerErrorClass(PaymentIdentificationError)
|
|
101
529
|
ResilientError.registerErrorClass(SubscriptionError)
|
|
102
530
|
ResilientError.registerErrorClass(UnknownSubscription)
|
|
531
|
+
ResilientError.registerErrorClass(LimitUnknown)
|
|
532
|
+
ResilientError.registerErrorClass(LimitMisdeclared)
|
|
533
|
+
ResilientError.registerErrorClass(PlanRequired)
|
|
534
|
+
ResilientError.registerErrorClass(PlanRankConflict)
|
|
535
|
+
ResilientError.registerErrorClass(WebhookSetupError)
|
|
536
|
+
ResilientError.registerErrorClass(PortalUnavailable)
|
|
537
|
+
ResilientError.registerErrorClass(EntitlementRefusal)
|
|
538
|
+
ResilientError.registerErrorClass(CapabilityRequired)
|
|
539
|
+
ResilientError.registerErrorClass(LimitExhausted)
|
|
540
|
+
ResilientError.registerErrorClass(ConsumerRightsError)
|
|
541
|
+
ResilientError.registerErrorClass(ConsumerRightsRefusal)
|
|
542
|
+
ResilientError.registerErrorClass(PerformanceConsentRequired)
|
|
543
|
+
ResilientError.registerErrorClass(SubscriptionStartRequired)
|
|
544
|
+
ResilientError.registerErrorClass(BillingCountryLocked)
|
|
545
|
+
ResilientError.registerErrorClass(WithdrawalUnavailable)
|
|
546
|
+
ResilientError.registerErrorClass(CancellationUnavailable)
|
|
547
|
+
ResilientError.registerErrorClass(CheckoutLimitExceeded)
|