@owlmeans/payment 0.1.18-rc.8 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) 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 +12 -0
  134. package/build/entitlement.d.ts.map +1 -0
  135. package/build/entitlement.js +64 -0
  136. package/build/entitlement.js.map +1 -0
  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 +19 -1
  169. package/build/index.d.ts.map +1 -1
  170. package/build/index.js +12 -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 +86 -0
  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 +19 -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 +103 -0
  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/tsconfig.json +11 -0
  359. package/tests/view.spec.ts +130 -0
  360. package/tsconfig.json +6 -1
  361. package/build/.gitkeep +0 -0
  362. package/src/model/subscription.ts +0 -63
  363. package/src/model/utils.ts +0 -32
  364. package/src/modules.ts +0 -43
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 type { ConfigRecord } from '@owlmeans/context'
3
- import { DEFAULT_ALIAS, PaymentEntityType, PLAN_RECORD_PREFIX, PRODUCT_RECORD_TYPE } from './consts.js'
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
@@ -0,0 +1,5 @@
1
+ import type { ConsumerRightsPolicy } from './types.js'
2
+
3
+ export type RegionPolicy = Pick<ConsumerRightsPolicy, 'countries'> | null | undefined
4
+
5
+ export type Dated = Record<string, unknown>