@reactionary/source 0.0.52 → 0.2.16

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 (293) hide show
  1. package/.env-template +19 -0
  2. package/.github/workflows/pull-request.yml +3 -1
  3. package/.github/workflows/release.yml +9 -0
  4. package/.vscode/extensions.json +0 -2
  5. package/LICENSE +21 -0
  6. package/README.md +175 -23
  7. package/core/package.json +6 -3
  8. package/core/src/cache/cache.interface.ts +1 -0
  9. package/core/src/cache/index.ts +4 -0
  10. package/core/src/cache/memory-cache.ts +30 -2
  11. package/core/src/cache/noop-cache.ts +15 -1
  12. package/core/src/cache/redis-cache.ts +20 -0
  13. package/core/src/client/client-builder.ts +71 -54
  14. package/core/src/client/client.ts +9 -47
  15. package/core/src/client/index.ts +2 -0
  16. package/core/src/decorators/index.ts +1 -0
  17. package/core/src/decorators/reactionary.decorator.ts +203 -34
  18. package/core/src/index.ts +6 -19
  19. package/core/src/initialization.ts +1 -18
  20. package/core/src/metrics/metrics.ts +67 -0
  21. package/core/src/providers/analytics.provider.ts +1 -6
  22. package/core/src/providers/base.provider.ts +5 -69
  23. package/core/src/providers/cart.provider.ts +15 -55
  24. package/core/src/providers/category.provider.ts +7 -11
  25. package/core/src/providers/checkout.provider.ts +17 -15
  26. package/core/src/providers/identity.provider.ts +6 -8
  27. package/core/src/providers/index.ts +2 -1
  28. package/core/src/providers/inventory.provider.ts +15 -5
  29. package/core/src/providers/order-search.provider.ts +29 -0
  30. package/core/src/providers/order.provider.ts +47 -15
  31. package/core/src/providers/price.provider.ts +30 -36
  32. package/core/src/providers/product-search.provider.ts +61 -0
  33. package/core/src/providers/product.provider.ts +71 -12
  34. package/core/src/providers/profile.provider.ts +74 -14
  35. package/core/src/providers/store.provider.ts +3 -5
  36. package/core/src/schemas/capabilities.schema.ts +10 -3
  37. package/core/src/schemas/errors/generic.error.ts +9 -0
  38. package/core/src/schemas/errors/index.ts +4 -0
  39. package/core/src/schemas/errors/invalid-input.error.ts +9 -0
  40. package/core/src/schemas/errors/invalid-output.error.ts +9 -0
  41. package/core/src/schemas/errors/not-found.error.ts +9 -0
  42. package/core/src/schemas/index.ts +7 -0
  43. package/core/src/schemas/models/analytics.model.ts +2 -1
  44. package/core/src/schemas/models/base.model.ts +6 -24
  45. package/core/src/schemas/models/cart.model.ts +5 -8
  46. package/core/src/schemas/models/category.model.ts +4 -9
  47. package/core/src/schemas/models/checkout.model.ts +6 -7
  48. package/core/src/schemas/models/cost.model.ts +4 -3
  49. package/core/src/schemas/models/currency.model.ts +2 -1
  50. package/core/src/schemas/models/identifiers.model.ts +106 -62
  51. package/core/src/schemas/models/identity.model.ts +10 -19
  52. package/core/src/schemas/models/index.ts +2 -1
  53. package/core/src/schemas/models/inventory.model.ts +8 -5
  54. package/core/src/schemas/models/order-search.model.ts +28 -0
  55. package/core/src/schemas/models/order.model.ts +20 -26
  56. package/core/src/schemas/models/payment.model.ts +14 -17
  57. package/core/src/schemas/models/price.model.ts +11 -11
  58. package/core/src/schemas/models/product-search.model.ts +42 -0
  59. package/core/src/schemas/models/product.model.ts +64 -22
  60. package/core/src/schemas/models/profile.model.ts +19 -22
  61. package/core/src/schemas/models/shipping-method.model.ts +24 -29
  62. package/core/src/schemas/models/store.model.ts +9 -5
  63. package/core/src/schemas/mutations/analytics.mutation.ts +8 -7
  64. package/core/src/schemas/mutations/base.mutation.ts +2 -1
  65. package/core/src/schemas/mutations/cart.mutation.ts +33 -33
  66. package/core/src/schemas/mutations/checkout.mutation.ts +23 -30
  67. package/core/src/schemas/mutations/identity.mutation.ts +4 -3
  68. package/core/src/schemas/mutations/profile.mutation.ts +38 -3
  69. package/core/src/schemas/queries/base.query.ts +2 -1
  70. package/core/src/schemas/queries/cart.query.ts +3 -3
  71. package/core/src/schemas/queries/category.query.ts +18 -18
  72. package/core/src/schemas/queries/checkout.query.ts +7 -9
  73. package/core/src/schemas/queries/identity.query.ts +2 -1
  74. package/core/src/schemas/queries/index.ts +2 -1
  75. package/core/src/schemas/queries/inventory.query.ts +5 -5
  76. package/core/src/schemas/queries/order-search.query.ts +10 -0
  77. package/core/src/schemas/queries/order.query.ts +3 -2
  78. package/core/src/schemas/queries/price.query.ts +10 -4
  79. package/core/src/schemas/queries/product-search.query.ts +16 -0
  80. package/core/src/schemas/queries/product.query.ts +13 -6
  81. package/core/src/schemas/queries/profile.query.ts +5 -2
  82. package/core/src/schemas/queries/store.query.ts +6 -5
  83. package/core/src/schemas/result.ts +107 -0
  84. package/core/src/schemas/session.schema.ts +4 -4
  85. package/core/src/test/reactionary.decorator.spec.ts +249 -0
  86. package/core/src/zod-utils.ts +19 -0
  87. package/core/tsconfig.json +1 -1
  88. package/core/tsconfig.spec.json +2 -26
  89. package/core/vitest.config.ts +14 -0
  90. package/documentation/1-purpose.md +114 -0
  91. package/documentation/2-getting-started.md +229 -0
  92. package/documentation/3-querying-and-changing-data.md +74 -0
  93. package/documentation/4-product-data.md +107 -0
  94. package/documentation/5-cart-and-checkout.md +211 -0
  95. package/documentation/6-product-search.md +143 -0
  96. package/documentation/7-marketing.md +3 -0
  97. package/eslint.config.mjs +1 -0
  98. package/examples/node/eslint.config.mjs +1 -4
  99. package/examples/node/package.json +10 -3
  100. package/examples/node/project.json +4 -1
  101. package/examples/node/src/basic/basic-node-provider-model-extension.spec.ts +22 -23
  102. package/examples/node/src/basic/basic-node-provider-query-extension.spec.ts +15 -11
  103. package/examples/node/src/basic/basic-node-setup.spec.ts +44 -28
  104. package/examples/node/src/basic/client-creation.spec.ts +53 -0
  105. package/examples/node/src/capabilities/cart.spec.ts +255 -0
  106. package/examples/node/src/capabilities/category.spec.ts +193 -0
  107. package/examples/node/src/capabilities/checkout.spec.ts +341 -0
  108. package/examples/node/src/capabilities/identity.spec.ts +93 -0
  109. package/examples/node/src/capabilities/inventory.spec.ts +66 -0
  110. package/examples/node/src/capabilities/order-search.spec.ts +265 -0
  111. package/examples/node/src/capabilities/order.spec.ts +91 -0
  112. package/examples/node/src/capabilities/price.spec.ts +51 -0
  113. package/examples/node/src/capabilities/product-search.spec.ts +293 -0
  114. package/examples/node/src/capabilities/product.spec.ts +122 -0
  115. package/examples/node/src/capabilities/profile.spec.ts +316 -0
  116. package/examples/node/src/capabilities/store.spec.ts +26 -0
  117. package/examples/node/src/utils.ts +147 -0
  118. package/examples/node/tsconfig.json +9 -12
  119. package/examples/node/tsconfig.lib.json +1 -2
  120. package/examples/node/tsconfig.spec.json +2 -14
  121. package/examples/node/vitest.config.ts +14 -0
  122. package/migrations.json +22 -5
  123. package/nx.json +8 -47
  124. package/package.json +24 -96
  125. package/providers/algolia/README.md +39 -2
  126. package/providers/algolia/package.json +2 -1
  127. package/providers/algolia/src/core/initialize.ts +7 -14
  128. package/providers/algolia/src/index.ts +2 -4
  129. package/providers/algolia/src/providers/index.ts +1 -0
  130. package/providers/algolia/src/providers/product-search.provider.ts +241 -0
  131. package/providers/algolia/src/schema/capabilities.schema.ts +2 -3
  132. package/providers/algolia/src/schema/index.ts +3 -0
  133. package/providers/algolia/src/schema/search.schema.ts +8 -8
  134. package/providers/algolia/tsconfig.json +1 -1
  135. package/providers/algolia/tsconfig.lib.json +1 -1
  136. package/providers/algolia/tsconfig.spec.json +2 -14
  137. package/providers/algolia/vitest.config.ts +14 -0
  138. package/providers/commercetools/README.md +30 -3
  139. package/providers/commercetools/package.json +2 -1
  140. package/providers/commercetools/src/core/client.ts +178 -99
  141. package/providers/commercetools/src/core/initialize.ts +130 -74
  142. package/providers/commercetools/src/core/token-cache.ts +45 -0
  143. package/providers/commercetools/src/index.ts +3 -2
  144. package/providers/commercetools/src/providers/cart.provider.ts +281 -341
  145. package/providers/commercetools/src/providers/category.provider.ts +223 -138
  146. package/providers/commercetools/src/providers/checkout.provider.ts +631 -449
  147. package/providers/commercetools/src/providers/identity.provider.ts +50 -29
  148. package/providers/commercetools/src/providers/index.ts +2 -2
  149. package/providers/commercetools/src/providers/inventory.provider.ts +76 -74
  150. package/providers/commercetools/src/providers/order-search.provider.ts +220 -0
  151. package/providers/commercetools/src/providers/order.provider.ts +96 -61
  152. package/providers/commercetools/src/providers/price.provider.ts +147 -117
  153. package/providers/commercetools/src/providers/product-search.provider.ts +528 -0
  154. package/providers/commercetools/src/providers/product.provider.ts +249 -74
  155. package/providers/commercetools/src/providers/profile.provider.ts +445 -28
  156. package/providers/commercetools/src/providers/store.provider.ts +54 -40
  157. package/providers/commercetools/src/schema/capabilities.schema.ts +3 -1
  158. package/providers/commercetools/src/schema/commercetools.schema.ts +17 -3
  159. package/providers/commercetools/src/schema/configuration.schema.ts +1 -0
  160. package/providers/commercetools/src/schema/session.schema.ts +7 -0
  161. package/providers/commercetools/src/test/caching.spec.ts +82 -0
  162. package/providers/commercetools/src/test/identity.spec.ts +109 -0
  163. package/providers/commercetools/src/test/test-utils.ts +21 -19
  164. package/providers/commercetools/tsconfig.json +1 -1
  165. package/providers/commercetools/tsconfig.lib.json +1 -1
  166. package/providers/commercetools/tsconfig.spec.json +2 -14
  167. package/providers/commercetools/vitest.config.ts +15 -0
  168. package/providers/fake/README.md +20 -4
  169. package/providers/fake/package.json +2 -1
  170. package/providers/fake/src/core/initialize.ts +47 -49
  171. package/providers/fake/src/providers/analytics.provider.ts +5 -7
  172. package/providers/fake/src/providers/cart.provider.ts +163 -92
  173. package/providers/fake/src/providers/category.provider.ts +78 -50
  174. package/providers/fake/src/providers/checkout.provider.ts +254 -0
  175. package/providers/fake/src/providers/identity.provider.ts +57 -65
  176. package/providers/fake/src/providers/index.ts +6 -2
  177. package/providers/fake/src/providers/inventory.provider.ts +40 -36
  178. package/providers/fake/src/providers/order-search.provider.ts +78 -0
  179. package/providers/fake/src/providers/order.provider.ts +106 -0
  180. package/providers/fake/src/providers/price.provider.ts +93 -41
  181. package/providers/fake/src/providers/product-search.provider.ts +206 -0
  182. package/providers/fake/src/providers/product.provider.ts +56 -41
  183. package/providers/fake/src/providers/profile.provider.ts +147 -0
  184. package/providers/fake/src/providers/store.provider.ts +30 -20
  185. package/providers/fake/src/schema/capabilities.schema.ts +5 -1
  186. package/providers/fake/src/test/cart.provider.spec.ts +59 -80
  187. package/providers/fake/src/test/category.provider.spec.ts +145 -87
  188. package/providers/fake/src/test/checkout.provider.spec.ts +222 -0
  189. package/providers/fake/src/test/order-search.provider.spec.ts +50 -0
  190. package/providers/fake/src/test/order.provider.spec.ts +44 -0
  191. package/providers/fake/src/test/price.provider.spec.ts +50 -45
  192. package/providers/fake/src/test/product.provider.spec.ts +15 -7
  193. package/providers/fake/src/test/profile.provider.spec.ts +167 -0
  194. package/providers/fake/tsconfig.json +1 -1
  195. package/providers/fake/tsconfig.lib.json +1 -1
  196. package/providers/fake/tsconfig.spec.json +2 -12
  197. package/providers/fake/vitest.config.ts +14 -0
  198. package/providers/medusa/README.md +30 -0
  199. package/providers/medusa/TESTING.md +98 -0
  200. package/providers/medusa/eslint.config.mjs +19 -0
  201. package/providers/medusa/package.json +22 -0
  202. package/providers/medusa/project.json +34 -0
  203. package/providers/medusa/src/core/client.ts +370 -0
  204. package/providers/medusa/src/core/initialize.ts +78 -0
  205. package/providers/medusa/src/index.ts +13 -0
  206. package/providers/medusa/src/providers/cart.provider.ts +575 -0
  207. package/providers/medusa/src/providers/category.provider.ts +247 -0
  208. package/providers/medusa/src/providers/checkout.provider.ts +636 -0
  209. package/providers/medusa/src/providers/identity.provider.ts +137 -0
  210. package/providers/medusa/src/providers/inventory.provider.ts +173 -0
  211. package/providers/medusa/src/providers/order-search.provider.ts +202 -0
  212. package/providers/medusa/src/providers/order.provider.ts +226 -0
  213. package/providers/medusa/src/providers/price.provider.ts +140 -0
  214. package/providers/medusa/src/providers/product-search.provider.ts +243 -0
  215. package/providers/medusa/src/providers/product.provider.ts +261 -0
  216. package/providers/medusa/src/providers/profile.provider.ts +392 -0
  217. package/providers/medusa/src/schema/capabilities.schema.ts +18 -0
  218. package/providers/medusa/src/schema/configuration.schema.ts +11 -0
  219. package/providers/medusa/src/schema/medusa.schema.ts +31 -0
  220. package/providers/medusa/src/test/cart.provider.spec.ts +240 -0
  221. package/providers/medusa/src/test/category.provider.spec.ts +231 -0
  222. package/providers/medusa/src/test/checkout.spec.ts +349 -0
  223. package/providers/medusa/src/test/identity.provider.spec.ts +122 -0
  224. package/providers/medusa/src/test/inventory.provider.spec.ts +88 -0
  225. package/providers/medusa/src/test/large-cart.provider.spec.ts +103 -0
  226. package/providers/medusa/src/test/price.provider.spec.ts +104 -0
  227. package/providers/medusa/src/test/product.provider.spec.ts +146 -0
  228. package/providers/medusa/src/test/search.provider.spec.ts +203 -0
  229. package/providers/medusa/src/test/test-utils.ts +13 -0
  230. package/providers/medusa/src/utils/medusa-helpers.ts +89 -0
  231. package/providers/medusa/tsconfig.json +21 -0
  232. package/providers/medusa/tsconfig.lib.json +9 -0
  233. package/providers/medusa/tsconfig.spec.json +4 -0
  234. package/providers/medusa/vitest.config.ts +15 -0
  235. package/providers/meilisearch/README.md +48 -0
  236. package/providers/meilisearch/eslint.config.mjs +22 -0
  237. package/providers/meilisearch/package.json +13 -0
  238. package/providers/meilisearch/project.json +34 -0
  239. package/providers/meilisearch/src/core/initialize.ts +21 -0
  240. package/providers/meilisearch/src/index.ts +6 -0
  241. package/providers/meilisearch/src/providers/index.ts +1 -0
  242. package/providers/meilisearch/src/providers/order-search.provider.ts +222 -0
  243. package/providers/meilisearch/src/providers/product-search.provider.ts +251 -0
  244. package/providers/meilisearch/src/schema/capabilities.schema.ts +10 -0
  245. package/providers/meilisearch/src/schema/configuration.schema.ts +11 -0
  246. package/providers/meilisearch/src/schema/index.ts +3 -0
  247. package/providers/meilisearch/src/schema/search.schema.ts +14 -0
  248. package/providers/meilisearch/tsconfig.json +24 -0
  249. package/providers/meilisearch/tsconfig.lib.json +10 -0
  250. package/providers/meilisearch/tsconfig.spec.json +4 -0
  251. package/providers/meilisearch/vitest.config.ts +14 -0
  252. package/providers/posthog/package.json +2 -1
  253. package/providers/posthog/tsconfig.json +1 -1
  254. package/tsconfig.base.json +5 -0
  255. package/vitest.config.ts +10 -0
  256. package/core/src/providers/search.provider.ts +0 -18
  257. package/core/src/schemas/models/search.model.ts +0 -36
  258. package/core/src/schemas/queries/search.query.ts +0 -9
  259. package/examples/next/.swcrc +0 -30
  260. package/examples/next/eslint.config.mjs +0 -21
  261. package/examples/next/index.d.ts +0 -6
  262. package/examples/next/next-env.d.ts +0 -5
  263. package/examples/next/next.config.js +0 -31
  264. package/examples/next/project.json +0 -9
  265. package/examples/next/public/.gitkeep +0 -0
  266. package/examples/next/public/favicon.ico +0 -0
  267. package/examples/next/src/app/global.css +0 -0
  268. package/examples/next/src/app/layout.tsx +0 -18
  269. package/examples/next/src/app/page.module.scss +0 -2
  270. package/examples/next/src/app/page.tsx +0 -47
  271. package/examples/next/src/instrumentation.ts +0 -9
  272. package/examples/next/tsconfig.json +0 -44
  273. package/examples/node/jest.config.ts +0 -10
  274. package/jest.config.ts +0 -6
  275. package/jest.preset.js +0 -3
  276. package/providers/algolia/jest.config.ts +0 -10
  277. package/providers/algolia/src/providers/product.provider.ts +0 -66
  278. package/providers/algolia/src/providers/search.provider.ts +0 -106
  279. package/providers/algolia/src/test/search.provider.spec.ts +0 -91
  280. package/providers/commercetools/jest.config.cjs +0 -10
  281. package/providers/commercetools/src/providers/search.provider.ts +0 -96
  282. package/providers/commercetools/src/test/cart.provider.spec.ts +0 -199
  283. package/providers/commercetools/src/test/category.provider.spec.ts +0 -168
  284. package/providers/commercetools/src/test/checkout.provider.spec.ts +0 -312
  285. package/providers/commercetools/src/test/identity.provider.spec.ts +0 -88
  286. package/providers/commercetools/src/test/inventory.provider.spec.ts +0 -41
  287. package/providers/commercetools/src/test/price.provider.spec.ts +0 -81
  288. package/providers/commercetools/src/test/product.provider.spec.ts +0 -80
  289. package/providers/commercetools/src/test/profile.provider.spec.ts +0 -49
  290. package/providers/commercetools/src/test/search.provider.spec.ts +0 -61
  291. package/providers/commercetools/src/test/store.provider.spec.ts +0 -37
  292. package/providers/fake/jest.config.cjs +0 -10
  293. package/providers/fake/src/providers/search.provider.ts +0 -132
