@unchainedshop/plugins 3.0.0-rc.13 → 3.0.0-rc.15

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 (268) hide show
  1. package/lib/files/gridfs/{gridfs-adapter.d.ts → adapter.d.ts} +2 -4
  2. package/lib/files/gridfs/adapter.d.ts.map +1 -0
  3. package/lib/files/gridfs/{gridfs-adapter.js → adapter.js} +1 -1
  4. package/lib/files/gridfs/adapter.js.map +1 -0
  5. package/lib/files/gridfs/handler-express.d.ts +10 -0
  6. package/lib/files/gridfs/handler-express.d.ts.map +1 -0
  7. package/lib/files/gridfs/{gridfs-webhook.js → handler-express.js} +3 -2
  8. package/lib/files/gridfs/handler-express.js.map +1 -0
  9. package/lib/files/gridfs/handler-fastify.d.ts +4 -0
  10. package/lib/files/gridfs/handler-fastify.d.ts.map +1 -0
  11. package/lib/files/gridfs/handler-fastify.js +91 -0
  12. package/lib/files/gridfs/handler-fastify.js.map +1 -0
  13. package/lib/files/gridfs/index.d.ts +4 -10
  14. package/lib/files/gridfs/index.d.ts.map +1 -1
  15. package/lib/files/gridfs/index.js +3 -27
  16. package/lib/files/gridfs/index.js.map +1 -1
  17. package/lib/files/gridfs/module.d.ts +26 -0
  18. package/lib/files/gridfs/module.d.ts.map +1 -0
  19. package/lib/files/gridfs/module.js +33 -0
  20. package/lib/files/gridfs/module.js.map +1 -0
  21. package/lib/files/minio/handler-express.d.ts +3 -0
  22. package/lib/files/minio/handler-express.d.ts.map +1 -0
  23. package/lib/files/minio/{minio-webhook.js → handler-express.js} +3 -2
  24. package/lib/files/minio/handler-express.js.map +1 -0
  25. package/lib/files/minio/handler-fastify.d.ts +4 -0
  26. package/lib/files/minio/handler-fastify.d.ts.map +1 -0
  27. package/lib/files/minio/handler-fastify.js +34 -0
  28. package/lib/files/minio/handler-fastify.js.map +1 -0
  29. package/lib/files/minio/{minio-adapter.d.ts → index.d.ts} +1 -1
  30. package/lib/files/minio/index.d.ts.map +1 -0
  31. package/lib/files/minio/{minio-adapter.js → index.js} +1 -1
  32. package/lib/files/minio/index.js.map +1 -0
  33. package/lib/payment/apple-iap/adapter.d.ts +1 -1
  34. package/lib/payment/apple-iap/adapter.d.ts.map +1 -1
  35. package/lib/payment/apple-iap/adapter.js +5 -155
  36. package/lib/payment/apple-iap/adapter.js.map +1 -1
  37. package/lib/payment/apple-iap/fix-periods.d.ts +8 -0
  38. package/lib/payment/apple-iap/fix-periods.d.ts.map +1 -0
  39. package/lib/payment/apple-iap/fix-periods.js +22 -0
  40. package/lib/payment/apple-iap/fix-periods.js.map +1 -0
  41. package/lib/payment/apple-iap/handler-express.d.ts +3 -0
  42. package/lib/payment/apple-iap/handler-express.d.ts.map +1 -0
  43. package/lib/payment/apple-iap/handler-express.js +110 -0
  44. package/lib/payment/apple-iap/handler-express.js.map +1 -0
  45. package/lib/payment/apple-iap/handler-fastify.d.ts +4 -0
  46. package/lib/payment/apple-iap/handler-fastify.d.ts.map +1 -0
  47. package/lib/payment/apple-iap/handler-fastify.js +104 -0
  48. package/lib/payment/apple-iap/handler-fastify.js.map +1 -0
  49. package/lib/payment/apple-iap/index.d.ts +3 -1
  50. package/lib/payment/apple-iap/index.d.ts.map +1 -1
  51. package/lib/payment/apple-iap/index.js +2 -1
  52. package/lib/payment/apple-iap/index.js.map +1 -1
  53. package/lib/payment/apple-iap/module.d.ts +22 -0
  54. package/lib/payment/apple-iap/module.d.ts.map +1 -0
  55. package/lib/payment/apple-iap/{module/configureAppleTransactionsModule.js → module.js} +7 -2
  56. package/lib/payment/apple-iap/module.js.map +1 -0
  57. package/lib/payment/apple-iap/verify-receipt.d.ts +5 -0
  58. package/lib/payment/apple-iap/verify-receipt.d.ts.map +1 -0
  59. package/lib/payment/apple-iap/verify-receipt.js +26 -0
  60. package/lib/payment/apple-iap/verify-receipt.js.map +1 -0
  61. package/lib/payment/cryptopay/handler-express.d.ts +3 -0
  62. package/lib/payment/cryptopay/handler-express.d.ts.map +1 -0
  63. package/lib/payment/cryptopay/{middleware.js → handler-express.js} +3 -2
  64. package/lib/payment/cryptopay/{middleware.js.map → handler-express.js.map} +1 -1
  65. package/lib/payment/cryptopay/handler-fastify.d.ts +4 -0
  66. package/lib/payment/cryptopay/handler-fastify.d.ts.map +1 -0
  67. package/lib/payment/cryptopay/handler-fastify.js +69 -0
  68. package/lib/payment/cryptopay/handler-fastify.js.map +1 -0
  69. package/lib/payment/cryptopay/index.d.ts +4 -4
  70. package/lib/payment/cryptopay/index.d.ts.map +1 -1
  71. package/lib/payment/cryptopay/index.js +3 -4
  72. package/lib/payment/cryptopay/index.js.map +1 -1
  73. package/lib/payment/cryptopay/module.d.ts +54 -0
  74. package/lib/payment/cryptopay/module.d.ts.map +1 -0
  75. package/lib/payment/cryptopay/{module/configureCryptopayModule.js → module.js} +8 -3
  76. package/lib/payment/cryptopay/module.js.map +1 -0
  77. package/lib/payment/cryptopay/plugin.js +1 -1
  78. package/lib/payment/cryptopay/plugin.js.map +1 -1
  79. package/lib/payment/datatrans-v2/{middleware.d.ts → handler-express.d.ts} +1 -1
  80. package/lib/payment/datatrans-v2/handler-express.d.ts.map +1 -0
  81. package/lib/payment/datatrans-v2/{middleware.js → handler-express.js} +2 -2
  82. package/lib/payment/datatrans-v2/handler-express.js.map +1 -0
  83. package/lib/payment/datatrans-v2/handler-fastify.d.ts +3 -0
  84. package/lib/payment/datatrans-v2/handler-fastify.d.ts.map +1 -0
  85. package/lib/payment/datatrans-v2/handler-fastify.js +66 -0
  86. package/lib/payment/datatrans-v2/handler-fastify.js.map +1 -0
  87. package/lib/payment/datatrans-v2/index.d.ts +1 -1
  88. package/lib/payment/datatrans-v2/index.d.ts.map +1 -1
  89. package/lib/payment/datatrans-v2/index.js +0 -1
  90. package/lib/payment/datatrans-v2/index.js.map +1 -1
  91. package/lib/payment/payrexx/{middleware.d.ts → handler-express.d.ts} +1 -1
  92. package/lib/payment/payrexx/handler-express.d.ts.map +1 -0
  93. package/lib/payment/payrexx/{middleware.js → handler-express.js} +2 -2
  94. package/lib/payment/payrexx/handler-express.js.map +1 -0
  95. package/lib/payment/payrexx/handler-fastify.d.ts +3 -0
  96. package/lib/payment/payrexx/handler-fastify.d.ts.map +1 -0
  97. package/lib/payment/payrexx/handler-fastify.js +87 -0
  98. package/lib/payment/payrexx/handler-fastify.js.map +1 -0
  99. package/lib/payment/payrexx/index.d.ts +1 -1
  100. package/lib/payment/payrexx/index.d.ts.map +1 -1
  101. package/lib/payment/payrexx/index.js +0 -1
  102. package/lib/payment/payrexx/index.js.map +1 -1
  103. package/lib/payment/postfinance-checkout/{middleware.d.ts → handler-express.d.ts} +1 -1
  104. package/lib/payment/postfinance-checkout/handler-express.d.ts.map +1 -0
  105. package/lib/payment/postfinance-checkout/{middleware.js → handler-express.js} +1 -1
  106. package/lib/payment/postfinance-checkout/handler-express.js.map +1 -0
  107. package/lib/payment/postfinance-checkout/handler-fastify.d.ts +3 -0
  108. package/lib/payment/postfinance-checkout/handler-fastify.d.ts.map +1 -0
  109. package/lib/payment/postfinance-checkout/handler-fastify.js +39 -0
  110. package/lib/payment/postfinance-checkout/handler-fastify.js.map +1 -0
  111. package/lib/payment/postfinance-checkout/index.d.ts +1 -1
  112. package/lib/payment/postfinance-checkout/index.d.ts.map +1 -1
  113. package/lib/payment/postfinance-checkout/index.js +0 -1
  114. package/lib/payment/postfinance-checkout/index.js.map +1 -1
  115. package/lib/payment/saferpay/adapter.d.ts +0 -1
  116. package/lib/payment/saferpay/adapter.d.ts.map +1 -1
  117. package/lib/payment/saferpay/adapter.js +0 -1
  118. package/lib/payment/saferpay/adapter.js.map +1 -1
  119. package/lib/payment/saferpay/{middleware.d.ts → handler-express.d.ts} +1 -1
  120. package/lib/payment/saferpay/handler-express.d.ts.map +1 -0
  121. package/lib/payment/saferpay/{middleware.js → handler-express.js} +2 -2
  122. package/lib/payment/saferpay/handler-express.js.map +1 -0
  123. package/lib/payment/saferpay/handler-fastify.d.ts +3 -0
  124. package/lib/payment/saferpay/handler-fastify.d.ts.map +1 -0
  125. package/lib/payment/saferpay/handler-fastify.js +56 -0
  126. package/lib/payment/saferpay/handler-fastify.js.map +1 -0
  127. package/lib/payment/saferpay/index.d.ts +3 -2
  128. package/lib/payment/saferpay/index.d.ts.map +1 -1
  129. package/lib/payment/saferpay/index.js +2 -2
  130. package/lib/payment/saferpay/index.js.map +1 -1
  131. package/lib/payment/saferpay/module.d.ts +24 -0
  132. package/lib/payment/saferpay/module.d.ts.map +1 -0
  133. package/lib/payment/saferpay/{module/configureSaferpayTransactionsModule.js → module.js} +8 -3
  134. package/lib/payment/saferpay/module.js.map +1 -0
  135. package/lib/payment/stripe/{middleware.d.ts → handler-express.d.ts} +1 -1
  136. package/lib/payment/stripe/handler-express.d.ts.map +1 -0
  137. package/lib/payment/stripe/{middleware.js → handler-express.js} +2 -2
  138. package/lib/payment/stripe/handler-express.js.map +1 -0
  139. package/lib/payment/stripe/handler-fastify.d.ts +7 -0
  140. package/lib/payment/stripe/handler-fastify.d.ts.map +1 -0
  141. package/lib/payment/stripe/handler-fastify.js +112 -0
  142. package/lib/payment/stripe/handler-fastify.js.map +1 -0
  143. package/lib/payment/stripe/index.d.ts +1 -1
  144. package/lib/payment/stripe/index.d.ts.map +1 -1
  145. package/lib/payment/stripe/index.js +0 -1
  146. package/lib/payment/stripe/index.js.map +1 -1
  147. package/lib/presets/all-express.d.ts +7 -0
  148. package/lib/presets/all-express.d.ts.map +1 -0
  149. package/lib/presets/all-express.js +27 -0
  150. package/lib/presets/all-express.js.map +1 -0
  151. package/lib/presets/all-fastify.d.ts +7 -0
  152. package/lib/presets/all-fastify.d.ts.map +1 -0
  153. package/lib/presets/all-fastify.js +64 -0
  154. package/lib/presets/all-fastify.js.map +1 -0
  155. package/lib/presets/all.d.ts +69 -0
  156. package/lib/presets/all.d.ts.map +1 -0
  157. package/lib/presets/all.js +30 -0
  158. package/lib/presets/all.js.map +1 -0
  159. package/lib/presets/base-express.js +2 -2
  160. package/lib/presets/base-express.js.map +1 -1
  161. package/lib/presets/base-fastify.d.ts +4 -0
  162. package/lib/presets/base-fastify.d.ts.map +1 -0
  163. package/lib/presets/base-fastify.js +18 -0
  164. package/lib/presets/base-fastify.js.map +1 -0
  165. package/lib/presets/{base-modules.d.ts → base.d.ts} +3 -7
  166. package/lib/presets/base.d.ts.map +1 -0
  167. package/lib/presets/{base-modules.js → base.js} +3 -9
  168. package/lib/presets/base.js.map +1 -0
  169. package/lib/presets/crypto-express.js +2 -2
  170. package/lib/presets/crypto-express.js.map +1 -1
  171. package/lib/presets/crypto-fastify.d.ts +4 -0
  172. package/lib/presets/crypto-fastify.d.ts.map +1 -0
  173. package/lib/presets/crypto-fastify.js +12 -0
  174. package/lib/presets/crypto-fastify.js.map +1 -0
  175. package/lib/presets/crypto.d.ts +8 -0
  176. package/lib/presets/crypto.d.ts.map +1 -0
  177. package/lib/presets/crypto.js +9 -0
  178. package/lib/presets/crypto.js.map +1 -0
  179. package/package.json +18 -14
  180. package/src/files/gridfs/{gridfs-adapter.ts → adapter.ts} +1 -3
  181. package/src/files/gridfs/{gridfs-webhook.ts → handler-express.ts} +5 -3
  182. package/src/files/gridfs/handler-fastify.ts +122 -0
  183. package/src/files/gridfs/index.ts +4 -29
  184. package/src/files/gridfs/module.ts +39 -0
  185. package/src/files/minio/{minio-webhook.ts → handler-express.ts} +3 -1
  186. package/src/files/minio/handler-fastify.ts +44 -0
  187. package/src/payment/apple-iap/adapter.ts +6 -193
  188. package/src/payment/apple-iap/fix-periods.ts +34 -0
  189. package/src/payment/apple-iap/handler-express.ts +134 -0
  190. package/src/payment/apple-iap/handler-fastify.ts +137 -0
  191. package/src/payment/apple-iap/index.ts +5 -1
  192. package/src/payment/apple-iap/{module/configureAppleTransactionsModule.ts → module.ts} +10 -2
  193. package/src/payment/apple-iap/verify-receipt.ts +27 -0
  194. package/src/payment/cryptopay/{middleware.ts → handler-express.ts} +5 -5
  195. package/src/payment/cryptopay/handler-fastify.ts +89 -0
  196. package/src/payment/cryptopay/index.ts +5 -4
  197. package/src/payment/cryptopay/{module/configureCryptopayModule.ts → module.ts} +11 -3
  198. package/src/payment/cryptopay/plugin.ts +3 -3
  199. package/src/payment/datatrans-v2/{middleware.ts → handler-express.ts} +1 -1
  200. package/src/payment/datatrans-v2/handler-fastify.ts +88 -0
  201. package/src/payment/datatrans-v2/index.ts +0 -2
  202. package/src/payment/payrexx/{middleware.ts → handler-express.ts} +1 -1
  203. package/src/payment/payrexx/handler-fastify.ts +105 -0
  204. package/src/payment/payrexx/index.ts +0 -2
  205. package/src/payment/postfinance-checkout/handler-fastify.ts +52 -0
  206. package/src/payment/postfinance-checkout/index.ts +0 -2
  207. package/src/payment/saferpay/adapter.ts +2 -4
  208. package/src/payment/saferpay/{middleware.ts → handler-express.ts} +3 -3
  209. package/src/payment/saferpay/handler-fastify.ts +75 -0
  210. package/src/payment/saferpay/index.ts +5 -2
  211. package/src/payment/saferpay/{module/configureSaferpayTransactionsModule.ts → module.ts} +11 -3
  212. package/src/payment/stripe/{middleware.ts → handler-express.ts} +1 -1
  213. package/src/payment/stripe/handler-fastify.ts +139 -0
  214. package/src/payment/stripe/index.ts +0 -2
  215. package/src/presets/all-express.ts +51 -0
  216. package/src/presets/all-fastify.ts +87 -0
  217. package/src/presets/all.ts +35 -0
  218. package/src/presets/base-express.ts +2 -2
  219. package/src/presets/base-fastify.ts +20 -0
  220. package/src/presets/{base-modules.ts → base.ts} +3 -17
  221. package/src/presets/crypto-express.ts +2 -2
  222. package/src/presets/crypto-fastify.ts +15 -0
  223. package/src/presets/crypto.ts +11 -0
  224. package/lib/files/gridfs/gridfs-adapter.d.ts.map +0 -1
  225. package/lib/files/gridfs/gridfs-adapter.js.map +0 -1
  226. package/lib/files/gridfs/gridfs-webhook.d.ts +0 -11
  227. package/lib/files/gridfs/gridfs-webhook.d.ts.map +0 -1
  228. package/lib/files/gridfs/gridfs-webhook.js.map +0 -1
  229. package/lib/files/minio/minio-adapter.d.ts.map +0 -1
  230. package/lib/files/minio/minio-adapter.js.map +0 -1
  231. package/lib/files/minio/minio-webhook.d.ts +0 -2
  232. package/lib/files/minio/minio-webhook.d.ts.map +0 -1
  233. package/lib/files/minio/minio-webhook.js.map +0 -1
  234. package/lib/payment/apple-iap/module/configureAppleTransactionsModule.d.ts +0 -9
  235. package/lib/payment/apple-iap/module/configureAppleTransactionsModule.d.ts.map +0 -1
  236. package/lib/payment/apple-iap/module/configureAppleTransactionsModule.js.map +0 -1
  237. package/lib/payment/cryptopay/middleware.d.ts +0 -2
  238. package/lib/payment/cryptopay/middleware.d.ts.map +0 -1
  239. package/lib/payment/cryptopay/module/configureCryptopayModule.d.ts +0 -25
  240. package/lib/payment/cryptopay/module/configureCryptopayModule.d.ts.map +0 -1
  241. package/lib/payment/cryptopay/module/configureCryptopayModule.js.map +0 -1
  242. package/lib/payment/datatrans-v2/middleware.d.ts.map +0 -1
  243. package/lib/payment/datatrans-v2/middleware.js.map +0 -1
  244. package/lib/payment/payrexx/middleware.d.ts.map +0 -1
  245. package/lib/payment/payrexx/middleware.js.map +0 -1
  246. package/lib/payment/postfinance-checkout/middleware.d.ts.map +0 -1
  247. package/lib/payment/postfinance-checkout/middleware.js.map +0 -1
  248. package/lib/payment/saferpay/middleware.d.ts.map +0 -1
  249. package/lib/payment/saferpay/middleware.js.map +0 -1
  250. package/lib/payment/saferpay/module/configureSaferpayTransactionsModule.d.ts +0 -10
  251. package/lib/payment/saferpay/module/configureSaferpayTransactionsModule.d.ts.map +0 -1
  252. package/lib/payment/saferpay/module/configureSaferpayTransactionsModule.js.map +0 -1
  253. package/lib/payment/stripe/middleware.d.ts.map +0 -1
  254. package/lib/payment/stripe/middleware.js.map +0 -1
  255. package/lib/plugins-index.d.ts +0 -18
  256. package/lib/plugins-index.d.ts.map +0 -1
  257. package/lib/plugins-index.js +0 -52
  258. package/lib/plugins-index.js.map +0 -1
  259. package/lib/presets/base-modules.d.ts.map +0 -1
  260. package/lib/presets/base-modules.js.map +0 -1
  261. package/lib/presets/crypto-modules.d.ts +0 -11
  262. package/lib/presets/crypto-modules.d.ts.map +0 -1
  263. package/lib/presets/crypto-modules.js +0 -14
  264. package/lib/presets/crypto-modules.js.map +0 -1
  265. package/src/plugins-index.ts +0 -90
  266. package/src/presets/crypto-modules.ts +0 -23
  267. /package/src/files/minio/{minio-adapter.ts → index.ts} +0 -0
  268. /package/src/payment/postfinance-checkout/{middleware.ts → handler-express.ts} +0 -0
