@ducatjs/money 0.0.0-stage → 0.1.0

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 (285) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +51 -0
  3. package/README.md +617 -2
  4. package/assets/CREDITS.md +12 -0
  5. package/data/adyen-fx.json +19 -0
  6. package/data/adyen.json +171 -0
  7. package/data/braintree.json +223 -0
  8. package/data/cash-rounding.json +399 -0
  9. package/data/checkout.json +230 -0
  10. package/data/iso4217-list-one.xml +1956 -0
  11. package/data/klarna.json +95 -0
  12. package/data/paypal.json +65 -0
  13. package/data/stripe-fx.json +17 -0
  14. package/data/stripe.json +285 -0
  15. package/dist/adyen-fx.d.ts +25 -0
  16. package/dist/adyen-fx.d.ts.map +1 -0
  17. package/dist/adyen-fx.js +122 -0
  18. package/dist/adyen-fx.js.map +1 -0
  19. package/dist/adyen.d.ts +25 -0
  20. package/dist/adyen.d.ts.map +1 -0
  21. package/dist/adyen.js +34 -0
  22. package/dist/adyen.js.map +1 -0
  23. package/dist/allocate.d.ts +72 -0
  24. package/dist/allocate.d.ts.map +1 -0
  25. package/dist/allocate.js +314 -0
  26. package/dist/allocate.js.map +1 -0
  27. package/dist/bag.d.ts +58 -0
  28. package/dist/bag.d.ts.map +1 -0
  29. package/dist/bag.js +122 -0
  30. package/dist/bag.js.map +1 -0
  31. package/dist/braintree.d.ts +34 -0
  32. package/dist/braintree.d.ts.map +1 -0
  33. package/dist/braintree.js +45 -0
  34. package/dist/braintree.js.map +1 -0
  35. package/dist/cash.d.ts +35 -0
  36. package/dist/cash.d.ts.map +1 -0
  37. package/dist/cash.js +86 -0
  38. package/dist/cash.js.map +1 -0
  39. package/dist/checkout.d.ts +42 -0
  40. package/dist/checkout.d.ts.map +1 -0
  41. package/dist/checkout.js +52 -0
  42. package/dist/checkout.js.map +1 -0
  43. package/dist/convert.d.ts +51 -0
  44. package/dist/convert.d.ts.map +1 -0
  45. package/dist/convert.js +55 -0
  46. package/dist/convert.js.map +1 -0
  47. package/dist/currency.d.ts +22 -0
  48. package/dist/currency.d.ts.map +1 -0
  49. package/dist/currency.js +38 -0
  50. package/dist/currency.js.map +1 -0
  51. package/dist/decimal.d.ts +52 -0
  52. package/dist/decimal.d.ts.map +1 -0
  53. package/dist/decimal.js +251 -0
  54. package/dist/decimal.js.map +1 -0
  55. package/dist/ecb.d.ts +17 -0
  56. package/dist/ecb.d.ts.map +1 -0
  57. package/dist/ecb.js +147 -0
  58. package/dist/ecb.js.map +1 -0
  59. package/dist/exact.d.ts +62 -0
  60. package/dist/exact.d.ts.map +1 -0
  61. package/dist/exact.js +147 -0
  62. package/dist/exact.js.map +1 -0
  63. package/dist/fetch.d.ts +58 -0
  64. package/dist/fetch.d.ts.map +1 -0
  65. package/dist/fetch.js +79 -0
  66. package/dist/fetch.js.map +1 -0
  67. package/dist/fixed.d.ts +121 -0
  68. package/dist/fixed.d.ts.map +1 -0
  69. package/dist/fixed.js +420 -0
  70. package/dist/fixed.js.map +1 -0
  71. package/dist/fn.d.ts +27 -0
  72. package/dist/fn.d.ts.map +1 -0
  73. package/dist/fn.js +30 -0
  74. package/dist/fn.js.map +1 -0
  75. package/dist/format.d.ts +13 -0
  76. package/dist/format.d.ts.map +1 -0
  77. package/dist/format.js +90 -0
  78. package/dist/format.js.map +1 -0
  79. package/dist/generated/adyen.d.ts +3 -0
  80. package/dist/generated/adyen.d.ts.map +1 -0
  81. package/dist/generated/adyen.js +144 -0
  82. package/dist/generated/adyen.js.map +1 -0
  83. package/dist/generated/braintree.d.ts +3 -0
  84. package/dist/generated/braintree.d.ts.map +1 -0
  85. package/dist/generated/braintree.js +140 -0
  86. package/dist/generated/braintree.js.map +1 -0
  87. package/dist/generated/cash.d.ts +12 -0
  88. package/dist/generated/cash.d.ts.map +1 -0
  89. package/dist/generated/cash.js +364 -0
  90. package/dist/generated/cash.js.map +1 -0
  91. package/dist/generated/checkout.d.ts +3 -0
  92. package/dist/generated/checkout.d.ts.map +1 -0
  93. package/dist/generated/checkout.js +151 -0
  94. package/dist/generated/checkout.js.map +1 -0
  95. package/dist/generated/iso4217.d.ts +174 -0
  96. package/dist/generated/iso4217.d.ts.map +1 -0
  97. package/dist/generated/iso4217.js +175 -0
  98. package/dist/generated/iso4217.js.map +1 -0
  99. package/dist/generated/kernel.d.ts +17 -0
  100. package/dist/generated/kernel.d.ts.map +1 -0
  101. package/dist/generated/kernel.js +208 -0
  102. package/dist/generated/kernel.js.map +1 -0
  103. package/dist/generated/klarna.d.ts +3 -0
  104. package/dist/generated/klarna.d.ts.map +1 -0
  105. package/dist/generated/klarna.js +21 -0
  106. package/dist/generated/klarna.js.map +1 -0
  107. package/dist/generated/paypal.d.ts +3 -0
  108. package/dist/generated/paypal.d.ts.map +1 -0
  109. package/dist/generated/paypal.js +31 -0
  110. package/dist/generated/paypal.js.map +1 -0
  111. package/dist/generated/stripe.d.ts +8 -0
  112. package/dist/generated/stripe.d.ts.map +1 -0
  113. package/dist/generated/stripe.js +150 -0
  114. package/dist/generated/stripe.js.map +1 -0
  115. package/dist/index.d.ts +11 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +11 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/klarna.d.ts +31 -0
  120. package/dist/klarna.d.ts.map +1 -0
  121. package/dist/klarna.js +38 -0
  122. package/dist/klarna.js.map +1 -0
  123. package/dist/money.d.ts +218 -0
  124. package/dist/money.d.ts.map +1 -0
  125. package/dist/money.js +577 -0
  126. package/dist/money.js.map +1 -0
  127. package/dist/parse.d.ts +11 -0
  128. package/dist/parse.d.ts.map +1 -0
  129. package/dist/parse.js +132 -0
  130. package/dist/parse.js.map +1 -0
  131. package/dist/paypal.d.ts +41 -0
  132. package/dist/paypal.d.ts.map +1 -0
  133. package/dist/paypal.js +58 -0
  134. package/dist/paypal.js.map +1 -0
  135. package/dist/plugins/adyen.d.ts +15 -0
  136. package/dist/plugins/adyen.d.ts.map +1 -0
  137. package/dist/plugins/adyen.js +10 -0
  138. package/dist/plugins/adyen.js.map +1 -0
  139. package/dist/plugins/all.d.ts +22 -0
  140. package/dist/plugins/all.d.ts.map +1 -0
  141. package/dist/plugins/all.js +23 -0
  142. package/dist/plugins/all.js.map +1 -0
  143. package/dist/plugins/allocate.d.ts +20 -0
  144. package/dist/plugins/allocate.d.ts.map +1 -0
  145. package/dist/plugins/allocate.js +26 -0
  146. package/dist/plugins/allocate.js.map +1 -0
  147. package/dist/plugins/braintree.d.ts +12 -0
  148. package/dist/plugins/braintree.d.ts.map +1 -0
  149. package/dist/plugins/braintree.js +12 -0
  150. package/dist/plugins/braintree.js.map +1 -0
  151. package/dist/plugins/cash.d.ts +19 -0
  152. package/dist/plugins/cash.d.ts.map +1 -0
  153. package/dist/plugins/cash.js +16 -0
  154. package/dist/plugins/cash.js.map +1 -0
  155. package/dist/plugins/checkout.d.ts +21 -0
  156. package/dist/plugins/checkout.d.ts.map +1 -0
  157. package/dist/plugins/checkout.js +16 -0
  158. package/dist/plugins/checkout.js.map +1 -0
  159. package/dist/plugins/convert.d.ts +20 -0
  160. package/dist/plugins/convert.d.ts.map +1 -0
  161. package/dist/plugins/convert.js +13 -0
  162. package/dist/plugins/convert.js.map +1 -0
  163. package/dist/plugins/exact.d.ts +15 -0
  164. package/dist/plugins/exact.d.ts.map +1 -0
  165. package/dist/plugins/exact.js +13 -0
  166. package/dist/plugins/exact.js.map +1 -0
  167. package/dist/plugins/fixed.d.ts +17 -0
  168. package/dist/plugins/fixed.d.ts.map +1 -0
  169. package/dist/plugins/fixed.js +13 -0
  170. package/dist/plugins/fixed.js.map +1 -0
  171. package/dist/plugins/format.d.ts +9 -0
  172. package/dist/plugins/format.d.ts.map +1 -0
  173. package/dist/plugins/format.js +16 -0
  174. package/dist/plugins/format.js.map +1 -0
  175. package/dist/plugins/klarna.d.ts +10 -0
  176. package/dist/plugins/klarna.d.ts.map +1 -0
  177. package/dist/plugins/klarna.js +10 -0
  178. package/dist/plugins/klarna.js.map +1 -0
  179. package/dist/plugins/paypal.d.ts +14 -0
  180. package/dist/plugins/paypal.d.ts.map +1 -0
  181. package/dist/plugins/paypal.js +16 -0
  182. package/dist/plugins/paypal.js.map +1 -0
  183. package/dist/plugins/prisma.d.ts +8 -0
  184. package/dist/plugins/prisma.d.ts.map +1 -0
  185. package/dist/plugins/prisma.js +13 -0
  186. package/dist/plugins/prisma.js.map +1 -0
  187. package/dist/plugins/stripe.d.ts +14 -0
  188. package/dist/plugins/stripe.d.ts.map +1 -0
  189. package/dist/plugins/stripe.js +16 -0
  190. package/dist/plugins/stripe.js.map +1 -0
  191. package/dist/plugins/tax.d.ts +21 -0
  192. package/dist/plugins/tax.d.ts.map +1 -0
  193. package/dist/plugins/tax.js +18 -0
  194. package/dist/plugins/tax.js.map +1 -0
  195. package/dist/prisma.d.ts +66 -0
  196. package/dist/prisma.d.ts.map +1 -0
  197. package/dist/prisma.js +66 -0
  198. package/dist/prisma.js.map +1 -0
  199. package/dist/processor.d.ts +28 -0
  200. package/dist/processor.d.ts.map +1 -0
  201. package/dist/processor.js +118 -0
  202. package/dist/processor.js.map +1 -0
  203. package/dist/provider.d.ts +89 -0
  204. package/dist/provider.d.ts.map +1 -0
  205. package/dist/provider.js +240 -0
  206. package/dist/provider.js.map +1 -0
  207. package/dist/rate.d.ts +119 -0
  208. package/dist/rate.d.ts.map +1 -0
  209. package/dist/rate.js +271 -0
  210. package/dist/rate.js.map +1 -0
  211. package/dist/rounding-error.d.ts +9 -0
  212. package/dist/rounding-error.d.ts.map +1 -0
  213. package/dist/rounding-error.js +9 -0
  214. package/dist/rounding-error.js.map +1 -0
  215. package/dist/rounding.d.ts +50 -0
  216. package/dist/rounding.d.ts.map +1 -0
  217. package/dist/rounding.js +87 -0
  218. package/dist/rounding.js.map +1 -0
  219. package/dist/stripe-fx.d.ts +22 -0
  220. package/dist/stripe-fx.d.ts.map +1 -0
  221. package/dist/stripe-fx.js +30 -0
  222. package/dist/stripe-fx.js.map +1 -0
  223. package/dist/stripe.d.ts +36 -0
  224. package/dist/stripe.d.ts.map +1 -0
  225. package/dist/stripe.js +39 -0
  226. package/dist/stripe.js.map +1 -0
  227. package/dist/tax.d.ts +29 -0
  228. package/dist/tax.d.ts.map +1 -0
  229. package/dist/tax.js +39 -0
  230. package/dist/tax.js.map +1 -0
  231. package/package.json +171 -4
  232. package/src/adyen-fx.ts +154 -0
  233. package/src/adyen.ts +46 -0
  234. package/src/allocate.ts +393 -0
  235. package/src/bag.ts +170 -0
  236. package/src/braintree.ts +71 -0
  237. package/src/cash.ts +115 -0
  238. package/src/checkout.ts +77 -0
  239. package/src/convert.ts +159 -0
  240. package/src/currency.ts +61 -0
  241. package/src/decimal.ts +283 -0
  242. package/src/ecb.ts +162 -0
  243. package/src/exact.ts +182 -0
  244. package/src/fetch.ts +127 -0
  245. package/src/fixed.ts +558 -0
  246. package/src/fn.ts +80 -0
  247. package/src/format.ts +106 -0
  248. package/src/generated/adyen.ts +144 -0
  249. package/src/generated/braintree.ts +140 -0
  250. package/src/generated/cash.ts +375 -0
  251. package/src/generated/checkout.ts +151 -0
  252. package/src/generated/iso4217.ts +179 -0
  253. package/src/generated/kernel.ts +230 -0
  254. package/src/generated/klarna.ts +21 -0
  255. package/src/generated/paypal.ts +31 -0
  256. package/src/generated/stripe.ts +150 -0
  257. package/src/index.ts +37 -0
  258. package/src/klarna.ts +54 -0
  259. package/src/money.ts +781 -0
  260. package/src/parse.ts +144 -0
  261. package/src/paypal.ts +93 -0
  262. package/src/plugins/adyen.ts +25 -0
  263. package/src/plugins/all.ts +47 -0
  264. package/src/plugins/allocate.ts +53 -0
  265. package/src/plugins/braintree.ts +23 -0
  266. package/src/plugins/cash.ts +33 -0
  267. package/src/plugins/checkout.ts +32 -0
  268. package/src/plugins/convert.ts +50 -0
  269. package/src/plugins/exact.ts +23 -0
  270. package/src/plugins/fixed.ts +28 -0
  271. package/src/plugins/format.ts +28 -0
  272. package/src/plugins/klarna.ts +21 -0
  273. package/src/plugins/paypal.ts +29 -0
  274. package/src/plugins/prisma.ts +24 -0
  275. package/src/plugins/stripe.ts +29 -0
  276. package/src/plugins/tax.ts +41 -0
  277. package/src/prisma.ts +118 -0
  278. package/src/processor.ts +181 -0
  279. package/src/provider.ts +324 -0
  280. package/src/rate.ts +377 -0
  281. package/src/rounding-error.ts +8 -0
  282. package/src/rounding.ts +122 -0
  283. package/src/stripe-fx.ts +49 -0
  284. package/src/stripe.ts +69 -0
  285. package/src/tax.ts +71 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax.d.ts","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,MAAM,EAAc,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAkC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EAAoB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAOpE;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,KAAK,CAAC,CAAC,CAAC,CAEV;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,MAAM;IAC5C,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EACrC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,EACb,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,YAAY,CAAC,CAAC,CAAC,CAIjB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,YAAY,CAAC,CAAC,CAAC,CASjB;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,MAAM,EACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,YAA+B,GACxC,KAAK,CAAC,CAAC,CAAC,CAEV"}
