@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.
Files changed (366) hide show
  1. package/README.md +95 -57
  2. package/agent-meta/manifest.json +10 -3
  3. package/agent-meta/skills/entitlements/SKILL.md +159 -0
  4. package/agent-meta/skills/payment/SKILL.md +491 -28
  5. package/build/advertise.d.ts +2 -0
  6. package/build/advertise.d.ts.map +1 -0
  7. package/build/advertise.js +10 -0
  8. package/build/advertise.js.map +1 -0
  9. package/build/checkout/consts.d.ts +2 -0
  10. package/build/checkout/consts.d.ts.map +1 -0
  11. package/build/checkout/consts.js +2 -0
  12. package/build/checkout/consts.js.map +1 -0
  13. package/build/checkout/index.d.ts +6 -0
  14. package/build/checkout/index.d.ts.map +1 -0
  15. package/build/checkout/index.js +4 -0
  16. package/build/checkout/index.js.map +1 -0
  17. package/build/checkout/narrow/types.d.ts +25 -0
  18. package/build/checkout/narrow/types.d.ts.map +1 -0
  19. package/build/checkout/narrow/types.js +2 -0
  20. package/build/checkout/narrow/types.js.map +1 -0
  21. package/build/checkout/narrow.d.ts +8 -0
  22. package/build/checkout/narrow.d.ts.map +1 -0
  23. package/build/checkout/narrow.js +47 -0
  24. package/build/checkout/narrow.js.map +1 -0
  25. package/build/checkout/protocols.d.ts +8 -0
  26. package/build/checkout/protocols.d.ts.map +1 -0
  27. package/build/checkout/protocols.js +28 -0
  28. package/build/checkout/protocols.js.map +1 -0
  29. package/build/checkout/types.d.ts +29 -0
  30. package/build/checkout/types.d.ts.map +1 -0
  31. package/build/checkout/types.js +2 -0
  32. package/build/checkout/types.js.map +1 -0
  33. package/build/consts.d.ts +242 -25
  34. package/build/consts.d.ts.map +1 -1
  35. package/build/consts.js +377 -24
  36. package/build/consts.js.map +1 -1
  37. package/build/consts.local.d.ts +20 -0
  38. package/build/consts.local.d.ts.map +1 -0
  39. package/build/consts.local.js +23 -0
  40. package/build/consts.local.js.map +1 -0
  41. package/build/consumer/cancel.d.ts +11 -0
  42. package/build/consumer/cancel.d.ts.map +1 -0
  43. package/build/consumer/cancel.js +33 -0
  44. package/build/consumer/cancel.js.map +1 -0
  45. package/build/consumer/consts.d.ts +20 -0
  46. package/build/consumer/consts.d.ts.map +1 -0
  47. package/build/consumer/consts.js +39 -0
  48. package/build/consumer/consts.js.map +1 -0
  49. package/build/consumer/consts.local.d.ts +7 -0
  50. package/build/consumer/consts.local.d.ts.map +1 -0
  51. package/build/consumer/consts.local.js +13 -0
  52. package/build/consumer/consts.local.js.map +1 -0
  53. package/build/consumer/copy/types.d.ts +56 -0
  54. package/build/consumer/copy/types.d.ts.map +1 -0
  55. package/build/consumer/copy/types.js +2 -0
  56. package/build/consumer/copy/types.js.map +1 -0
  57. package/build/consumer/copy.d.ts +22 -0
  58. package/build/consumer/copy.d.ts.map +1 -0
  59. package/build/consumer/copy.js +87 -0
  60. package/build/consumer/copy.js.map +1 -0
  61. package/build/consumer/deadline/types.d.ts +30 -0
  62. package/build/consumer/deadline/types.d.ts.map +1 -0
  63. package/build/consumer/deadline/types.js +2 -0
  64. package/build/consumer/deadline/types.js.map +1 -0
  65. package/build/consumer/deadline.d.ts +12 -0
  66. package/build/consumer/deadline.d.ts.map +1 -0
  67. package/build/consumer/deadline.js +44 -0
  68. package/build/consumer/deadline.js.map +1 -0
  69. package/build/consumer/fifo/types.d.ts +17 -0
  70. package/build/consumer/fifo/types.d.ts.map +1 -0
  71. package/build/consumer/fifo/types.js +2 -0
  72. package/build/consumer/fifo/types.js.map +1 -0
  73. package/build/consumer/fifo.d.ts +4 -0
  74. package/build/consumer/fifo.d.ts.map +1 -0
  75. package/build/consumer/fifo.js +34 -0
  76. package/build/consumer/fifo.js.map +1 -0
  77. package/build/consumer/index.d.ts +18 -0
  78. package/build/consumer/index.d.ts.map +1 -0
  79. package/build/consumer/index.js +11 -0
  80. package/build/consumer/index.js.map +1 -0
  81. package/build/consumer/policy/types.d.ts +20 -0
  82. package/build/consumer/policy/types.d.ts.map +1 -0
  83. package/build/consumer/policy/types.js +2 -0
  84. package/build/consumer/policy/types.js.map +1 -0
  85. package/build/consumer/policy.d.ts +17 -0
  86. package/build/consumer/policy.d.ts.map +1 -0
  87. package/build/consumer/policy.js +91 -0
  88. package/build/consumer/policy.js.map +1 -0
  89. package/build/consumer/protocols.d.ts +20 -0
  90. package/build/consumer/protocols.d.ts.map +1 -0
  91. package/build/consumer/protocols.js +70 -0
  92. package/build/consumer/protocols.js.map +1 -0
  93. package/build/consumer/refund/types.d.ts +27 -0
  94. package/build/consumer/refund/types.d.ts.map +1 -0
  95. package/build/consumer/refund/types.js +2 -0
  96. package/build/consumer/refund/types.js.map +1 -0
  97. package/build/consumer/refund.d.ts +9 -0
  98. package/build/consumer/refund.d.ts.map +1 -0
  99. package/build/consumer/refund.js +112 -0
  100. package/build/consumer/refund.js.map +1 -0
  101. package/build/consumer/refusal.d.ts +10 -0
  102. package/build/consumer/refusal.d.ts.map +1 -0
  103. package/build/consumer/refusal.js +35 -0
  104. package/build/consumer/refusal.js.map +1 -0
  105. package/build/consumer/revive/types.d.ts +19 -0
  106. package/build/consumer/revive/types.d.ts.map +1 -0
  107. package/build/consumer/revive/types.js +2 -0
  108. package/build/consumer/revive/types.js.map +1 -0
  109. package/build/consumer/revive.d.ts +13 -0
  110. package/build/consumer/revive.d.ts.map +1 -0
  111. package/build/consumer/revive.js +43 -0
  112. package/build/consumer/revive.js.map +1 -0
  113. package/build/consumer/types.d.ts +196 -0
  114. package/build/consumer/types.d.ts.map +1 -0
  115. package/build/consumer/types.js +2 -0
  116. package/build/consumer/types.js.map +1 -0
  117. package/build/consumer/types.local.d.ts +11 -0
  118. package/build/consumer/types.local.d.ts.map +1 -0
  119. package/build/consumer/types.local.js +2 -0
  120. package/build/consumer/types.local.js.map +1 -0
  121. package/build/countries.d.ts +3 -0
  122. package/build/countries.d.ts.map +1 -0
  123. package/build/countries.js +4 -0
  124. package/build/countries.js.map +1 -0
  125. package/build/entitled.d.ts +14 -0
  126. package/build/entitled.d.ts.map +1 -0
  127. package/build/entitled.js +17 -0
  128. package/build/entitled.js.map +1 -0
  129. package/build/entitlement/types.d.ts +29 -0
  130. package/build/entitlement/types.d.ts.map +1 -0
  131. package/build/entitlement/types.js +2 -0
  132. package/build/entitlement/types.js.map +1 -0
  133. package/build/entitlement.d.ts +7 -55
  134. package/build/entitlement.d.ts.map +1 -1
  135. package/build/entitlement.js +55 -86
  136. package/build/entitlement.js.map +1 -1
  137. package/build/errors.d.ts +189 -0
  138. package/build/errors.d.ts.map +1 -1
  139. package/build/errors.js +361 -0
  140. package/build/errors.js.map +1 -1
  141. package/build/estimate/types.d.ts +34 -0
  142. package/build/estimate/types.d.ts.map +1 -0
  143. package/build/estimate/types.js +2 -0
  144. package/build/estimate/types.js.map +1 -0
  145. package/build/estimate.d.ts +11 -0
  146. package/build/estimate.d.ts.map +1 -0
  147. package/build/estimate.js +76 -0
  148. package/build/estimate.js.map +1 -0
  149. package/build/i18n/consumer-rights/be.json +131 -0
  150. package/build/i18n/consumer-rights/de.json +131 -0
  151. package/build/i18n/consumer-rights/en.json +131 -0
  152. package/build/i18n/consumer-rights/es.json +131 -0
  153. package/build/i18n/consumer-rights/fr.json +131 -0
  154. package/build/i18n/consumer-rights/pl.json +131 -0
  155. package/build/i18n/consumer-rights/ru.json +131 -0
  156. package/build/i18n/consumer-rights/uk.json +131 -0
  157. package/build/i18n/errors/be.json +30 -0
  158. package/build/i18n/errors/de.json +30 -0
  159. package/build/i18n/errors/en.json +30 -0
  160. package/build/i18n/errors/es.json +30 -0
  161. package/build/i18n/errors/fr.json +30 -0
  162. package/build/i18n/errors/pl.json +30 -0
  163. package/build/i18n/errors/ru.json +30 -0
  164. package/build/i18n/errors/uk.json +30 -0
  165. package/build/i18n/fr.json +39 -0
  166. package/build/i18n.js +53 -8
  167. package/build/i18n.js.map +1 -1
  168. package/build/index.d.ts +18 -1
  169. package/build/index.d.ts.map +1 -1
  170. package/build/index.js +11 -1
  171. package/build/index.js.map +1 -1
  172. package/build/limit/types.d.ts +44 -0
  173. package/build/limit/types.d.ts.map +1 -0
  174. package/build/limit/types.js +2 -0
  175. package/build/limit/types.js.map +1 -0
  176. package/build/limit.d.ts +23 -0
  177. package/build/limit.d.ts.map +1 -0
  178. package/build/limit.js +97 -0
  179. package/build/limit.js.map +1 -0
  180. package/build/model/checkout.d.ts +2 -3
  181. package/build/model/checkout.d.ts.map +1 -1
  182. package/build/model/checkout.js +12 -7
  183. package/build/model/checkout.js.map +1 -1
  184. package/build/model/consts.d.ts +15 -0
  185. package/build/model/consts.d.ts.map +1 -0
  186. package/build/model/consts.js +193 -0
  187. package/build/model/consts.js.map +1 -0
  188. package/build/model/consts.local.d.ts +35 -0
  189. package/build/model/consts.local.d.ts.map +1 -0
  190. package/build/model/consts.local.js +65 -0
  191. package/build/model/consts.local.js.map +1 -0
  192. package/build/model/consumer.d.ts +23 -0
  193. package/build/model/consumer.d.ts.map +1 -0
  194. package/build/model/consumer.js +272 -0
  195. package/build/model/consumer.js.map +1 -0
  196. package/build/model/estimate.d.ts +5 -0
  197. package/build/model/estimate.d.ts.map +1 -0
  198. package/build/model/estimate.js +69 -0
  199. package/build/model/estimate.js.map +1 -0
  200. package/build/model/index.d.ts +7 -2
  201. package/build/model/index.d.ts.map +1 -1
  202. package/build/model/index.js +7 -2
  203. package/build/model/index.js.map +1 -1
  204. package/build/model/limit.d.ts +7 -0
  205. package/build/model/limit.d.ts.map +1 -0
  206. package/build/model/limit.js +32 -0
  207. package/build/model/limit.js.map +1 -0
  208. package/build/model/plan.d.ts.map +1 -1
  209. package/build/model/plan.js +24 -11
  210. package/build/model/plan.js.map +1 -1
  211. package/build/model/portal.d.ts +4 -0
  212. package/build/model/portal.d.ts.map +1 -0
  213. package/build/model/portal.js +22 -0
  214. package/build/model/portal.js.map +1 -0
  215. package/build/model/pricing.d.ts +4 -0
  216. package/build/model/pricing.d.ts.map +1 -0
  217. package/build/model/pricing.js +31 -0
  218. package/build/model/pricing.js.map +1 -0
  219. package/build/model/view.d.ts +3 -0
  220. package/build/model/view.d.ts.map +1 -0
  221. package/build/model/view.js +15 -0
  222. package/build/model/view.js.map +1 -0
  223. package/build/pricing/types.d.ts +13 -0
  224. package/build/pricing/types.d.ts.map +1 -0
  225. package/build/pricing/types.js +2 -0
  226. package/build/pricing/types.js.map +1 -0
  227. package/build/pricing.d.ts +13 -0
  228. package/build/pricing.d.ts.map +1 -0
  229. package/build/pricing.js +74 -0
  230. package/build/pricing.js.map +1 -0
  231. package/build/promo/types.d.ts +14 -0
  232. package/build/promo/types.d.ts.map +1 -0
  233. package/build/promo/types.js +2 -0
  234. package/build/promo/types.js.map +1 -0
  235. package/build/promo.d.ts +7 -0
  236. package/build/promo.d.ts.map +1 -0
  237. package/build/promo.js +25 -0
  238. package/build/promo.js.map +1 -0
  239. package/build/regions/types.d.ts +32 -0
  240. package/build/regions/types.d.ts.map +1 -0
  241. package/build/regions/types.js +2 -0
  242. package/build/regions/types.js.map +1 -0
  243. package/build/regions.d.ts +15 -0
  244. package/build/regions.d.ts.map +1 -0
  245. package/build/regions.js +43 -0
  246. package/build/regions.js.map +1 -0
  247. package/build/service.d.ts.map +1 -1
  248. package/build/service.js +24 -9
  249. package/build/service.js.map +1 -1
  250. package/build/types.d.ts +619 -52
  251. package/build/types.d.ts.map +1 -1
  252. package/build/types.local.d.ts +4 -0
  253. package/build/types.local.d.ts.map +1 -0
  254. package/build/types.local.js +2 -0
  255. package/build/types.local.js.map +1 -0
  256. package/build/view/types.d.ts +33 -0
  257. package/build/view/types.d.ts.map +1 -0
  258. package/build/view/types.js +2 -0
  259. package/build/view/types.js.map +1 -0
  260. package/build/view.d.ts +13 -0
  261. package/build/view.d.ts.map +1 -0
  262. package/build/view.js +78 -0
  263. package/build/view.js.map +1 -0
  264. package/package.json +14 -12
  265. package/src/advertise.ts +13 -0
  266. package/src/checkout/consts.ts +1 -0
  267. package/src/checkout/index.ts +5 -0
  268. package/src/checkout/narrow/types.ts +27 -0
  269. package/src/checkout/narrow.ts +63 -0
  270. package/src/checkout/protocols.ts +39 -0
  271. package/src/checkout/types.ts +27 -0
  272. package/src/consts.local.ts +34 -0
  273. package/src/consts.ts +417 -24
  274. package/src/consumer/cancel.ts +40 -0
  275. package/src/consumer/consts.local.ts +16 -0
  276. package/src/consumer/consts.ts +46 -0
  277. package/src/consumer/copy/types.ts +54 -0
  278. package/src/consumer/copy.ts +121 -0
  279. package/src/consumer/deadline/types.ts +30 -0
  280. package/src/consumer/deadline.ts +61 -0
  281. package/src/consumer/fifo/types.ts +17 -0
  282. package/src/consumer/fifo.ts +42 -0
  283. package/src/consumer/index.ts +17 -0
  284. package/src/consumer/policy/types.ts +20 -0
  285. package/src/consumer/policy.ts +101 -0
  286. package/src/consumer/protocols.ts +116 -0
  287. package/src/consumer/refund/types.ts +27 -0
  288. package/src/consumer/refund.ts +136 -0
  289. package/src/consumer/refusal.ts +35 -0
  290. package/src/consumer/revive/types.ts +23 -0
  291. package/src/consumer/revive.ts +82 -0
  292. package/src/consumer/types.local.ts +10 -0
  293. package/src/consumer/types.ts +189 -0
  294. package/src/countries.ts +5 -0
  295. package/src/entitled.ts +20 -0
  296. package/src/entitlement/types.ts +29 -0
  297. package/src/entitlement.ts +67 -103
  298. package/src/errors.ts +445 -0
  299. package/src/estimate/types.ts +34 -0
  300. package/src/estimate.ts +90 -0
  301. package/src/i18n/consumer-rights/be.json +131 -0
  302. package/src/i18n/consumer-rights/de.json +131 -0
  303. package/src/i18n/consumer-rights/en.json +131 -0
  304. package/src/i18n/consumer-rights/es.json +131 -0
  305. package/src/i18n/consumer-rights/fr.json +131 -0
  306. package/src/i18n/consumer-rights/pl.json +131 -0
  307. package/src/i18n/consumer-rights/ru.json +131 -0
  308. package/src/i18n/consumer-rights/uk.json +131 -0
  309. package/src/i18n/errors/be.json +30 -0
  310. package/src/i18n/errors/de.json +30 -0
  311. package/src/i18n/errors/en.json +30 -0
  312. package/src/i18n/errors/es.json +30 -0
  313. package/src/i18n/errors/fr.json +30 -0
  314. package/src/i18n/errors/pl.json +30 -0
  315. package/src/i18n/errors/ru.json +30 -0
  316. package/src/i18n/errors/uk.json +30 -0
  317. package/src/i18n/fr.json +39 -0
  318. package/src/i18n.ts +58 -8
  319. package/src/index.ts +18 -2
  320. package/src/limit/types.ts +41 -0
  321. package/src/limit.ts +132 -0
  322. package/src/model/checkout.ts +12 -7
  323. package/src/model/consts.local.ts +83 -0
  324. package/src/model/consts.ts +206 -0
  325. package/src/model/consumer.ts +294 -0
  326. package/src/model/estimate.ts +73 -0
  327. package/src/model/index.ts +7 -2
  328. package/src/model/limit.ts +36 -0
  329. package/src/model/plan.ts +25 -12
  330. package/src/model/portal.ts +25 -0
  331. package/src/model/pricing.ts +34 -0
  332. package/src/model/view.ts +17 -0
  333. package/src/pricing/types.ts +13 -0
  334. package/src/pricing.ts +110 -0
  335. package/src/promo/types.ts +18 -0
  336. package/src/promo.ts +46 -0
  337. package/src/regions/types.ts +42 -0
  338. package/src/regions.ts +85 -0
  339. package/src/service.ts +32 -15
  340. package/src/types.local.ts +5 -0
  341. package/src/types.ts +669 -49
  342. package/src/view/types.ts +39 -0
  343. package/src/view.ts +119 -0
  344. package/tests/checkout-narrow.spec.ts +75 -0
  345. package/tests/consumer-copy.spec.ts +378 -0
  346. package/tests/consumer-deadline.spec.ts +85 -0
  347. package/tests/consumer-fifo.spec.ts +61 -0
  348. package/tests/consumer-protocols.spec.ts +103 -0
  349. package/tests/consumer-refund.spec.ts +134 -0
  350. package/tests/consumer-regions.spec.ts +140 -0
  351. package/tests/consumer-wire.spec.ts +170 -0
  352. package/tests/contract.spec.ts +183 -0
  353. package/tests/entitlement.spec.ts +38 -25
  354. package/tests/errors.spec.ts +168 -0
  355. package/tests/estimate.spec.ts +139 -0
  356. package/tests/limit.spec.ts +117 -0
  357. package/tests/pricing.spec.ts +30 -0
  358. package/tests/view.spec.ts +130 -0
  359. package/build/.gitkeep +0 -0
  360. package/src/model/subscription.ts +0 -63
  361. package/src/model/utils.ts +0 -32
  362. package/src/modules.ts +0 -43
  363. package/tests/entitlement.spec.d.ts +0 -2
  364. package/tests/entitlement.spec.d.ts.map +0 -1
  365. package/tests/entitlement.spec.js +0 -72
  366. package/tests/entitlement.spec.js.map +0 -1