@@ -0,0 +1,134 @@
1
+ import { Context } from '@unchainedshop/api';
2
+ import { EnrollmentStatus } from '@unchainedshop/core-enrollments';
3
+ import { createLogger } from '@unchainedshop/logger';
4
+ import { fixPeriods } from './fix-periods.js';
5
+
6
+ const logger = createLogger('unchained:core-payment:iap');
7
+
8
+ const { APPLE_IAP_SHARED_SECRET } = process.env;
9
+
10
+ const AppleNotificationTypes = {
11
+ INITIAL_BUY: 'INITIAL_BUY',
12
+ DID_RECOVER: 'DID_RECOVER',
13
+ DID_CHANGE_RENEWAL_STATUS: 'DID_CHANGE_RENEWAL_STATUS',
14
+ DID_FAIL_TO_RENEW: 'DID_FAIL_TO_RENEW',
15
+ DID_CHANGE_RENEWAL_PREF: 'DID_CHANGE_RENEWAL_PREF',
16
+ };
17
+
18
+ export const appleIAPHandler = async (req, res) => {
19
+ if (req.method === 'POST') {
20
+ try {
21
+ const resolvedContext = req.unchainedContext as Context;
22
+ const { modules, services } = resolvedContext;
23
+ const responseBody = req.body || {};
24
+ if (responseBody.password !== APPLE_IAP_SHARED_SECRET) {
25
+ throw new Error('shared secret not valid');
26
+ }
27
+
28
+ const transactions = responseBody?.unified_receipt?.latest_receipt_info; // eslint-disable-line
29
+ const latestTransaction = transactions[0];
30
+
31
+ if (responseBody.notification_type === AppleNotificationTypes.INITIAL_BUY) {
32
+ // Find the cart to checkout
33
+ const orderPayment = await modules.orders.payments.findOrderPaymentByContextData({
34
+ context: {
35
+ 'meta.transactionIdentifier': latestTransaction.transaction_id,
36
+ },
37
+ });
38
+
39
+ if (!orderPayment) throw new Error('Could not find any matching order payment');
40
+
41
+ const order = await services.orders.checkoutOrder(orderPayment.orderId, {
42
+ paymentContext: {
43
+ receiptData: responseBody?.unified_receipt?.latest_receipt, // eslint-disable-line
44
+ },
45
+ });
46
+ const orderId = order._id;
47
+ const enrollment = await modules.enrollments.findEnrollment({
48
+ orderId,
49
+ });
50
+ await fixPeriods(
51
+ {
52
+ transactionId: latestTransaction.original_transaction_id,
53
+ transactions,
54
+ enrollmentId: enrollment._id,
55
+ orderId,
56
+ },
57
+ resolvedContext,
58
+ );
59
+
60
+ logger.info(`Apple IAP Webhook: Confirmed checkout for order ${order.orderNumber}`, {
61
+ orderId: order._id,
62
+ });
63
+ } else {
64
+ // Just store payment credentials, use the enrollments paymentProvider reference and
65
+ // let the job do the rest
66
+ const originalOrderPayment = await modules.orders.payments.findOrderPaymentByContextData({
67
+ context: {
68
+ 'meta.transactionIdentifier': latestTransaction.original_transaction_id,
69
+ },
70
+ });
71
+ if (!originalOrderPayment) throw new Error('Could not find any matching order payment');
72
+ const originalOrder = await modules.orders.findOrder({
73
+ orderId: originalOrderPayment.orderId,
74
+ });
75
+ const enrollment = await modules.enrollments.findEnrollment({
76
+ orderId: originalOrder._id,
77
+ });
78
+
79
+ await services.orders.registerPaymentCredentials(enrollment.payment.paymentProviderId, {
80
+ transactionContext: {
81
+ receiptData: responseBody?.unified_receipt?.latest_receipt, // eslint-disable-line
82
+ },
83
+ userId: enrollment.userId,
84
+ });
85
+
86
+ await fixPeriods(
87
+ {
88
+ transactionId: latestTransaction.original_transaction_id,
89
+ transactions,
90
+ enrollmentId: enrollment._id,
91
+ orderId: originalOrder._id,
92
+ },
93
+ resolvedContext,
94
+ );
95
+
96
+ logger.info(
97
+ `Apple IAP Webhook: Processed notification for ${latestTransaction.original_transaction_id} and type ${responseBody.notification_type}`,
98
+ );
99
+
100
+ if (responseBody.notification_type === AppleNotificationTypes.DID_RECOVER) {
101
+ if (
102
+ enrollment.status !== EnrollmentStatus.TERMINATED &&
103
+ responseBody.auto_renew_status === 'false'
104
+ ) {
105
+ await services.enrollments.terminateEnrollment(enrollment);
106
+ }
107
+ }
108
+
109
+ if (responseBody.notification_type === AppleNotificationTypes.DID_CHANGE_RENEWAL_STATUS) {
110
+ if (
111
+ enrollment.status !== EnrollmentStatus.TERMINATED &&
112
+ responseBody.auto_renew_status === 'false'
113
+ ) {
114
+ await services.enrollments.terminateEnrollment(enrollment);
115
+ }
116
+ }
117
+ logger.info(`Apple IAP Webhook: Updated enrollment from Apple`);
118
+ }
119
+
120
+ res.writeHead(200);
121
+ res.end();
122
+ return;
123
+ } catch (e) {
124
+ logger.warn(`Apple IAP Webhook: ${e.message}`, e);
125
+ res.writeHead(503);
126
+ res.end(JSON.stringify({ name: e.name, code: e.code, message: e.message }));
127
+ return;
128
+ }
129
+ }
130
+ res.writeHead(404);
131
+ res.end();
132
+ };
133
+
134
+ export default appleIAPHandler;
@@ -0,0 +1,137 @@
1
+ import { Context } from '@unchainedshop/api';
2
+ import { EnrollmentStatus } from '@unchainedshop/core-enrollments';
3
+ import { createLogger } from '@unchainedshop/logger';
4
+ import { fixPeriods } from './fix-periods.js';
5
+ import { FastifyRequest, RouteHandlerMethod } from 'fastify';
6
+ import { AppleTransactionsModule } from './module.js';
7
+
8
+ const logger = createLogger('unchained:core-payment:iap');
9
+
10
+ const { APPLE_IAP_SHARED_SECRET } = process.env;
11
+
12
+ const AppleNotificationTypes = {
13
+ INITIAL_BUY: 'INITIAL_BUY',
14
+ DID_RECOVER: 'DID_RECOVER',
15
+ DID_CHANGE_RENEWAL_STATUS: 'DID_CHANGE_RENEWAL_STATUS',
16
+ DID_FAIL_TO_RENEW: 'DID_FAIL_TO_RENEW',
17
+ DID_CHANGE_RENEWAL_PREF: 'DID_CHANGE_RENEWAL_PREF',
18
+ };
19
+
20
+ export const appleIAPHandler: RouteHandlerMethod = async (
21
+ req: FastifyRequest & {
22
+ unchainedContext: Context & {
23
+ modules: AppleTransactionsModule;
24
+ };
25
+ },
26
+ reply,
27
+ ) => {
28
+ try {
29
+ const resolvedContext = req.unchainedContext as Context;
30
+ const { modules, services } = resolvedContext;
31
+ const responseBody: Record<string, any> = req.body || {};
32
+ if (responseBody.password !== APPLE_IAP_SHARED_SECRET) {
33
+ throw new Error('shared secret not valid');
34
+ }
35
+
36
+ const transactions = responseBody?.unified_receipt?.latest_receipt_info; // eslint-disable-line
37
+ const latestTransaction = transactions[0];
38
+
39
+ if (responseBody.notification_type === AppleNotificationTypes.INITIAL_BUY) {
40
+ // Find the cart to checkout
41
+ const orderPayment = await modules.orders.payments.findOrderPaymentByContextData({
42
+ context: {
43
+ 'meta.transactionIdentifier': latestTransaction.transaction_id,
44
+ },
45
+ });
46
+
47
+ if (!orderPayment) throw new Error('Could not find any matching order payment');
48
+
49
+ const order = await services.orders.checkoutOrder(orderPayment.orderId, {
50
+ paymentContext: {
51
+ receiptData: responseBody?.unified_receipt?.latest_receipt, // eslint-disable-line
52
+ },
53
+ });
54
+ const orderId = order._id;
55
+ const enrollment = await modules.enrollments.findEnrollment({
56
+ orderId,
57
+ });
58
+ await fixPeriods(
59
+ {
60
+ transactionId: latestTransaction.original_transaction_id,
61
+ transactions,
62
+ enrollmentId: enrollment._id,
63
+ orderId,
64
+ },
65
+ resolvedContext,
66
+ );
67
+
68
+ logger.info(`Apple IAP Webhook: Confirmed checkout for order ${order.orderNumber}`, {
69
+ orderId: order._id,
70
+ });
71
+ } else {
72
+ // Just store payment credentials, use the enrollments paymentProvider reference and
73
+ // let the job do the rest
74
+ const originalOrderPayment = await modules.orders.payments.findOrderPaymentByContextData({
75
+ context: {
76
+ 'meta.transactionIdentifier': latestTransaction.original_transaction_id,
77
+ },
78
+ });
79
+ if (!originalOrderPayment) throw new Error('Could not find any matching order payment');
80
+ const originalOrder = await modules.orders.findOrder({
81
+ orderId: originalOrderPayment.orderId,
82
+ });
83
+ const enrollment = await modules.enrollments.findEnrollment({
84
+ orderId: originalOrder._id,
85
+ });
86
+
87
+ await services.orders.registerPaymentCredentials(enrollment.payment.paymentProviderId, {
88
+ transactionContext: {
89
+ receiptData: responseBody?.unified_receipt?.latest_receipt, // eslint-disable-line
90
+ },
91
+ userId: enrollment.userId,
92
+ });
93
+
94
+ await fixPeriods(
95
+ {
96
+ transactionId: latestTransaction.original_transaction_id,
97
+ transactions,
98
+ enrollmentId: enrollment._id,
99
+ orderId: originalOrder._id,
100
+ },
101
+ resolvedContext,
102
+ );
103
+
104
+ logger.info(
105
+ `Apple IAP Webhook: Processed notification for ${latestTransaction.original_transaction_id} and type ${responseBody.notification_type}`,
106
+ );
107
+
108
+ if (responseBody.notification_type === AppleNotificationTypes.DID_RECOVER) {
109
+ if (
110
+ enrollment.status !== EnrollmentStatus.TERMINATED &&
111
+ responseBody.auto_renew_status === 'false'
112
+ ) {
113
+ await services.enrollments.terminateEnrollment(enrollment);
114
+ }
115
+ }
116
+
117
+ if (responseBody.notification_type === AppleNotificationTypes.DID_CHANGE_RENEWAL_STATUS) {
118
+ if (
119
+ enrollment.status !== EnrollmentStatus.TERMINATED &&
120
+ responseBody.auto_renew_status === 'false'
121
+ ) {
122
+ await services.enrollments.terminateEnrollment(enrollment);
123
+ }
124
+ }
125
+ logger.info(`Apple IAP Webhook: Updated enrollment from Apple`);
126
+ }
127
+
128
+ reply.status(200);
129
+ return reply.send();
130
+ } catch (e) {
131
+ logger.warn(`Apple IAP Webhook: ${e.message}`, e);
132
+ reply.status(503);
133
+ return reply.send({ name: e.name, code: e.code, message: e.message });
134
+ }
135
+ };
136
+
137
+ export default appleIAPHandler;
@@ -1,2 +1,6 @@
1
1
  export * from './adapter.js';