@@ -0,0 +1,636 @@
1
+ import type { StoreCart, StoreCartAddress, StoreCartLineItem } from '@medusajs/types';
2
+ import type {
3
+ Address,
4
+ AddressIdentifier,
5
+ Cache,
6
+ Checkout,
7
+ CheckoutIdentifier,
8
+ CheckoutItem,
9
+ CheckoutMutationAddPaymentInstruction,
10
+ CheckoutMutationFinalizeCheckout,
11
+ CheckoutMutationInitiateCheckout,
12
+ CheckoutMutationRemovePaymentInstruction,
13
+ CheckoutMutationSetShippingAddress,
14
+ CheckoutMutationSetShippingInstruction,
15
+ CheckoutQueryById,
16
+ CheckoutQueryForAvailablePaymentMethods,
17
+ CheckoutQueryForAvailableShippingMethods,
18
+ CostBreakDown,
19
+ Currency,
20
+ ItemCostBreakdown,
21
+ MonetaryAmount,
22
+ PaymentInstruction,
23
+ PaymentMethod,
24
+ PaymentMethodIdentifier,
25
+ RequestContext,
26
+ ShippingMethod,
27
+ Result,
28
+ NotFoundError
29
+ } from '@reactionary/core';
30
+ import {
31
+ AddressIdentifierSchema,
32
+ CheckoutMutationAddPaymentInstructionSchema,
33
+ CheckoutMutationFinalizeCheckoutSchema,
34
+ CheckoutMutationInitiateCheckoutSchema,
35
+ CheckoutMutationRemovePaymentInstructionSchema,
36
+ CheckoutMutationSetShippingAddressSchema,
37
+ CheckoutMutationSetShippingInstructionSchema,
38
+ CheckoutProvider,
39
+ CheckoutQueryByIdSchema,
40
+ CheckoutQueryForAvailablePaymentMethodsSchema,
41
+ CheckoutQueryForAvailableShippingMethodsSchema,
42
+ CheckoutSchema,
43
+ MonetaryAmountSchema,
44
+ PaymentMethodSchema,
45
+ Reactionary,
46
+ ShippingMethodIdentifierSchema,
47
+ ShippingMethodSchema,
48
+ success,
49
+ error
50
+ } from '@reactionary/core';
51
+ import createDebug from 'debug';
52
+ import z from 'zod';
53
+ import type { MedusaAPI } from '../core/client.js';
54
+ import type { MedusaConfiguration } from '../schema/configuration.schema.js';
55
+ import { handleProviderError, parseMedusaCostBreakdown, parseMedusaItemPrice } from '../utils/medusa-helpers.js';
56
+ import {
57
+ type MedusaCartIdentifier,
58
+ type MedusaOrderIdentifier
59
+ } from '../schema/medusa.schema.js';
60
+ const debug = createDebug('reactionary:medusa:checkout');
61
+
62
+ export class CheckoutNotReadyForFinalizationError extends Error {
63
+
64
+
65
+
66
+ constructor(public checkoutIdentifier: CheckoutIdentifier) {
67
+ super(
68
+ 'Checkout is not ready for finalization. Ensure all required fields are set and valid. ' +
69
+ (checkoutIdentifier
70
+ ? `Checkout ID: ${JSON.stringify(checkoutIdentifier)}`
71
+ : '')
72
+ );
73
+ this.name = 'CheckoutNotReadyForFinalizationError';
74
+ }
75
+ }
76
+
77
+ export class MedusaCheckoutProvider extends CheckoutProvider {
78
+ protected config: MedusaConfiguration;
79
+
80
+ /**
81
+ * This controls which fields are always included when fetching a cart
82
+ * You can override this in a subclass to add more fields as needed.
83
+ *
84
+ * example: this.includedFields = [includedFields, '+discounts.*'].join(',');
85
+ */
86
+ protected includedFields: string = ['+items.*'].join(',');
87
+
88
+
89
+
90
+ constructor(
91
+ config: MedusaConfiguration,
92
+ cache: Cache,
93
+ context: RequestContext,
94
+ public medusaApi: MedusaAPI
95
+ ) {
96
+ super(cache, context);
97
+ this.config = config;
98
+ }
99
+
100
+ @Reactionary({
101
+ inputSchema: CheckoutMutationInitiateCheckoutSchema,
102
+ outputSchema: CheckoutSchema,
103
+ })
104
+ public override async initiateCheckoutForCart(
105
+ payload: CheckoutMutationInitiateCheckout
106
+ ): Promise<Result<Checkout>> {
107
+ const client = await this.medusaApi.getClient();
108
+ // we should eventually copy the cart.... but for now we just continue with the existing one.
109
+ if (debug.enabled) {
110
+ debug(
111
+ `Initiating checkout for cart with key: ${payload.cart.identifier.key}`
112
+ );
113
+ }
114
+ // zero out existing checkout data?
115
+ const response = await client.store.cart.update(
116
+ payload.cart.identifier.key,
117
+ {
118
+ billing_address: payload.billingAddress
119
+ ? this.mapAddressToStoreAddress(payload.billingAddress)
120
+ : undefined,
121
+ shipping_address: payload.billingAddress
122
+ ? this.mapAddressToStoreAddress(payload.billingAddress)
123
+ : undefined,
124
+ email: payload.notificationEmail,
125
+ metadata: {
126
+ sms_notification: payload.notificationPhone,
127
+ },
128
+ },
129
+ {
130
+ fields: this.includedFields,
131
+ }
132
+ );
133
+
134
+ return success(this.parseSingle(response.cart));
135
+ }
136
+
137
+ @Reactionary({
138
+ inputSchema: CheckoutQueryByIdSchema,
139
+ outputSchema: CheckoutSchema.nullable(),
140
+ })
141
+ public override async getById(
142
+ payload: CheckoutQueryById
143
+ ): Promise<Result<Checkout, NotFoundError>> {
144
+ const client = await this.medusaApi.getClient();
145
+ const response = await client.store.cart.retrieve(payload.identifier.key, {
146
+ fields: this.includedFields,
147
+ });
148
+ return success(this.parseSingle(response.cart));
149
+ }
150
+
151
+ @Reactionary({
152
+ inputSchema: CheckoutMutationSetShippingAddressSchema,
153
+ outputSchema: CheckoutSchema,
154
+ })
155
+ public override async setShippingAddress(
156
+ payload: CheckoutMutationSetShippingAddress
157
+ ): Promise<Result<Checkout>> {
158
+ const client = await this.medusaApi.getClient();
159
+
160
+ const response = await client.store.cart.update(
161
+ payload.checkout.key,
162
+ {
163
+ shipping_address: this.mapAddressToStoreAddress(
164
+ payload.shippingAddress
165
+ ),
166
+ },
167
+ {
168
+ fields: this.includedFields,
169
+ }
170
+ );
171
+ return success(this.parseSingle(response.cart));
172
+ }
173
+
174
+ @Reactionary({
175
+ inputSchema: CheckoutQueryForAvailableShippingMethodsSchema,
176
+ outputSchema: z.array(ShippingMethodSchema),
177
+ })
178
+ public override async getAvailableShippingMethods(
179
+ payload: CheckoutQueryForAvailableShippingMethods
180
+ ): Promise<Result<ShippingMethod[]>> {
181
+ const client = await this.medusaApi.getClient();
182
+
183
+ if (debug.enabled) {
184
+ debug(
185
+ `Fetching available shipping methods for checkout with key: ${payload.checkout.key}`
186
+ );
187
+ }
188
+
189
+ const shippingMethodResponse =
190
+ await client.store.fulfillment.listCartOptions({
191
+ cart_id: payload.checkout.key,
192
+ });
193
+
194
+ const shippingMethods: ShippingMethod[] = [];
195
+
196
+ for (const sm of shippingMethodResponse.shipping_options) {
197
+ shippingMethods.push(
198
+ ShippingMethodSchema.parse({
199
+ identifier: ShippingMethodIdentifierSchema.parse({ key: sm.id }),
200
+ name: sm.name,
201
+ description: sm.type.description || '',
202
+ price: MonetaryAmountSchema.parse({
203
+ value: sm.calculated_price.calculated_amount || 0,
204
+ currency:
205
+ sm.calculated_price.currency_code?.toUpperCase() as Currency,
206
+ } satisfies MonetaryAmount),
207
+ deliveryTime: '',
208
+ } satisfies ShippingMethod)
209
+ );
210
+ }
211
+
212
+ if (debug.enabled) {
213
+ debug(
214
+ `Found ${shippingMethods.length} shipping methods for checkout with key: ${payload.checkout.key}`,
215
+ shippingMethods
216
+ );
217
+ }
218
+ return success(shippingMethods);
219
+ }
220
+
221
+ @Reactionary({
222
+ inputSchema: CheckoutQueryForAvailablePaymentMethodsSchema,
223
+ outputSchema: z.array(PaymentMethodSchema),
224
+ })
225
+ public override async getAvailablePaymentMethods(
226
+ payload: CheckoutQueryForAvailablePaymentMethods
227
+ ): Promise<Result<PaymentMethod[]>> {
228
+ const client = await this.medusaApi.getClient();
229
+
230
+ if (debug.enabled) {
231
+ debug(
232
+ `Fetching available payment methods for checkout with key: ${payload.checkout.key}`
233
+ );
234
+ }
235
+ const checkout = await client.store.cart.retrieve(payload.checkout.key);
236
+ const paymentMethodResponse =
237
+ await client.store.payment.listPaymentProviders({
238
+ region_id:
239
+ checkout.cart.region_id || (await this.medusaApi.getActiveRegion()).id,
240
+ });
241
+
242
+ const paymentMethods: PaymentMethod[] = [];
243
+
244
+ for (const pm of paymentMethodResponse.payment_providers) {
245
+ paymentMethods.push(
246
+ {
247
+ identifier: {
248
+ method: pm.id,
249
+ name: pm.id,
250
+ paymentProcessor: pm.id,
251
+ },
252
+ logo: undefined,
253
+ description: pm.id,
254
+ isPunchOut: true,
255
+ }
256
+
257
+ );
258
+ }
259
+
260
+ if (debug.enabled) {
261
+ debug(
262
+ `Found ${paymentMethods.length} payment methods for checkout with key: ${payload.checkout.key}`,
263
+ paymentMethods
264
+ );
265
+ }
266
+
267
+ return success(paymentMethods);
268
+ }
269
+
270
+ @Reactionary({
271
+ inputSchema: CheckoutMutationAddPaymentInstructionSchema,
272
+ outputSchema: CheckoutSchema,
273
+ })
274
+ public override async addPaymentInstruction(
275
+ payload: CheckoutMutationAddPaymentInstruction
276
+ ): Promise<Result<Checkout>> {
277
+ const client = await this.medusaApi.getClient();
278
+
279
+ if (debug.enabled) {
280
+ debug(
281
+ `Adding payment instruction ${payload.paymentInstruction.paymentMethod.name} to checkout with key: ${payload.checkout.key}`
282
+ );
283
+ }
284
+ try {
285
+ const cartResponse = await client.store.cart.retrieve(
286
+ payload.checkout.key
287
+ );
288
+
289
+ const paymentSessionResponse =
290
+ await client.store.payment.initiatePaymentSession(cartResponse.cart, {
291
+ provider_id: payload.paymentInstruction.paymentMethod.method,
292
+ data: payload.paymentInstruction.protocolData.reduce((acc, curr) => {
293
+ acc[curr.key] = curr.value;
294
+ return acc;
295
+ }, {} as Record<string, string>),
296
+ });
297
+
298
+ const updatedCartResponse = await client.store.cart.retrieve(
299
+ payload.checkout.key,
300
+ {
301
+ fields: this.includedFields,
302
+ }
303
+ );
304
+
305
+ return success(this.parseSingle(updatedCartResponse.cart));
306
+ } catch (error) {
307
+ debug('Failed to add payment instruction: {0}', [error]);
308
+ throw new Error(
309
+ `Failed to add payment instruction: ${
310
+ error instanceof Error ? error.message : 'Unknown error'
311
+ }`
312
+ );
313
+ }
314
+ }
315
+
316
+ @Reactionary({
317
+ inputSchema: CheckoutMutationRemovePaymentInstructionSchema,
318
+ outputSchema: CheckoutSchema,
319
+ })
320
+ public override removePaymentInstruction(
321
+ payload: CheckoutMutationRemovePaymentInstruction
322
+ ): Promise<Result<Checkout>> {
323
+ throw new Error('Method not implemented.');
324
+ }
325
+
326
+ @Reactionary({
327
+ inputSchema: CheckoutMutationSetShippingInstructionSchema,
328
+ outputSchema: CheckoutSchema,
329
+ })
330
+ public override async setShippingInstruction(
331
+ payload: CheckoutMutationSetShippingInstruction
332
+ ): Promise<Result<Checkout>> {
333
+ const client = await this.medusaApi.getClient();
334
+ const medusaId = payload.checkout as MedusaCartIdentifier;
335
+ try {
336
+ // Set shipping method
337
+ if (payload.shippingInstruction.shippingMethod) {
338
+ await client.store.cart.addShippingMethod(medusaId.key, {
339
+ option_id: payload.shippingInstruction.shippingMethod.key,
340
+ data: {
341
+ consent_for_unattended_delivery:
342
+ payload.shippingInstruction.consentForUnattendedDelivery + '',
343
+ instructions: payload.shippingInstruction.instructions || '',
344
+ },
345
+ });
346
+ }
347
+
348
+ // for now, we store a backup of the shipping instruction in metadata
349
+ await client.store.cart.update(medusaId.key, {
350
+ metadata: {
351
+ consent_for_unattended_delivery:
352
+ payload.shippingInstruction.consentForUnattendedDelivery + '',
353
+ instructions: payload.shippingInstruction.instructions || '',
354
+ pickup_point: payload.shippingInstruction.pickupPoint || '',
355
+ },
356
+ });
357
+
358
+ // Get updated cart
359
+ const response = await client.store.cart.retrieve(medusaId.key, {
360
+ fields: this.includedFields,
361
+ });
362
+
363
+ if (response.cart) {
364
+ return success(this.parseSingle(response.cart));
365
+ }
366
+
367
+ throw new Error('Failed to set shipping method');
368
+ } catch (error) {
369
+ handleProviderError('set shipping method', error);
370
+ }
371
+ }
372
+ @Reactionary({
373
+ inputSchema: CheckoutMutationFinalizeCheckoutSchema,
374
+ outputSchema: CheckoutSchema,
375
+ })
376
+ public override async finalizeCheckout(
377
+ payload: CheckoutMutationFinalizeCheckout
378
+ ): Promise<Result<Checkout>> {
379
+ const checkout = await this.getById({ identifier: payload.checkout });
380
+ if (!checkout.success || !checkout.value.readyForFinalization) {
381
+ throw new CheckoutNotReadyForFinalizationError(payload.checkout);
382
+ }
383
+
384
+ const client = await this.medusaApi.getClient();
385
+ const medusaId = payload.checkout as CheckoutIdentifier;
386
+
387
+ // Complete the cart to create an order
388
+ const response = await client.store.cart.complete(medusaId.key);
389
+
390
+ if (response.type === 'order') {
391
+ // lets persist this on the carts metadata for future reference
392
+ await client.store.cart.update(medusaId.key, {
393
+ metadata: {
394
+ order_id: response.order.id,
395
+ order_display_id: response.order?.display_id ? +'' : '',
396
+ },
397
+ });
398
+ return this.getById({
399
+ identifier: payload.checkout,
400
+ }) as Promise<Result<Checkout>>;
401
+ }
402
+
403
+ throw new Error('Something failed during order creation');
404
+ }
405
+
406
+ /**
407
+ * Extension point to map an Address to a Store Address
408
+ * @param address
409
+ * @returns
410
+ */
411
+ protected mapAddressToStoreAddress(address: Partial<Address>) {
412
+ return {
413
+ first_name: address.firstName,
414
+ last_name: address.lastName,
415
+ address_1: address.streetAddress,
416
+ address_2: address.streetNumber || '',
417
+ city: address.city,
418
+ postal_code: address.postalCode,
419
+ country_code: address.countryCode?.toLowerCase(),
420
+ };
421
+ }
422
+
423
+ /**
424
+ * Extension point to control the parsing of an address from a store cart address
425
+ * @param storeAddress
426
+ * @returns
427
+ */
428
+ protected composeAddressFromStoreAddress(storeAddress: StoreCartAddress): Address {
429
+ return {
430
+ identifier: AddressIdentifierSchema.parse({
431
+ nickName: storeAddress.id,
432
+ } satisfies AddressIdentifier),
433
+ firstName: storeAddress.first_name || '',
434
+ lastName: storeAddress.last_name || '',
435
+ streetAddress: storeAddress.address_1 || '',
436
+ streetNumber: storeAddress.address_2 || '',
437
+ city: storeAddress.city || '',
438
+ postalCode: storeAddress.postal_code || '',
439
+ countryCode: storeAddress.country_code || '',
440
+ region: '',
441
+ };
442
+ }
443
+
444
+ /**
445
+ * Extension point to control the parsing of a single cart item price
446
+ * @param remoteItem
447
+ * @param currency
448
+ * @returns
449
+ */
450
+ protected parseItemPrice(
451
+ remoteItem: StoreCartLineItem,
452
+ currency: Currency
453
+ ): ItemCostBreakdown {
454
+ return parseMedusaItemPrice(remoteItem, currency);
455
+ }
456
+
457
+ /**
458
+ * Extension point to control the parsing of the cost breakdown of a cart
459
+ * @param remote
460
+ * @returns
461
+ */
462
+ protected parseCostBreakdown(remote: StoreCart): CostBreakDown {
463
+ return parseMedusaCostBreakdown(remote);
464
+ }
465
+
466
+
467
+ /**
468
+ * Extension point to control the parsing of a single checkout item
469
+ * @param remoteItem
470
+ * @param currency
471
+ * @returns
472
+ */
473
+ protected parseCheckoutItem(
474
+ remoteItem: StoreCartLineItem,
475
+ currency: Currency
476
+ ): CheckoutItem {
477
+ const unitPrice = remoteItem.unit_price || 0;
478
+ const totalPrice = unitPrice * remoteItem.quantity || 0;
479
+ const discountTotal = remoteItem.discount_total || 0;
480
+
481
+ const item: CheckoutItem = {
482
+ identifier: {
483
+ key: remoteItem.id,
484
+ },
485
+ variant: {
486
+ sku: remoteItem.variant_sku || '',
487
+ },
488
+ quantity: remoteItem.quantity || 1,
489
+
490
+ price: this.parseItemPrice(remoteItem,currency),
491
+ };
492
+ return item;
493
+ }
494
+
495
+ /**
496
+ * Extension point to control the parsing of the Checkout object
497
+ * @param remote
498
+ * @returns
499
+ */
500
+ protected parseSingle(remote: StoreCart): Checkout {
501
+ const identifier = {
502
+ key: remote.id,
503
+ // region_id: remote.region_id,
504
+ };
505
+
506
+ const name = '' + (remote.metadata?.['name'] || '');
507
+ const description = '' + (remote.metadata?.['description'] || '');
508
+
509
+ const price = this.parseCostBreakdown(remote);
510
+
511
+ // Parse checkout items
512
+ const items = new Array<CheckoutItem>();
513
+ for (const remoteItem of remote.items || []) {
514
+ items.push(this.parseCheckoutItem(remoteItem, price.grandTotal.currency));
515
+ }
516
+
517
+ const billingAddress = remote.billing_address
518
+ ? this.composeAddressFromStoreAddress(remote.billing_address)
519
+ : undefined;
520
+ const shippingAddress = remote.shipping_address
521
+ ? this.composeAddressFromStoreAddress(remote.shipping_address)
522
+ : undefined;
523
+
524
+ const backupUnattendedDelivery =
525
+ remote.metadata?.['consent_for_unattended_delivery'] !== undefined
526
+ ? remote.metadata?.['consent_for_unattended_delivery'] === 'true'
527
+ : undefined;
528
+ const backupInstructions =
529
+ remote.metadata?.['instructions'] !== undefined
530
+ ? remote.metadata?.['instructions'] + ''
531
+ : undefined;
532
+ const backupPickupPoint =
533
+ remote.metadata?.['pickup_point'] !== undefined
534
+ ? remote.metadata?.['pickup_point'] + ''
535
+ : undefined;
536
+
537
+ let shippingInstruction;
538
+ remote.shipping_methods?.forEach((sm) => {
539
+ let pickupPoint = '';
540
+ let instructions = '';
541
+ let consentForUnattendedDelivery = false;
542
+ if (sm.data) {
543
+ pickupPoint = sm.data['pickup_point'] + '' || '';
544
+ instructions = sm.data['instructions'] + '' || '';
545
+ consentForUnattendedDelivery =
546
+ sm.data['consent_for_unattended_delivery'] === 'true';
547
+ }
548
+
549
+ if (!pickupPoint) {
550
+ pickupPoint = backupPickupPoint || '';
551
+ }
552
+ if (!instructions) {
553
+ instructions = backupInstructions || '';
554
+ }
555
+ if (!consentForUnattendedDelivery) {
556
+ consentForUnattendedDelivery = backupUnattendedDelivery || false;
557
+ }
558
+
559
+ // currently Medusa only supports one shipping method per cart
560
+ shippingInstruction = {
561
+ shippingMethod: { key: sm.shipping_option_id },
562
+ consentForUnattendedDelivery,
563
+ instructions,
564
+ pickupPoint,
565
+ };
566
+ });
567
+
568
+ const paymentInstructions = new Array<PaymentInstruction>();
569
+ for (const remotePayment of remote.payment_collection?.payment_sessions ||
570
+ []) {
571
+ if (
572
+ remotePayment.status === 'canceled' ||
573
+ remotePayment.status === 'error'
574
+ ) {
575
+ console.warn(
576
+ `Skipping payment session ${remotePayment.id} with status ${remotePayment.status}`
577
+ );
578
+ continue;
579
+ }
580
+ const paymentMethodIdentifier: PaymentMethodIdentifier = {
581
+ method: remotePayment.provider_id,
582
+ name: remotePayment.provider_id,
583
+ paymentProcessor: remotePayment.provider_id,
584
+ };
585
+
586
+ paymentInstructions.push({
587
+ identifier: {
588
+ key: remotePayment.id,
589
+ },
590
+ amount: {
591
+ value: remotePayment.amount,
592
+ currency: remotePayment.currency_code?.toUpperCase() as Currency,
593
+ },
594
+ paymentMethod: paymentMethodIdentifier,
595
+ protocolData: remotePayment.data
596
+ ? Object.entries(remotePayment.data).map(([key, value]) => ({
597
+ key,
598
+ value: String(value),
599
+ }))
600
+ : [],
601
+ status: 'pending',
602
+ }
603
+ );
604
+ }
605
+
606
+ const originalCartReference: MedusaCartIdentifier = {
607
+ key: remote.id,
608
+ region: remote.region_id,
609
+ };
610
+
611
+ let resultingOrder: MedusaOrderIdentifier | undefined = undefined;
612
+ if (remote.metadata?.['order_id']) {
613
+ resultingOrder = {
614
+ key: remote.metadata?.['order_id'] + '' || '',
615
+ display_id: Number(remote.metadata?.['order_display_id'] + '' || '0'),
616
+ };
617
+ }
618
+
619
+ const result: Checkout = {
620
+ identifier,
621
+ name,
622
+ description,
623
+ price,
624
+ items,
625
+ originalCartReference,
626
+ paymentInstructions,
627
+ readyForFinalization: false,
628
+ billingAddress,
629
+ resultingOrder,
630
+ shippingAddress,
631
+ shippingInstruction
632
+ };
633
+
634
+ return result;
635
+ }
636
+ }