@@ -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
- * The gate alias a paid capability is asserted under.
7
- *
8
- * Distinct from `paymentGate.base` (a ROUTE id) and from the gateway service alias — three
9
- * different things that would otherwise all be called "payment gate".
10
- */
11
- export const ENTITLEMENT_GATE = 'entitlement-gate'
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
- * The capability scope that carries FEATURE flags.
15
- *
16
- * Kept apart from `renewable`, which carries numeric quotas that are consumed or counted. Merging
17
- * them would make "has one production slot left" and "may remove the platform credit" the same
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
- return parsed
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
- return capabilities.some(set => {
82
- if (scope != null && set.scope !== scope) {
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
- const held = set.permissions?.[permission]
86
- if (held == null || held === false) {
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
- // A floor is a numeric question: a boolean flag, however true, does not answer it.
91
- return atLeast != null ? typeof held === 'number' && held >= atLeast : true
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
- /** Everything a union grants, formatted back into parameters. The wire shape of a client read. */
96
- export const entitlementList = (capabilities: PermissionSet[] | undefined): string[] => {
97
- const list: string[] = []
98
- for (const set of capabilities ?? []) {
99
- for (const [permission, value] of Object.entries(set.permissions ?? {})) {
100
- if (value == null || value === false) {
101
- continue
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 [...new Set(list)]
71
+ return { parseEntitlementParam, formatEntitlementParam, hasEntitlement, entitlementList }
108
72
  }
109
73
 
110
- /**
111
- * Declare that an entrypoint needs a paid capability.
112
- *
113
- * Sugar over `gate(ENTITLEMENT_GATE, params)` so a route reads as what it means. Several
114
- * parameters are OR'd, matching every other gate in the framework.
115
- *
116
- * Putting the requirement HERE rather than in a handler is the point: the framework asserts a gate
117
- * before the handler is entered, so the route table states what a feature costs and no new
118
- * endpoint can forget to check.
119
- */
120
- export const entitled = (
121
- params: string | string[], opts?: CommonEntrypointOptions
122
- ): CommonEntrypointOptions => gate(ENTITLEMENT_GATE, params, opts)
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)