2
- export * from './module/configureAppleTransactionsModule.js';
2
+
3
+ import appleTransactionsModule, { type AppleTransactionsModule } from './module.js';
4
+
5
+ export type { AppleTransactionsModule };
6
+ export default appleTransactionsModule;
@@ -1,4 +1,4 @@
1
- import { AppleTransaction, AppleTransactionsCollection } from '../db/AppleTransactionsCollection.js';
1
+ import { AppleTransaction, AppleTransactionsCollection } from './db/AppleTransactionsCollection.js';
2
2
 
3
3
  export const configureAppleTransactionsModule = async ({ db }) => {
4
4
  const AppleTransactions = await AppleTransactionsCollection(db);
@@ -18,4 +18,12 @@ export const configureAppleTransactionsModule = async ({ db }) => {
18
18
  };
19
19
  };
20
20
 
21
- export type AppleTransactionsModule = Awaited<ReturnType<typeof configureAppleTransactionsModule>>;
21
+ export default {
22
+ appleTransactions: {
23
+ configure: configureAppleTransactionsModule,
24
+ },
25
+ };
26
+
27
+ export type AppleTransactionsModule = {
28
+ appleTransactions: Awaited<ReturnType<typeof configureAppleTransactionsModule>>;
29
+ };
@@ -0,0 +1,27 @@
1
+ const { APPLE_IAP_ENVIRONMENT = 'sandbox' } = process.env;
2
+
3
+ // https://developer.apple.com/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store
4
+ const environments = {
5
+ sandbox: 'https://sandbox.itunes.apple.com/verifyReceipt',
6
+ production: 'https://buy.itunes.apple.com/verifyReceipt',
7
+ };
8
+
9
+ export const verifyReceipt = async ({ receiptData, password }): Promise<any> => {
10
+ const payload: any = {
11
+ 'receipt-data': receiptData,
12
+ };
13
+ if (password) {
14
+ payload.password = password;
15
+ }
16
+ const result = await fetch(environments[APPLE_IAP_ENVIRONMENT], {
17
+ body: JSON.stringify(payload),
18
+ method: 'POST',
19
+ // eslint-disable-next-line
20
+ // @ts-ignore
21
+ duplex: 'half',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ });
26
+ return result.json();
27
+ };
@@ -1,18 +1,16 @@
1
1
  import { createLogger } from '@unchainedshop/logger';