package/dist/tax.js ADDED
@@ -0,0 +1,39 @@
1
+ import { ratio } from './decimal.js';
2
+ import { add, multiplyByRatio, subtract } from './money.js';
3
+ import { DEFAULT_ROUNDING } from './rounding.js';
4
+ const percentRatio = (percent) => {
5
+ const asRatio = ratio(percent);
6
+ return ratio(asRatio.num, asRatio.den * 100n);
7
+ };
8
+ /**
9
+ * percent% of m: percentage(EUR 80, '12.5', 'halfEven') = EUR 10.00. Rounding defaults to
10
+ * 'halfExpand'.
11
+ */
12
+ export function percentage(money, percent, rounding = DEFAULT_ROUNDING) {
13
+ return multiplyByRatio(money, percentRatio(percent), rounding);
14
+ }
15
+ /**
16
+ * Tax-exclusive: adds percent% tax to a net amount. addTax(EUR 10, '20', 'halfEven') -> net 10, tax
17
+ * 2, gross 12.
18
+ */
19
+ export function addTax(net, percent, rounding = DEFAULT_ROUNDING) {
20
+ const tax = multiplyByRatio(net, percentRatio(percent), rounding);
21
+ const gross = add(net, tax);
22
+ return { net, tax, gross };
23
+ }
24
+ /**
25
+ * Tax-inclusive: extracts percent% tax contained in a gross amount (VAT extraction).
26
+ * net = gross / (1 + percent/100) rounded; tax = gross - net, so the parts always add up.
27
+ * extractTax(EUR 12, '20', 'halfEven') -> net 10.00, tax 2.00.
28
+ */
29
+ export function extractTax(gross, percent, rounding = DEFAULT_ROUNDING) {
30
+ const percentAsRatio = ratio(percent);
31
+ // 1 / (1 + p/100) = 100 den / (100 den + num)
32
+ const net = multiplyByRatio(gross, ratio(100n * percentAsRatio.den, 100n * percentAsRatio.den + percentAsRatio.num), rounding);
33
+ return { net, tax: subtract(gross, net), gross };
34
+ }
35
+ /** Price after a percent% discount; the discount amount is rounded with `rounding`. */
36
+ export function discount(price, percent, rounding = DEFAULT_ROUNDING) {
37
+ return subtract(price, multiplyByRatio(price, percentRatio(percent), rounding));
38
+ }
39
+ //# sourceMappingURL=tax.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tax.js","sourceRoot":"","sources":["../src/tax.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA2B,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAc,MAAM,YAAY,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAqB,MAAM,eAAe,CAAC;AAEpE,MAAM,YAAY,GAAG,CAAC,OAAe,EAAS,EAAE;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,OAAO,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,MAAM,CACpB,GAAa,EACb,OAAe,EACf,WAAyB,gBAAgB;IAEzC,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;IAClE,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CACxB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;IACtC,8CAA8C;IAC9C,MAAM,GAAG,GAAG,eAAe,CACzB,KAAK,EACL,KAAK,CAAC,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,EAChF,QAAQ,CACT,CAAC;IACF,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,QAAQ,CACtB,KAAe,EACf,OAAe,EACf,WAAyB,gBAAgB;IAEzC,OAAO,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAClF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,173 @@
1
1
  {
2
2
  "name": "@ducatjs/money",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Exact, fast, typed Money for TypeScript: integer minor units, bigint fallback, Stripe/Adyen views, allocation, cash rounding, Prisma helpers.",
5
+ "homepage": "https://github.com/public/ducat#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/public/ducat/issues"
8
+ },
9
+ "license": "MIT",
10
+ "author": "Alex Stapleton <alexs@prol.etari.at>",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/public/ducat.git"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "data",
19
+ "assets/CREDITS.md",
20
+ "NOTICE",
21
+ "LICENSE"
22
+ ],
23
+ "type": "module",
24
+ "sideEffects": [
25
+ "./dist/plugins/*.js",
26
+ "./src/plugins/*.ts"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./fn": {
34
+ "types": "./dist/fn.d.ts",
35
+ "default": "./dist/fn.js"
36
+ },
37
+ "./allocate": {
38
+ "types": "./dist/plugins/allocate.d.ts",
39
+ "default": "./dist/plugins/allocate.js"
40
+ },
41
+ "./tax": {
42
+ "types": "./dist/plugins/tax.d.ts",
43
+ "default": "./dist/plugins/tax.js"
44
+ },
45
+ "./convert": {
46
+ "types": "./dist/plugins/convert.d.ts",
47
+ "default": "./dist/plugins/convert.js"
48
+ },
49
+ "./convert/ecb": {
50
+ "types": "./dist/ecb.d.ts",
51
+ "default": "./dist/ecb.js"
52
+ },
53
+ "./convert/fetch": {
54
+ "types": "./dist/fetch.d.ts",
55
+ "default": "./dist/fetch.js"
56
+ },
57
+ "./exact": {
58
+ "types": "./dist/plugins/exact.d.ts",
59
+ "default": "./dist/plugins/exact.js"
60
+ },
61
+ "./cash": {
62
+ "types": "./dist/plugins/cash.d.ts",
63
+ "default": "./dist/plugins/cash.js"
64
+ },
65
+ "./format": {
66
+ "types": "./dist/plugins/format.d.ts",
67
+ "default": "./dist/plugins/format.js"
68
+ },
69
+ "./fixed": {
70
+ "types": "./dist/plugins/fixed.d.ts",
71
+ "default": "./dist/plugins/fixed.js"
72
+ },
73
+ "./parse": {
74
+ "types": "./dist/parse.d.ts",
75
+ "default": "./dist/parse.js"
76
+ },
77
+ "./stripe": {
78
+ "types": "./dist/plugins/stripe.d.ts",
79
+ "default": "./dist/plugins/stripe.js"
80
+ },
81
+ "./adyen": {
82
+ "types": "./dist/plugins/adyen.d.ts",
83
+ "default": "./dist/plugins/adyen.js"
84
+ },
85
+ "./checkout": {
86
+ "types": "./dist/plugins/checkout.d.ts",
87
+ "default": "./dist/plugins/checkout.js"
88
+ },
89
+ "./paypal": {
90
+ "types": "./dist/plugins/paypal.d.ts",
91
+ "default": "./dist/plugins/paypal.js"
92
+ },
93
+ "./braintree": {
94
+ "types": "./dist/plugins/braintree.d.ts",
95
+ "default": "./dist/plugins/braintree.js"
96
+ },
97
+ "./klarna": {
98
+ "types": "./dist/plugins/klarna.d.ts",
99
+ "default": "./dist/plugins/klarna.js"
100
+ },
101
+ "./stripe-fx": {
102
+ "types": "./dist/stripe-fx.d.ts",
103
+ "default": "./dist/stripe-fx.js"
104
+ },
105
+ "./adyen-fx": {
106
+ "types": "./dist/adyen-fx.d.ts",
107
+ "default": "./dist/adyen-fx.js"
108
+ },
109
+ "./prisma": {
110
+ "types": "./dist/plugins/prisma.d.ts",
111
+ "default": "./dist/plugins/prisma.js"
112
+ },
113
+ "./all": {
114
+ "types": "./dist/plugins/all.d.ts",
115
+ "default": "./dist/plugins/all.js"
116
+ },
117
+ "./package.json": "./package.json"
118
+ },
119
+ "scripts": {
120
+ "build": "tsc -p tsconfig.json",
121
+ "typecheck": "tsc -p tsconfig.test.json && tsc -p test/core-only/tsconfig.json && tsc -p test/browser/tsconfig.json",
122
+ "test": "vitest run",
123
+ "test:deep": "DUCAT_DEEP=1 vitest run",
124
+ "compare": "pnpm --dir compare battery && pnpm --dir compare tables",
125
+ "compare:check": "pnpm --dir compare battery:check && pnpm --dir compare tables:check",
126
+ "tables": "pnpm --dir compare tables",
127
+ "test:db": "vitest run --config vitest.db.config.ts",
128
+ "test:browser": "playwright test",
129
+ "test:mutation": "DUCAT_MAX_RUNS=50 stryker run",
130
+ "fetch:dectest": "node scripts/fetch-dectest.mjs",
131
+ "vectors:check": "python3 scripts/gen-pydecimal-vectors.py --check",
132
+ "test:runtimes": "node scripts/smoke-runtimes.mjs",
133
+ "gen": "node scripts/gen-currencies.mjs",
134
+ "gen:check": "node scripts/gen-currencies.mjs --check && node scripts/gen-kernel.mjs --verify",
135
+ "gen:kernel": "node scripts/gen-kernel.mjs",
136
+ "gen:kernel:check": "node scripts/gen-kernel.mjs --check",
137
+ "gen:fetch": "node scripts/gen-currencies.mjs --check --fetch",
138
+ "bench": "node scripts/bench-all.mjs",
139
+ "size": "node scripts/size.mjs",
140
+ "lint": "oxlint",
141
+ "fmt": "oxfmt",
142
+ "fmt:check": "oxfmt --check",
143
+ "check": "pnpm lint && pnpm fmt:check && pnpm typecheck && pnpm size && pnpm test",
144
+ "bench:vitest": "vitest bench --run"
145
+ },
146
+ "devDependencies": {
147
+ "@codspeed/vitest-plugin": "6.1.1",
148
+ "@playwright/test": "1.65.0",
149
+ "@prisma/client": "^6.19.3",
150
+ "@stryker-mutator/core": "10.1.1",
151
+ "@stryker-mutator/vitest-runner": "10.1.1",
152
+ "@types/big.js": "^7.0.0",
153
+ "@types/node": "^26.6.6",
154
+ "@types/pg": "8.23.1",
155
+ "big.js": "^7.0.1",
156
+ "currency.js": "^2.0.4",
157
+ "decimal.js": "^10.6.0",
158
+ "dinero.js": "^2.0.2",
159
+ "esbuild": "^0.28.2",
160
+ "fast-check": "^4.10.2",
161
+ "mysql2": "3.24.6",
162
+ "oxfmt": "0.77.0",
163
+ "oxlint": "1.92.0",
164
+ "pg": "8.23.1",
165
+ "ts-money": "^0.4.8",
166
+ "typescript": "^5.9.3",
167
+ "vitest": "^4.1.12"
168
+ },
169
+ "engines": {
170
+ "node": ">=24"
171
+ },
172
+ "packageManager": "pnpm@12.18.0"
173
+ }
@@ -0,0 +1,154 @@
1
+ // Adyen exchange rates as exact Rates, from settlement details report rows.
2
+ import { exchangeRate, type Rate, type RateInfo } from './rate.js';
3
+
4
+ // UTC offsets, in minutes, of the zone abbreviations that name one offset. Ambiguous ones are left
5
+ // out on purpose: IST is India, Ireland or Israel; CST is US Central or China; BST is British or
6
+ // Bangladesh.
7
+ const ZONE_OFFSET_MINUTES: Readonly<Record<string, number>> = {
8
+ UTC: 0,
9
+ GMT: 0,
10
+ WET: 0,
11
+ WEST: 60,
12
+ CET: 60,
13
+ CEST: 120,
14
+ EET: 120,
15
+ EEST: 180,
16
+ MSK: 180,
17
+ SGT: 480,
18
+ HKT: 480,
19
+ JST: 540,
20
+ KST: 540,
21
+ AEST: 600,
22
+ AEDT: 660,
23
+ NZST: 720,
24
+ NZDT: 780,
25
+ EST: -300,
26
+ EDT: -240,
27
+ MST: -420,
28
+ MDT: -360,
29
+ PST: -480,
30
+ PDT: -420,
31
+ };
32
+
33
+ const LOCAL_TIME = /^(\d{4}-\d\d-\d\d)[ T](\d\d:\d\d(?::\d\d(?:\.\d+)?)?)$/;
34
+ const NUMERIC_OFFSET = /^(?:UTC|GMT)?\s*([+-])(\d{1,2})(?::?(\d\d))?$/;
35
+
36
+ // Real UTC offsets run from -12:00 to +14:00.
37
+ const MAX_OFFSET_MINUTES = 14 * 60;
38
+
39
+ /** The zone's offset in minutes, or undefined when it can't be read unambiguously. */
40
+ function offsetMinutes(zone: string): number | undefined {
41
+ const named = ZONE_OFFSET_MINUTES[zone.toUpperCase()];
42
+ if (named !== undefined) {
43
+ return named;
44
+ }
45
+ const numeric = NUMERIC_OFFSET.exec(zone);
46
+ if (numeric === null) {
47
+ return undefined;
48
+ }
49
+ const hours = Number(numeric[2]);
50
+ const minutes = Number(numeric[3] ?? 0);
51
+ const total = hours * 60 + minutes;
52
+ if (minutes >= 60 || total > MAX_OFFSET_MINUTES) {
53
+ return undefined;
54
+ }
55
+ return numeric[1] === '-' ? -total : total;
56
+ }
57
+
58
+ /** The offset of an IANA time zone at a UTC instant, in minutes. */
59
+ function ianaOffsetMinutes(timeZone: string, utcMs: number): number {
60
+ const parts = new Intl.DateTimeFormat('en-US', {
61
+ timeZone,
62
+ hourCycle: 'h23',
63
+ year: 'numeric',
64
+ month: 'numeric',
65
+ day: 'numeric',
66
+ hour: 'numeric',
67
+ minute: 'numeric',
68
+ second: 'numeric',
69
+ }).formatToParts(new Date(utcMs));
70
+ const field = (type: string): number => Number(parts.find((part) => part.type === type)!.value);
71
+ const wallClockAsUtc = Date.UTC(
72
+ field('year'),
73
+ field('month') - 1,
74
+ field('day'),
75
+ field('hour'),
76
+ field('minute'),
77
+ field('second'),
78
+ );
79
+ return Math.round((wallClockAsUtc - Math.floor(utcMs / 1000) * 1000) / 60_000);
80
+ }
81
+
82
+ /** The UTC instant of a wall-clock time in an IANA zone, summer time included. */
83
+ function ianaToUtc(localAsUtcMs: number, timeZone: string): number {
84
+ // The offset at the guess can differ from the offset at the answer across a clock change, so
85
+ // look it up again at the first answer.
86
+ const firstGuess = localAsUtcMs - ianaOffsetMinutes(timeZone, localAsUtcMs) * 60_000;
87
+ return localAsUtcMs - ianaOffsetMinutes(timeZone, firstGuess) * 60_000;
88
+ }
89
+
90
+ /** Creation Date as a UTC instant when its zone is known; otherwise its local day. */
91
+ function creationAsOf(
92
+ created: string,
93
+ label: string | undefined,
94
+ timeZone: string | undefined,
95
+ ): string | undefined {
96
+ const local = LOCAL_TIME.exec(created.trim());
97
+ if (local === null) {
98
+ return /^\d{4}-\d\d-\d\d/.test(created) ? created.slice(0, 10) : undefined;
99
+ }
100
+ const localAsUtcMs = Date.parse(`${local[1]}T${local[2]}Z`);
101
+ if (timeZone !== undefined) {
102
+ return new Date(ianaToUtc(localAsUtcMs, timeZone)).toISOString();
103
+ }
104
+ const offset = label === undefined ? undefined : offsetMinutes(label.trim());
105
+ if (offset === undefined) {
106
+ return local[1];
107
+ }
108
+ return new Date(localAsUtcMs - offset * 60_000).toISOString();
109
+ }
110
+
111
+ /** Options for adyenRate. */
112
+ export interface AdyenRateOptions {
113
+ /**
114
+ * The account's IANA time zone ('Europe/Amsterdam'). When given, Creation Date is read in it,
115
+ * summer time included, and the TimeZone column is not used.
116
+ */
117
+ readonly timeZone?: string;
118
+ }
119
+
120
+ /** A settlement details report row, keyed by column name (as from a CSV parser with headers). */
121
+ export type AdyenSettlementRow = Readonly<Record<string, string | undefined>>;
122
+
123
+ /**
124
+ * The rate in a settlement details report row: "The exchange rate used for converting the gross
125
+ * amount into the net amount", Gross Currency → Net Currency (1 when they are the same). The
126
+ * decimal string is kept exactly. `asOf` comes from Creation Date, which Adyen gives in local time,
127
+ * with the zone in the TimeZone column. Pass `{ timeZone: 'Europe/Amsterdam' }` (the account's IANA
128
+ * zone) to read it correctly all year. Without it, an unambiguous abbreviation (CET, PST, ...) or
129
+ * offset (+01:00) is taken literally: Adyen's documentation doesn't say whether the column switches
130
+ * to CEST in summer, and if it doesn't, a literal CET is an hour off then. Any other label gives
131
+ * the local day, which can be a day off from UTC. `undefined` when the row has no rate. For
132
+ * externally settled payments Adyen lists its reference rate, not the acquirer's, so check those
133
+ * with impliedRate().
134
+ */
135
+ export function adyenRate(row: AdyenSettlementRow, options?: AdyenRateOptions): Rate | undefined {
136
+ const gross = row['Gross Currency'];
137
+ const net = row['Net Currency'];
138
+ const rate = row['Exchange Rate']?.trim();
139
+ if (gross === undefined || gross === '' || net === undefined || net === '') {
140
+ return undefined;
141
+ }
142
+ if (rate === undefined || rate === '') {
143
+ return undefined;
144
+ }
145
+
146
+ const created = row['Creation Date'];
147
+ let info: RateInfo = { source: 'adyen' };
148
+ const asOf =
149
+ created === undefined ? undefined : creationAsOf(created, row['TimeZone'], options?.timeZone);
150
+ if (asOf !== undefined) {
151
+ info = { source: 'adyen', asOf };
152
+ }
153
+ return exchangeRate(gross, net, rate, info);
154
+ }
package/src/adyen.ts ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Adyen's minor-unit view (https://docs.adyen.com/development-resources/currency-codes, encoded in
3
+ * data/adyen.json). Adyen's table is authoritative where it differs from ISO 4217: CLP 2 (ISO 0),
4
+ * ISK 2 (ISO 0), IDR 0 (ISO 2), CVE 0 (ISO 2). The Terminal API uses major units instead.
5
+ */
6
+ import { ADYEN_EXPONENTS } from './generated/adyen.js';
7
+ import { type CurrencyInput, type Money } from './money.js';
8
+ import { fromProcessorAmount, toProcessorAmount } from './processor.js';
9
+ import type { RoundingMode } from './rounding.js';
10
+
11
+ export { ADYEN_EXPONENTS };
12
+
13
+ function exponent(currency: string): number {
14
+ const found = ADYEN_EXPONENTS[currency];
15
+ // typeof, not !== undefined: the table is a plain object, so 'toString' or 'constructor' would
16
+ // otherwise find an inherited method instead of failing.
17
+ if (typeof found !== 'number') {
18
+ throw new RangeError(`${currency} is not in Adyen's currency table`);
19
+ }
20
+ return found;
21
+ }
22
+
23
+ /** Whether Adyen lists the currency (its table, not ISO 4217). */
24
+ export const isAdyenCurrency = (currency: string): boolean =>
25
+ typeof ADYEN_EXPONENTS[currency] === 'number';
26
+ /** The exponent Adyen's API uses for a currency (CLP 2, ISK 2, IDR 0, CVE 0 differ from ISO). */
27
+ export const adyenExponent = exponent;
28
+
29
+ /** Adyen `amount` object: toAdyen(money('15000', 'IDR')) -> { value: 15000, currency: 'IDR' }. */
30
+ export function toAdyen(
31
+ money: Money,
32
+ rounding: RoundingMode = 'unnecessary',
33
+ ): { value: number; currency: string } {
34
+ return {
35
+ value: toProcessorAmount('Adyen', money, exponent(money.currency), 1, rounding),
36
+ currency: money.currency,
37
+ };
38
+ }
39
+
40
+ /** Reads an Adyen `amount` object: fromAdyen({ value: 15000, currency: 'IDR' }) is 15000 IDR. */
41
+ export function fromAdyen<C extends CurrencyInput>(amount: {
42
+ value: number | bigint;
43
+ currency: C;
44
+ }): Money<C> {
45
+ return fromProcessorAmount('Adyen', amount.value, amount.currency, exponent(amount.currency));
46
+ }