2
2
  import { Context } from '@unchainedshop/api';
3
3
  import { OrderStatus } from '@unchainedshop/core-orders';
4
- import { CryptopayModule } from './module/configureCryptopayModule.js';
4
+ import { CryptopayModule } from './module.js';
5
5
  import { ProductPriceRate } from '@unchainedshop/core-products';
6
6
 
7
7
  const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
8
8
 
9
9
  const logger = createLogger('unchained:core-payment:cryptopay');
10
10
 
11
- export const cryptopayHandler = async (req, res) => {
11
+ const cryptopayHandler = async (req, res) => {
12
12
  const resolvedContext = req.unchainedContext as Context & {
13
- modules: {
14
- cryptopay: CryptopayModule;
15
- };
13
+ modules: CryptopayModule;
16
14
  };
17
15
  const { modules, services } = resolvedContext;
18
16
 
@@ -88,3 +86,5 @@ export const cryptopayHandler = async (req, res) => {
88
86
  res.writeHead(404);
89
87
  res.end(JSON.stringify({ success: false }));
90
88
  };
89
+
90
+ export default cryptopayHandler;
@@ -0,0 +1,89 @@
1
+ import { FastifyRequest, RouteHandlerMethod } from 'fastify';
2
+ import { createLogger } from '@unchainedshop/logger';
3
+ import { Context } from '@unchainedshop/api';
4
+ import { OrderStatus } from '@unchainedshop/core-orders';
5
+ import { CryptopayModule } from './module.js';
6
+ import { ProductPriceRate } from '@unchainedshop/core-products';
7
+
8
+ const { CRYPTOPAY_SECRET, CRYPTOPAY_MAX_RATE_AGE = '360' } = process.env;
9
+
10
+ const logger = createLogger('unchained:core-payment:cryptopay');
11
+
12
+ const cryptopayHandler: RouteHandlerMethod = async (
13
+ req: FastifyRequest & {
14
+ unchainedContext: Context & {
15
+ modules: CryptopayModule;
16
+ };
17
+ },
18
+ reply,
19
+ ) => {
20
+ const { modules, services } = req.unchainedContext;
21
+
22
+ try {
23
+ const { secret, price, wallet, ping } = req.body as Record<string, any>;
24
+ if (secret !== CRYPTOPAY_SECRET) {
25
+ logger.warn(`Cryptopay Plugin: Invalid Cryptopay Secret provided`);
26
+ throw new Error('Secret invalid');
27
+ }
28
+
29
+ if (wallet) {
30
+ const { address, blockHeight, amount, contract, decimals, currency } = wallet;
31
+ const { orderPaymentId } = await modules.cryptopay.updateWalletAddress({
32
+ addressId: address,
33
+ blockHeight,
34
+ amount,
35
+ contract,
36
+ decimals,
37
+ currency,
38
+ });
39
+ const orderPayment =
40
+ orderPaymentId &&
41
+ (await modules.orders.payments.findOrderPayment({
42
+ orderPaymentId,
43
+ }));
44
+
45
+ if (orderPayment) {
46
+ // Try to process order to next status
47
+ // TODO: Not sure if it's correct to use processOrder here if status is PENDING!
48
+ const order = await modules.orders.findOrder({ orderId: orderPayment.orderId });
49
+ if (order.status === null) {
50
+ await services.orders.checkoutOrder(order._id, {});
51
+ } else if (order.status === OrderStatus.PENDING) {
52
+ await services.orders.processOrder(order, {});
53
+ } else {
54
+ throw new Error('Already processed');
55
+ }
56
+ }
57
+ }
58
+
59
+ if (price) {
60
+ const { baseCurrency, token, rate, timestamp } = price;
61
+
62
+ const timestampDate = new Date(timestamp);
63
+ const expiresAt = new Date(new Date().getTime() + parseInt(CRYPTOPAY_MAX_RATE_AGE, 10) * 1000);
64
+
65
+ const rateData: ProductPriceRate = {
66
+ baseCurrency,
67
+ quoteCurrency: token,
68
+ rate,
69
+ expiresAt,
70
+ timestamp: timestampDate,
71
+ };
72
+ logger.info(`update rate ${JSON.stringify(price)}, ${JSON.stringify(rateData)}`);
73
+ await modules.products.prices.rates.updateRates([rateData]);
74
+ }
75
+
76
+ if (ping) {
77
+ const { blockHeight, currency } = ping;
78
+ await modules.cryptopay.updateMostRecentBlock(currency, blockHeight);
79
+ }
80
+
81
+ reply.status(200);
82
+ return reply.send({ success: true });
83
+ } catch (e) {
84
+ reply.status(500);
85
+ return reply.send({ success: false, error: e.message });
86
+ }
87
+ };
88
+
89
+ export default cryptopayHandler;
@@ -1,6 +1,7 @@
1
- import CryptopayPlugin from './plugin.js';
1
+ import './plugin.js';
2
2
 
3
- export * from './middleware.js';
4
- export * from './module/configureCryptopayModule.js';
3
+ import cryptopayModules, { type CryptopayModule } from './module.js';
5
4
 
6
- export default CryptopayPlugin;
5
+ export type { CryptopayModule };
6
+
7
+ export default cryptopayModules;
@@ -1,7 +1,7 @@
1
1
  import { mongodb } from '@unchainedshop/mongodb';
2
- import { CryptopayTransaction, CryptopayTransactionsCollection } from '../db/CryptopayTransactions.js';
2
+ import { CryptopayTransaction, CryptopayTransactionsCollection } from './db/CryptopayTransactions.js';
3
3
 
4
- export const configureCryptopayModule = ({ db }) => {
4
+ const configureCryptopayModule = ({ db }) => {
5
5
  const CryptoTransactions = CryptopayTransactionsCollection(db);
6
6
 
7
7
  const getWalletAddress = async (addressId: string): Promise<CryptopayTransaction> => {
@@ -120,4 +120,12 @@ export const configureCryptopayModule = ({ db }) => {
120
120
  };
121
121
  };
122
122
 
123
- export type CryptopayModule = ReturnType<typeof configureCryptopayModule>;
123
+ export default {
124
+ cryptopay: {
125
+ configure: configureCryptopayModule,
126
+ },
127
+ };
128
+
129
+ export type CryptopayModule = {
130
+ cryptopay: ReturnType<typeof configureCryptopayModule>;
131
+ };
@@ -10,7 +10,7 @@ import {
10
10
  PaymentError,
11
11
  OrderPricingSheet,
12
12
  } from '@unchainedshop/core';
13
- import { CryptopayModule } from './module/configureCryptopayModule.js';
13
+ import { CryptopayModule } from './module.js';
14
14
 
15
15
  const logger = createLogger('unchained:core-payment:cryptopay');
16
16
 
@@ -59,7 +59,7 @@ const Cryptopay: IPaymentAdapter = {
59
59
  },
60
60
 
61
61
  actions: (config, context) => {
62
- const { modules } = context as typeof context & { modules: { cryptopay: CryptopayModule } };
62
+ const { modules } = context as typeof context & { modules: CryptopayModule };
63
63
 
64
64
  const setConversionRates = async (currencyCode: string, existingAddresses: any[]) => {
65
65
  const originCurrencyObj = await modules.currencies.findCurrency({ isoCode: currencyCode });
@@ -267,7 +267,7 @@ const Cryptopay: IPaymentAdapter = {
267
267
 
268
268
  1. Check that generated crypto addresses are unique and no order payments share the same crypto address, only repeat when derivations completely exhausted.
269
269
  2. When derivation path get's close to "exhaustedness" (95%), send a special e-mail alert to tell the user should generate a new xpub.
270
- 3. Extend the middleware to report the highest seen block number so we can calculate confirmations,
270
+ 3. Extend the handler to report the highest seen block number so we can calculate confirmations,
271
271
  4. Order rejection should trigger an e-mail advising the vendor to actively send back funds
272
272
 
273
273
  To allow paying in ETH or BTC or Tokens for CHF orders, it should work like this:
@@ -9,7 +9,7 @@ const {
9
9
  DATATRANS_SECURITY = Security.DYNAMIC_SIGN,
10
10
  } = process.env;
11
11
 
12
- const logger = createLogger('unchained:core-payment:datatrans:webhook');
12
+ const logger = createLogger('unchained:core-payment:datatrans:handler');
13
13
 
14
14
  export const datatransHandler = async (req, res) => {
15
15
  const resolvedContext = req.unchainedContext as Context;
@@ -0,0 +1,88 @@
1
+ import { createLogger } from '@unchainedshop/logger';
2
+ import { Context } from '@unchainedshop/api';
3
+ import generateSignature, { Security } from './generateSignature.js';
4
+ import { StatusResponseSuccess } from './api/types.js';
5
+ import { FastifyRequest, RouteHandlerMethod } from 'fastify';
6
+
7
+ const {
8
+ DATATRANS_SIGN_KEY,
9
+ DATATRANS_SIGN2_KEY,
10
+ DATATRANS_SECURITY = Security.DYNAMIC_SIGN,
11
+ } = process.env;
12
+
13
+ const logger = createLogger('unchained:core-payment:datatrans:handler');
14
+
15
+ export const datatransHandler: RouteHandlerMethod = async (
16
+ req: FastifyRequest & {
17
+ unchainedContext: Context;
18
+ },
19
+ reply,
20
+ ) => {
21
+ const resolvedContext = req.unchainedContext as Context;
22
+ const { modules, services } = resolvedContext;
23
+ const signature = req.headers['datatrans-signature'] as string;
24
+ if (signature) {
25
+ const [rawTimestamp, rawHash] = signature.split(',');
26
+ const [, hash] = rawHash.split('=');
27
+ const [, timestamp] = rawTimestamp.split('=');
28
+
29
+ const comparableSignature = await generateSignature({
30
+ security: DATATRANS_SECURITY as any,
31
+ signKey: DATATRANS_SIGN2_KEY || DATATRANS_SIGN_KEY,
32
+ })(timestamp, req.body as string);
33
+
34
+ if (hash !== comparableSignature) {
35
+ logger.error(`hash mismatch: ${signature} / ${comparableSignature}`, req.body as string);
36
+ reply.status(403);
37
+ return reply.send('Hash mismatch');
38
+ }
39
+
40
+ const transaction: StatusResponseSuccess = JSON.parse(req.body as string) as StatusResponseSuccess;
41
+
42
+ logger.info(`received request`, {
43
+ type: transaction.type,
44
+ });
45
+
46
+ if (transaction.status === 'authorized') {
47
+ const userId = transaction.refno2;
48
+ const referenceId = Buffer.from(transaction.refno, 'base64').toString('hex');
49
+
50
+ try {
51
+ if (transaction.type === 'card_check') {
52
+ const paymentProviderId = referenceId;
53
+ const paymentCredentials = await services.orders.registerPaymentCredentials(
54
+ paymentProviderId,
55
+ { userId, transactionContext: { transactionId: transaction.transactionId } },
56
+ );
57
+ logger.info(`registered payment credentials for ${userId}`, {
58
+ userId,
59
+ });
60
+ reply.status(200);
61
+ return reply.send(paymentCredentials);
62
+ }
63
+ if (transaction.type === 'payment') {
64
+ const orderPaymentId = referenceId;
65
+ const orderPayment = await modules.orders.payments.findOrderPayment({
66
+ orderPaymentId,
67
+ });
68
+ if (!orderPayment) throw new Error(`Order Payment with id ${orderPaymentId} not found`);
69
+
70
+ const order = await services.orders.checkoutOrder(orderPayment.orderId, {
71
+ paymentContext: { userId, transactionId: transaction.transactionId },
72
+ });
73
+ reply.status(200);
74
+ logger.info(`confirmed checkout for order ${order.orderNumber}`, {
75
+ orderId: order._id,
76
+ });
77
+ return reply.send(order);
78
+ }
79
+ } catch (e) {
80
+ logger.error(`rejected to checkout with message`, e);
81
+ reply.status(500);
82
+ return reply.send({ name: e.name, code: e.code, message: e.message });
83
+ }
84
+ }
85
+ }
86
+ reply.status(404);
87
+ return reply.send();
88
+ };
@@ -20,8 +20,6 @@ import {
20
20
  OrderPricingSheet,
21
21
  } from '@unchainedshop/core';
22
22
 
23
- export * from './middleware.js';
24
-
25
23
  const logger = createLogger('unchained:core-payment:datatrans');
26
24
 
27
25
  // v2
@@ -1,7 +1,7 @@
1
1
  import { Context } from '@unchainedshop/api';
2
2
  import { createLogger } from '@unchainedshop/logger';
3
3
 
4
- const logger = createLogger('unchained:core-payment:payrexx:webhook');
4
+ const logger = createLogger('unchained:core-payment:payrexx:handler');
5
5
 
6
6
  export const payrexxHandler = async (request, response) => {
7
7
  const resolvedContext = request.unchainedContext as Context;