@reactionary/source 0.0.48 → 0.0.52

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 (175) hide show
  1. package/core/package.json +4 -3
  2. package/core/src/cache/cache.interface.ts +1 -1
  3. package/core/src/cache/memory-cache.ts +2 -2
  4. package/core/src/cache/noop-cache.ts +1 -1
  5. package/core/src/cache/redis-cache.ts +1 -1
  6. package/core/src/client/client-builder.ts +4 -4
  7. package/core/src/client/client.ts +12 -12
  8. package/core/src/decorators/reactionary.decorator.ts +22 -2
  9. package/core/src/index.ts +14 -14
  10. package/core/src/initialization.ts +1 -1
  11. package/core/src/providers/analytics.provider.ts +2 -2
  12. package/core/src/providers/base.provider.ts +5 -5
  13. package/core/src/providers/cart.provider.ts +6 -6
  14. package/core/src/providers/category.provider.ts +4 -9
  15. package/core/src/providers/checkout.provider.ts +156 -0
  16. package/core/src/providers/identity.provider.ts +5 -5
  17. package/core/src/providers/index.ts +13 -12
  18. package/core/src/providers/inventory.provider.ts +4 -4
  19. package/core/src/providers/order.provider.ts +31 -0
  20. package/core/src/providers/price.provider.ts +5 -5
  21. package/core/src/providers/product.provider.ts +5 -5
  22. package/core/src/providers/profile.provider.ts +5 -5
  23. package/core/src/providers/search.provider.ts +4 -4
  24. package/core/src/providers/store.provider.ts +4 -4
  25. package/core/src/schemas/capabilities.schema.ts +2 -1
  26. package/core/src/schemas/models/analytics.model.ts +1 -1
  27. package/core/src/schemas/models/cart.model.ts +3 -28
  28. package/core/src/schemas/models/category.model.ts +2 -2
  29. package/core/src/schemas/models/checkout.model.ts +66 -0
  30. package/core/src/schemas/models/cost.model.ts +21 -0
  31. package/core/src/schemas/models/identifiers.model.ts +23 -2
  32. package/core/src/schemas/models/identity.model.ts +8 -5
  33. package/core/src/schemas/models/index.ts +19 -15
  34. package/core/src/schemas/models/inventory.model.ts +2 -2
  35. package/core/src/schemas/models/order.model.ts +46 -0
  36. package/core/src/schemas/models/payment.model.ts +5 -12
  37. package/core/src/schemas/models/price.model.ts +3 -3
  38. package/core/src/schemas/models/product.model.ts +6 -3
  39. package/core/src/schemas/models/profile.model.ts +2 -2
  40. package/core/src/schemas/models/search.model.ts +2 -3
  41. package/core/src/schemas/models/shipping-method.model.ts +34 -3
  42. package/core/src/schemas/models/store.model.ts +2 -2
  43. package/core/src/schemas/mutations/analytics.mutation.ts +2 -2
  44. package/core/src/schemas/mutations/cart.mutation.ts +5 -5
  45. package/core/src/schemas/mutations/checkout.mutation.ts +50 -0
  46. package/core/src/schemas/mutations/identity.mutation.ts +1 -1
  47. package/core/src/schemas/mutations/index.ts +10 -10
  48. package/core/src/schemas/mutations/profile.mutation.ts +1 -1
  49. package/core/src/schemas/queries/cart.query.ts +2 -2
  50. package/core/src/schemas/queries/category.query.ts +3 -3
  51. package/core/src/schemas/queries/checkout.query.ts +22 -0
  52. package/core/src/schemas/queries/identity.query.ts +1 -1
  53. package/core/src/schemas/queries/index.ts +13 -12
  54. package/core/src/schemas/queries/inventory.query.ts +2 -2
  55. package/core/src/schemas/queries/order.query.ts +9 -0
  56. package/core/src/schemas/queries/price.query.ts +2 -2
  57. package/core/src/schemas/queries/product.query.ts +9 -2
  58. package/core/src/schemas/queries/profile.query.ts +1 -1
  59. package/core/src/schemas/queries/search.query.ts +2 -2
  60. package/core/src/schemas/queries/store.query.ts +1 -1
  61. package/core/src/schemas/session.schema.ts +3 -3
  62. package/core/tsconfig.json +3 -2
  63. package/examples/next/next.config.js +17 -6
  64. package/examples/next/src/app/page.tsx +1 -2
  65. package/examples/node/package.json +2 -1
  66. package/examples/node/src/basic/basic-node-setup.spec.ts +1 -1
  67. package/examples/node/tsconfig.json +2 -1
  68. package/examples/node/tsconfig.spec.json +3 -2
  69. package/package.json +3 -1
  70. package/providers/algolia/package.json +2 -1
  71. package/providers/algolia/src/core/initialize.ts +5 -5
  72. package/providers/algolia/src/index.ts +5 -5
  73. package/providers/algolia/src/providers/product.provider.ts +8 -2
  74. package/providers/algolia/src/providers/search.provider.ts +1 -1
  75. package/providers/algolia/src/test/search.provider.spec.ts +1 -1
  76. package/providers/algolia/tsconfig.json +2 -1
  77. package/providers/algolia/tsconfig.spec.json +3 -2
  78. package/providers/commercetools/{jest.config.ts → jest.config.cjs} +1 -1
  79. package/providers/commercetools/package.json +3 -2
  80. package/providers/commercetools/src/core/client.ts +63 -32
  81. package/providers/commercetools/src/core/initialize.ts +20 -16
  82. package/providers/commercetools/src/index.ts +10 -10
  83. package/providers/commercetools/src/providers/cart.provider.ts +14 -19
  84. package/providers/commercetools/src/providers/category.provider.ts +3 -12
  85. package/providers/commercetools/src/providers/checkout.provider.ts +644 -0
  86. package/providers/commercetools/src/providers/identity.provider.ts +8 -8
  87. package/providers/commercetools/src/providers/index.ts +12 -9
  88. package/providers/commercetools/src/providers/inventory.provider.ts +2 -4
  89. package/providers/commercetools/src/providers/order.provider.ts +163 -0
  90. package/providers/commercetools/src/providers/price.provider.ts +3 -3
  91. package/providers/commercetools/src/providers/product.provider.ts +24 -6
  92. package/providers/commercetools/src/providers/profile.provider.ts +2 -2
  93. package/providers/commercetools/src/providers/search.provider.ts +3 -5
  94. package/providers/commercetools/src/providers/store.provider.ts +3 -3
  95. package/providers/commercetools/src/schema/capabilities.schema.ts +2 -1
  96. package/providers/commercetools/src/schema/commercetools.schema.ts +7 -5
  97. package/providers/commercetools/src/schema/configuration.schema.ts +2 -0
  98. package/providers/commercetools/src/test/cart.provider.spec.ts +24 -4
  99. package/providers/commercetools/src/test/category.provider.spec.ts +3 -3
  100. package/providers/commercetools/src/test/checkout.provider.spec.ts +312 -0
  101. package/providers/commercetools/src/test/identity.provider.spec.ts +3 -3
  102. package/providers/commercetools/src/test/inventory.provider.spec.ts +2 -2
  103. package/providers/commercetools/src/test/price.provider.spec.ts +4 -4
  104. package/providers/commercetools/src/test/product.provider.spec.ts +22 -5
  105. package/providers/commercetools/src/test/profile.provider.spec.ts +3 -3
  106. package/providers/commercetools/src/test/search.provider.spec.ts +2 -2
  107. package/providers/commercetools/src/test/store.provider.spec.ts +2 -2
  108. package/providers/commercetools/src/test/test-utils.ts +14 -0
  109. package/providers/commercetools/tsconfig.json +2 -1
  110. package/providers/commercetools/tsconfig.spec.json +4 -3
  111. package/providers/fake/{jest.config.ts → jest.config.cjs} +1 -1
  112. package/providers/fake/package.json +2 -2
  113. package/providers/fake/src/core/initialize.ts +6 -6
  114. package/providers/fake/src/index.ts +4 -4
  115. package/providers/fake/src/providers/analytics.provider.ts +1 -1
  116. package/providers/fake/src/providers/cart.provider.ts +2 -2
  117. package/providers/fake/src/providers/category.provider.ts +7 -3
  118. package/providers/fake/src/providers/identity.provider.ts +1 -1
  119. package/providers/fake/src/providers/index.ts +9 -9
  120. package/providers/fake/src/providers/inventory.provider.ts +1 -1
  121. package/providers/fake/src/providers/price.provider.ts +1 -1
  122. package/providers/fake/src/providers/product.provider.ts +10 -4
  123. package/providers/fake/src/providers/search.provider.ts +2 -5
  124. package/providers/fake/src/providers/store.provider.ts +2 -3
  125. package/providers/fake/src/test/cart.provider.spec.ts +3 -3
  126. package/providers/fake/src/test/category.provider.spec.ts +2 -2
  127. package/providers/fake/src/test/price.provider.spec.ts +2 -2
  128. package/providers/fake/src/test/product.provider.spec.ts +8 -8
  129. package/providers/fake/src/test/test-utils.ts +1 -1
  130. package/providers/fake/tsconfig.json +2 -1
  131. package/providers/fake/tsconfig.spec.json +1 -3
  132. package/providers/posthog/package.json +4 -4
  133. package/providers/posthog/project.json +2 -2
  134. package/providers/posthog/src/core/initialize.ts +2 -2
  135. package/providers/posthog/src/index.ts +3 -3
  136. package/providers/posthog/tsconfig.json +2 -1
  137. package/tsconfig.base.json +3 -4
  138. package/.claude/settings.local.json +0 -28
  139. package/core/src/providers/cart-payment.provider.ts +0 -57
  140. package/core/src/schemas/mutations/cart-payment.mutation.ts +0 -21
  141. package/core/src/schemas/queries/cart-payment.query.ts +0 -12
  142. package/otel/README.md +0 -227
  143. package/otel/eslint.config.mjs +0 -23
  144. package/otel/package.json +0 -11
  145. package/otel/pnpm-lock.yaml +0 -805
  146. package/otel/project.json +0 -33
  147. package/otel/src/index.ts +0 -22
  148. package/otel/src/metrics.ts +0 -76
  149. package/otel/src/provider-instrumentation.ts +0 -108
  150. package/otel/src/test/otel.spec.ts +0 -8
  151. package/otel/src/trace-decorator.ts +0 -226
  152. package/otel/src/tracer.ts +0 -83
  153. package/otel/src/trpc-middleware.ts +0 -128
  154. package/otel/tsconfig.json +0 -23
  155. package/otel/tsconfig.lib.json +0 -23
  156. package/otel/tsconfig.spec.json +0 -28
  157. package/otel/vite.config.ts +0 -24
  158. package/providers/commercetools/src/providers/cart-payment.provider.ts +0 -193
  159. package/providers/commercetools/src/test/cart-payment.provider.spec.ts +0 -145
  160. package/trpc/README.md +0 -7
  161. package/trpc/__mocks__/superjson.js +0 -25
  162. package/trpc/eslint.config.mjs +0 -19
  163. package/trpc/jest.config.ts +0 -14
  164. package/trpc/package.json +0 -14
  165. package/trpc/project.json +0 -31
  166. package/trpc/src/client.ts +0 -175
  167. package/trpc/src/index.ts +0 -44
  168. package/trpc/src/integration.spec.ts +0 -223
  169. package/trpc/src/server.ts +0 -125
  170. package/trpc/src/test-utils.ts +0 -31
  171. package/trpc/src/transparent-client.spec.ts +0 -162
  172. package/trpc/src/types.ts +0 -144
  173. package/trpc/tsconfig.json +0 -16
  174. package/trpc/tsconfig.lib.json +0 -10
  175. package/trpc/tsconfig.spec.json +0 -15
@@ -0,0 +1,644 @@
1
+
2
+ import type {
3
+ Cache,
4
+ Checkout,
5
+ RequestContext,
6
+ PaymentMethod,
7
+ ShippingMethod,
8
+ CheckoutMutationInitiateCheckout,
9
+ CheckoutMutationSetShippingAddress,
10
+ CheckoutMutationFinalizeCheckout,
11
+ CheckoutMutationAddPaymentInstruction,
12
+ CheckoutMutationRemovePaymentInstruction,
13
+ CheckoutMutationSetShippingInstruction,
14
+ CheckoutQueryById,
15
+ CheckoutQueryForAvailablePaymentMethods,
16
+ CheckoutQueryForAvailableShippingMethods,
17
+ CheckoutIdentifier,
18
+ Currency,
19
+ ShippingInstruction,
20
+ PaymentInstruction
21
+ } from "@reactionary/core";
22
+ import { AddressSchema, CheckoutItemSchema, CheckoutProvider, PaymentInstructionIdentifierSchema, PaymentInstructionSchema, PaymentMethodIdentifierSchema, ShippingInstructionSchema, ShippingMethodSchema } from "@reactionary/core";
23
+ import type z from "zod";
24
+ import { CommercetoolsClient } from "../core/client.js";
25
+ import type { CommercetoolsConfiguration } from "../schema/configuration.schema.js";
26
+ import type { MyCartUpdateAction } from "@commercetools/platform-sdk";
27
+ import { CommercetoolsCartIdentifierSchema, CommercetoolsCheckoutIdentifierSchema, CommercetoolsOrderIdentifierSchema, type CommercetoolsCheckoutIdentifier } from "../schema/commercetools.schema.js";
28
+ import type { Address as CTAddress, Payment as CTPayment, Cart as CTCart, ShippingMethod as CTShippingMethod } from "@commercetools/platform-sdk";
29
+
30
+
31
+ export class CheckoutNotReadyForFinalizationError extends Error {
32
+ constructor(public checkoutIdentifier: CheckoutIdentifier) {
33
+ super("Checkout is not ready for finalization. Ensure all required fields are set and valid. " + (checkoutIdentifier ? `Checkout ID: ${JSON.stringify(checkoutIdentifier)}` : ''));
34
+ this.name = "CheckoutNotReadyForFinalizationError";
35
+ }
36
+ }
37
+
38
+
39
+ export class CommercetoolsCheckoutProvider<
40
+ T extends Checkout = Checkout
41
+ > extends CheckoutProvider<T> {
42
+ protected config: CommercetoolsConfiguration;
43
+
44
+ constructor(config: CommercetoolsConfiguration, schema: z.ZodType<T>, cache: Cache) {
45
+ super(schema, cache);
46
+
47
+ this.config = config;
48
+ }
49
+
50
+ protected async getClient(reqCtx: RequestContext) {
51
+ const client = await new CommercetoolsClient(this.config).getClient(reqCtx);
52
+
53
+ return {
54
+ payments: client.withProjectKey({ projectKey: this.config.projectKey }).me().payments(),
55
+ carts: client.withProjectKey({ projectKey: this.config.projectKey }).me().carts(),
56
+ shippingMethods: client.withProjectKey({ projectKey: this.config.projectKey }).shippingMethods(),
57
+ orders: client.withProjectKey({ projectKey: this.config.projectKey }).me().orders()
58
+ };
59
+ }
60
+
61
+ public async initiateCheckoutForCart(payload: CheckoutMutationInitiateCheckout, reqCtx: RequestContext): Promise<T> {
62
+ // so......we could copy the cart......
63
+
64
+ const client = await this.getClient(reqCtx);
65
+
66
+ const cart = await client.carts.withId({ ID: (payload.cart as any).key }).get().execute();
67
+ const replicationResponse = await client.carts.replicate().post({
68
+ body: {
69
+ reference: {
70
+ typeId: 'cart',
71
+ id: cart.body.id
72
+ }
73
+ }
74
+ }).execute();
75
+ // set the custom type to mark it as a checkout
76
+
77
+ const actions: MyCartUpdateAction[] = [
78
+ {
79
+ action: 'setCustomType',
80
+ type: {
81
+ typeId: 'type',
82
+ key: 'reactionaryCheckout'
83
+ },
84
+ fields: {
85
+ commerceToolsCartId: payload.cart.key,
86
+ }
87
+ },
88
+ ];
89
+
90
+ if (payload.billingAddress) {
91
+ actions.push({
92
+ action: 'setBillingAddress',
93
+ address: {
94
+ country: payload.billingAddress.countryCode,
95
+ firstName: payload.billingAddress.firstName || '',
96
+ lastName: payload.billingAddress.lastName || '',
97
+ streetName: payload.billingAddress.streetAddress || '',
98
+ streetNumber: payload.billingAddress.streetNumber || '',
99
+ postalCode: payload.billingAddress.postalCode || '',
100
+ city: payload.billingAddress.city || '',
101
+ email: payload.notificationEmail || '',
102
+ phone: payload.notificationPhone || ''
103
+ }
104
+ });
105
+ actions.push({
106
+ action: 'setShippingAddress',
107
+ address: {
108
+ country: payload.billingAddress.countryCode,
109
+ firstName: payload.billingAddress.firstName || '',
110
+ lastName: payload.billingAddress.lastName || '',
111
+ streetName: payload.billingAddress.streetAddress || '',
112
+ streetNumber: payload.billingAddress.streetNumber || '',
113
+ postalCode: payload.billingAddress.postalCode || '',
114
+ city: payload.billingAddress.city || '',
115
+ }
116
+ });
117
+ }
118
+
119
+ const checkoutResponse = await client.carts.withId({ ID: replicationResponse.body.id }).post({
120
+ body: {
121
+ version: replicationResponse.body.version || 0,
122
+ actions: [
123
+ ...actions
124
+ ]
125
+ }
126
+ }).execute();
127
+
128
+ return this.parseSingle(checkoutResponse.body, reqCtx);
129
+
130
+ }
131
+
132
+ public async getById(payload: CheckoutQueryById, reqCtx: RequestContext): Promise<T | null> {
133
+
134
+ const client = await this.getClient(reqCtx);
135
+ const checkoutResponse = await client.carts.withId({ ID: payload.identifier.key }).get({
136
+ queryArgs: {
137
+ expand: ['paymentInfo.payments[*]', 'shippingInfo.shippingMethod']
138
+ }
139
+ }).execute();
140
+
141
+ return this.parseSingle(checkoutResponse.body, reqCtx);
142
+ }
143
+
144
+ public async setShippingAddress(payload: CheckoutMutationSetShippingAddress, reqCtx: RequestContext): Promise<T> {
145
+ const client = await this.getClient(reqCtx);
146
+
147
+ const version = (payload.checkout as CommercetoolsCheckoutIdentifier).version;
148
+ const checkoutResponse = await client.carts.withId({ ID: payload.checkout.key }).post({
149
+ body: {
150
+ version: version,
151
+ actions: [
152
+ {
153
+ action: 'setShippingAddress',
154
+ address: {
155
+ country: payload.shippingAddress.countryCode,
156
+ firstName: payload.shippingAddress.firstName || '',
157
+ lastName: payload.shippingAddress.lastName || '',
158
+ streetName: payload.shippingAddress.streetAddress || '',
159
+ streetNumber: payload.shippingAddress.streetNumber || '',
160
+ postalCode: payload.shippingAddress.postalCode || '',
161
+ city: payload.shippingAddress.city || '',
162
+ }
163
+ }
164
+ ]
165
+ }
166
+ }).execute();
167
+
168
+ return this.parseSingle(checkoutResponse.body, reqCtx);
169
+ }
170
+
171
+ public async getAvailableShippingMethods(payload: CheckoutQueryForAvailableShippingMethods, reqCtx: RequestContext): Promise<ShippingMethod[]> {
172
+ const client = await this.getClient(reqCtx);
173
+ const shippingMethodsResponse = await client.shippingMethods.matchingCart().get({
174
+ queryArgs: {
175
+ cartId: payload.checkout.key
176
+ }
177
+ }).execute();
178
+
179
+ const result: Array<ShippingMethod> = [];
180
+ const inputShippingMethods: CTShippingMethod[] = shippingMethodsResponse.body.results as CTShippingMethod[];
181
+ for (const sm of inputShippingMethods) {
182
+ const shippingMethod = ShippingMethodSchema.parse({
183
+ identifier: {
184
+ key: sm.key,
185
+ },
186
+ name: sm.name,
187
+ description: sm.localizedDescription?.[ reqCtx.languageContext.locale ] || '',
188
+ price: sm.zoneRates[0].shippingRates[0].price ? {
189
+ value: (sm.zoneRates[0].shippingRates[0].price.centAmount || 0) / 100,
190
+ currency: sm.zoneRates[0].shippingRates[0].price.currencyCode || reqCtx.languageContext.currencyCode
191
+ } : { value: 0, currency: reqCtx.languageContext.currencyCode },
192
+ });
193
+ result.push(shippingMethod);
194
+ }
195
+ return result;
196
+ }
197
+
198
+ public async getAvailablePaymentMethods(payload: CheckoutQueryForAvailablePaymentMethods, reqCtx: RequestContext): Promise<PaymentMethod[]> {
199
+ // Commercetools does not have a concept of payment methods, as these are handled by the payment providers.
200
+ // So for now, we will return an empty array.
201
+ const staticMethods = this.getStaticPaymentMethods(payload.checkout, reqCtx);
202
+
203
+ const dynamicMethods: PaymentMethod[] = [];
204
+ // later we will also fetch any stored payment methods the user has...
205
+
206
+ return [...staticMethods, ...dynamicMethods];
207
+ }
208
+
209
+ public async addPaymentInstruction(payload: CheckoutMutationAddPaymentInstruction, reqCtx: RequestContext): Promise<T> {
210
+ const client = await this.getClient(reqCtx);
211
+
212
+ const response = await client.payments.post({
213
+ body: {
214
+
215
+ amountPlanned: {
216
+ centAmount: Math.round(payload.paymentInstruction.amount.value * 100),
217
+ currencyCode: payload.paymentInstruction.amount.currency
218
+ },
219
+ paymentMethodInfo: {
220
+ method: payload.paymentInstruction.paymentMethod.method,
221
+ name: {
222
+ [reqCtx.languageContext.locale]: payload.paymentInstruction.paymentMethod.name
223
+ },
224
+ paymentInterface: payload.paymentInstruction.paymentMethod.paymentProcessor
225
+ },
226
+ custom: {
227
+ type: {
228
+ typeId: 'type',
229
+ key: 'reactionaryPaymentCustomFields',
230
+ },
231
+ fields: {
232
+ 'commerceToolsCartId': payload.checkout.key,
233
+ }
234
+ }
235
+ },
236
+ }).execute();
237
+
238
+ const version = (payload.checkout as CommercetoolsCheckoutIdentifier).version;
239
+ const actions: MyCartUpdateAction[] = [
240
+ {
241
+ action: 'addPayment',
242
+ payment: {
243
+ typeId: 'payment',
244
+ id: response.body.id
245
+ }
246
+ }
247
+ ];
248
+
249
+ return this.applyActions(payload.checkout as CommercetoolsCheckoutIdentifier, actions, reqCtx);
250
+ }
251
+
252
+ public async removePaymentInstruction(payload: CheckoutMutationRemovePaymentInstruction, reqCtx: RequestContext): Promise<T> {
253
+ const client = await this.getClient(reqCtx);
254
+
255
+
256
+ // FIXME: Need to get full-endpoint rights, if we want to cancel the authorization on the payment. The MyPayment endpoint does not support
257
+ // changing a payment intent after it has transactions.
258
+
259
+
260
+
261
+ // get newest version
262
+ /*
263
+ const newestVersion = await client.payments.withId({ ID: payload.paymentInstruction.key }).get().execute();
264
+
265
+ const cancelledVersion = await client.payments.withId({ ID: payload.paymentInstruction.key }).post({
266
+ body: {
267
+ version: newestVersion.body.version || 0,
268
+ actions: [
269
+ {
270
+ action: "addTransaction",
271
+ transaction: {
272
+ type: "CancelAuthorization",
273
+ amount: newestVersion.body.amountPlanned,
274
+ }
275
+ }
276
+ ]
277
+ }
278
+ }).execute();
279
+ */
280
+ // we set the planned amount to 0, which effectively cancels the payment, and also allows the backend to clean it up.
281
+ // Note: This does NOT remove the payment from the cart, as that would be a breaking change to the cart, and we want to avoid that during checkout.
282
+ // Instead, the payment will remain on the cart, but with status 'canceled', and can be removed later if needed.
283
+ // This also allows us to keep a record of the payment instruction for auditing purposes.
284
+ // The cart can be re-used, and a new payment instruction can be added to it later.
285
+ // The frontend should ignore any payment instructions with status 'canceled' when displaying payment options to the user.
286
+
287
+ // Now add the payment to the cart
288
+ /*
289
+ const ctId = payload.checkout as CommercetoolsCheckoutIdentifier
290
+ const updatedCart = await client.carts.withId({ ID: ctId.key }).post({
291
+ body: {
292
+ version: ctId.version,
293
+ actions: [
294
+ {
295
+ 'action': 'removePayment',
296
+ 'payment': {
297
+ 'typeId': 'payment',
298
+ 'id': payload.paymentInstruction.key
299
+ }
300
+ }
301
+ ]
302
+ }
303
+ }).execute();
304
+
305
+ const response = await client.payments.withId({ ID: payload.paymentInstruction.key }).delete({
306
+ queryArgs: {
307
+ version: newestVersion.body.version || 0
308
+ }
309
+ }).execute();
310
+ */
311
+
312
+ const checkout = await this.getById({ identifier: payload.checkout }, reqCtx);
313
+ return checkout!
314
+
315
+ }
316
+
317
+ public async setShippingInstruction(payload: CheckoutMutationSetShippingInstruction, reqCtx: RequestContext): Promise<T> {
318
+
319
+ const client = await this.getClient(reqCtx);
320
+ const ctId = payload.checkout as CommercetoolsCheckoutIdentifier;
321
+
322
+ const actions: MyCartUpdateAction[] = [];
323
+ actions.push({
324
+ action: 'setShippingMethod',
325
+ shippingMethod: {
326
+ typeId: 'shipping-method',
327
+ key: payload.shippingInstruction.shippingMethod.key
328
+ }
329
+ });
330
+ actions.push({
331
+ action: 'setCustomField',
332
+ name: 'shippingInstruction',
333
+ value: payload.shippingInstruction.instructions
334
+ });
335
+ actions.push({
336
+ action: 'setCustomField',
337
+ name: 'consentForUnattendedDelivery',
338
+ value: payload.shippingInstruction.consentForUnattendedDelivery + ''
339
+ });
340
+ actions.push({
341
+ action: 'setCustomField',
342
+ name: 'pickupPointId',
343
+ value: payload.shippingInstruction.pickupPoint
344
+ });
345
+
346
+
347
+
348
+ return this.applyActions(payload.checkout as CommercetoolsCheckoutIdentifier, actions, reqCtx);
349
+ }
350
+
351
+ public async finalizeCheckout(payload: CheckoutMutationFinalizeCheckout, reqCtx: RequestContext): Promise<T> {
352
+ const checkout = await this.getById({ identifier: payload.checkout }, reqCtx);
353
+ if (!checkout || !checkout.readyForFinalization) {
354
+ throw new CheckoutNotReadyForFinalizationError(payload.checkout);
355
+ }
356
+
357
+ const client = await this.getClient(reqCtx);
358
+ const ctId = payload.checkout as CommercetoolsCheckoutIdentifier;
359
+
360
+ // create the order from the cart
361
+ const orderResponse = await client.orders.post({
362
+ body: {
363
+ id: ctId.key,
364
+ version: ctId.version
365
+ }
366
+ }).execute();
367
+
368
+ const actions: MyCartUpdateAction[] = [];
369
+ actions.push({
370
+ action: 'setCustomField',
371
+ name: 'commerceToolsOrderId',
372
+ value: orderResponse.body.id
373
+ });
374
+
375
+ return this.applyActions(payload.checkout as CommercetoolsCheckoutIdentifier, actions, reqCtx);
376
+ }
377
+
378
+
379
+
380
+
381
+ protected async applyActions(
382
+ checkout: CheckoutIdentifier,
383
+ actions: MyCartUpdateAction[],
384
+ reqCtx: RequestContext
385
+ ): Promise<T> {
386
+ const client = await this.getClient(reqCtx);
387
+ const ctId = checkout as CommercetoolsCheckoutIdentifier;
388
+
389
+
390
+ try {
391
+ const response = await client.carts
392
+ .withId({ ID: ctId.key })
393
+ .post({
394
+ queryArgs: {
395
+ expand: ['paymentInfo.payments[*]', 'shippingInfo.shippingMethod']
396
+ },
397
+ body: {
398
+ version: ctId.version,
399
+ actions,
400
+ },
401
+ })
402
+ .execute();
403
+
404
+ if (response.error) {
405
+ console.error(response.error);
406
+ }
407
+ return this.parseSingle(response.body, reqCtx);
408
+ } catch (e: any) {
409
+ console.error('Error applying actions to cart:', e);
410
+ throw e;
411
+ }
412
+
413
+ }
414
+
415
+ /**
416
+ * Extension point, to allow filtering the options, or adding new ones, like invoicing for b2b.
417
+ *
418
+ * Usecase: Override this, if you need to change the payment options based on the request context.
419
+ * @param reqCtx
420
+ * @returns
421
+ */
422
+ protected getStaticPaymentMethods(_checkout: CheckoutIdentifier, reqCtx: RequestContext): PaymentMethod[] {
423
+ return this.config.paymentMethods || [];
424
+ }
425
+
426
+
427
+
428
+ protected override getResourceName(): string {
429
+ return "checkout";
430
+ }
431
+
432
+
433
+ protected override parseSingle(remote: CTCart, reqCtx: RequestContext): T {
434
+ const result = this.newModel();
435
+
436
+ result.identifier = CommercetoolsCheckoutIdentifierSchema.parse({
437
+ key: remote.id,
438
+ version: remote.version || 0,
439
+ });
440
+
441
+ result.name = remote.custom?.fields['name'] || '';
442
+ result.description = remote.custom?.fields['description'] || '';
443
+
444
+
445
+ result.originalCartReference = CommercetoolsCartIdentifierSchema.parse({
446
+ key: remote.custom?.fields['commerceToolsCartId'] || '',
447
+ version: 0,
448
+ });
449
+
450
+ const orderId = remote.custom?.fields['commerceToolsOrderId'];
451
+ if (orderId) {
452
+ result.resultingOrder = CommercetoolsOrderIdentifierSchema.parse({
453
+ key: orderId,
454
+ version: 0,
455
+ });
456
+ }
457
+
458
+
459
+ if (remote.shippingAddress) {
460
+ result.shippingAddress = this.parseAddress( remote.shippingAddress, reqCtx) ;
461
+ }
462
+
463
+ if (remote.billingAddress ) {
464
+ result.billingAddress = this.parseAddress( remote.billingAddress, reqCtx) ;
465
+ }
466
+ result.shippingInstruction = this.parseShippingInstruction(remote);
467
+
468
+ for(const p of remote.paymentInfo?.payments || []) {
469
+ if (p.obj) {
470
+ result.paymentInstructions.push( this.parsePaymentInstruction(p.obj, reqCtx) );
471
+ }
472
+ }
473
+
474
+ const grandTotal = remote.totalPrice.centAmount || 0;
475
+ const shippingTotal = remote.shippingInfo?.price.centAmount || 0;
476
+ const productTotal = grandTotal - shippingTotal;
477
+ const taxTotal = remote.taxedPrice?.totalTax?.centAmount || 0;
478
+ const discountTotal =
479
+ remote.discountOnTotalPrice?.discountedAmount.centAmount || 0;
480
+ const surchargeTotal = 0;
481
+ const currency = remote.totalPrice.currencyCode as Currency;
482
+
483
+ result.price = {
484
+ totalTax: {
485
+ value: taxTotal / 100,
486
+ currency,
487
+ },
488
+ totalDiscount: {
489
+ value: discountTotal / 100,
490
+ currency,
491
+ },
492
+ totalSurcharge: {
493
+ value: surchargeTotal / 100,
494
+ currency,
495
+ },
496
+ totalShipping: {
497
+ value: shippingTotal / 100,
498
+ currency: remote.shippingInfo?.price.currencyCode as Currency,
499
+ },
500
+ totalProductPrice: {
501
+ value: productTotal / 100,
502
+ currency,
503
+ },
504
+ grandTotal: {
505
+ value: grandTotal / 100,
506
+ currency,
507
+ },
508
+ };
509
+
510
+ for (const remoteItem of remote.lineItems) {
511
+ const item = CheckoutItemSchema.parse({});
512
+
513
+ item.identifier.key = remoteItem.id;
514
+ item.sku.key = remoteItem.variant.sku || '';
515
+ item.quantity = remoteItem.quantity;
516
+
517
+ const unitPrice = remoteItem.price.value.centAmount;
518
+ const totalPrice = remoteItem.totalPrice.centAmount || 0;
519
+ const totalDiscount = remoteItem.price.discounted?.value.centAmount || 0;
520
+ const unitDiscount = totalDiscount / remoteItem.quantity;
521
+
522
+ item.price = {
523
+ unitPrice: {
524
+ value: unitPrice / 100,
525
+ currency,
526
+ },
527
+ unitDiscount: {
528
+ value: unitDiscount / 100,
529
+ currency,
530
+ },
531
+ totalPrice: {
532
+ value: (totalPrice || 0) / 100,
533
+ currency,
534
+ },
535
+ totalDiscount: {
536
+ value: totalDiscount / 100,
537
+ currency,
538
+ },
539
+ };
540
+
541
+ result.items.push(item);
542
+ }
543
+
544
+ result.readyForFinalization = this.isReadyForFinalization(result);
545
+ result.meta = {
546
+ cache: {
547
+ hit: false,
548
+ key: this.generateCacheKeySingle(result.identifier, reqCtx),
549
+ },
550
+ placeholder: false,
551
+ };
552
+
553
+ return this.assert(result);
554
+ }
555
+
556
+ protected isReadyForFinalization(checkout: T): boolean {
557
+ // we should have a billing address
558
+ if (!checkout.billingAddress) return false;
559
+
560
+ // we should know how to ship it
561
+ if (!checkout.shippingInstruction) return false;
562
+
563
+ // and it should ship either to an address or a pickup point
564
+ if (!checkout.shippingAddress && !checkout.shippingInstruction.pickupPoint) return false;
565
+
566
+ // and it should be paid for
567
+ if (checkout.paymentInstructions.length === 0) return false;
568
+
569
+ const authorizedPayments = checkout.paymentInstructions.filter(pi => pi.status === 'authorized').map(x => x.amount.value).reduce((a, b) => a + b, 0);
570
+ if (checkout.price.grandTotal.value !== authorizedPayments) return false;
571
+
572
+ return true
573
+ }
574
+
575
+ protected parsePaymentInstruction(remote: CTPayment, reqCtx: RequestContext): PaymentInstruction {
576
+ const newModel = PaymentInstructionSchema.parse({});
577
+ newModel.identifier = PaymentInstructionIdentifierSchema.parse({ key: remote.id || '' });
578
+ newModel.amount = {
579
+ value: remote.amountPlanned.centAmount / 100,
580
+ currency: remote.amountPlanned.currencyCode as Currency,
581
+ };
582
+
583
+
584
+
585
+ const method = remote.paymentMethodInfo?.method || 'unknown';
586
+ const paymentProcessor = remote.paymentMethodInfo?.paymentInterface || method;
587
+ const paymentName = remote.paymentMethodInfo.name![reqCtx.languageContext.locale];
588
+ newModel.paymentMethod = PaymentMethodIdentifierSchema.parse({
589
+ method,
590
+ paymentProcessor,
591
+ name: paymentName || method || 'Unknown',
592
+ });
593
+
594
+ const customData = remote.custom?.fields || {};
595
+ newModel.protocolData = Object.keys(customData).map(x => ({ key: x, value: customData[x] })) || [];
596
+ if (remote.transactions && remote.transactions.length > 0) {
597
+ const lastTransaction = remote.transactions[remote.transactions.length - 1];
598
+ if (lastTransaction.type === 'Authorization' && lastTransaction.state === 'Pending') {
599
+ newModel.status = 'pending';
600
+ } else if (lastTransaction.type === 'Authorization' && lastTransaction.state === 'Success') {
601
+ newModel.status = 'authorized';
602
+ }
603
+ } else {
604
+ newModel.status = 'pending';
605
+ }
606
+
607
+ return PaymentInstructionSchema.parse(newModel);
608
+ }
609
+
610
+ protected parseAddress(remote: CTAddress, reqCtx: RequestContext) {
611
+ return AddressSchema.parse({
612
+ countryCode: remote.country || '',
613
+ firstName: remote.firstName || '',
614
+ lastName: remote.lastName || '',
615
+ streetAddress: remote.streetName || '',
616
+ streetNumber: remote.streetNumber || '',
617
+ postalCode: remote.postalCode || '',
618
+ city: remote.city || '',
619
+ });
620
+ }
621
+
622
+ protected parseShippingInstruction(remote: CTCart): ShippingInstruction | undefined {
623
+ if (!remote.shippingInfo) return undefined;
624
+
625
+ const instructions = remote.custom?.fields['shippingInstruction'] || '';
626
+ const consentForUnattendedDelivery = remote.custom?.fields['consentForUnattendedDelivery'] === 'true' || false;
627
+ const pickupPoint = remote.custom?.fields['pickupPointId'] || '';
628
+
629
+ const shippingInstruction = ShippingInstructionSchema.parse({
630
+ amount: {
631
+ value: (remote.shippingInfo.price.centAmount || 0) / 100,
632
+ currency: remote.shippingInfo.price.currencyCode as Currency,
633
+ },
634
+ shippingMethod: {
635
+ key: remote.shippingInfo.shippingMethod?.obj?.key || '',
636
+ },
637
+ pickupPoint: pickupPoint || '',
638
+ instructions: instructions || '',
639
+ consentForUnattendedDelivery: consentForUnattendedDelivery || false,
640
+ });
641
+
642
+ return shippingInstruction;
643
+ }
644
+ }
@@ -7,9 +7,9 @@ import {
7
7
  IdentityProvider,
8
8
  type IdentityMutationRegister,
9
9
  } from '@reactionary/core';
10
- import type { CommercetoolsConfiguration } from '../schema/configuration.schema';
10
+ import type { CommercetoolsConfiguration } from '../schema/configuration.schema.js';
11
11
  import type z from 'zod';
12
- import { CommercetoolsClient } from '../core/client';
12
+ import { CommercetoolsClient } from '../core/client.js';
13
13
 
14
14
  export class CommercetoolsIdentityProvider<
15
15
  T extends Identity = Identity
@@ -37,26 +37,26 @@ export class CommercetoolsIdentityProvider<
37
37
  payload: IdentityMutationLogin,
38
38
  reqCtx: RequestContext
39
39
  ): Promise<T> {
40
- await new CommercetoolsClient(this.config).login(payload.username, payload.password, reqCtx);
40
+ const identity = await new CommercetoolsClient(this.config).login(payload.username, payload.password, reqCtx);
41
41
 
42
- return this.getSelf({}, reqCtx);
42
+ return identity as T;
43
43
  }
44
44
 
45
45
  public override async logout(
46
46
  payload: Record<string, never>,
47
47
  reqCtx: RequestContext
48
48
  ): Promise<T> {
49
- await new CommercetoolsClient(this.config).logout(reqCtx);
49
+ const identity = await new CommercetoolsClient(this.config).logout(reqCtx);
50
50
 
51
- return this.getSelf({}, reqCtx);
51
+ return identity as T;
52
52
  }
53
53
 
54
54
  public override async register(
55
55
  payload: IdentityMutationRegister,
56
56
  reqCtx: RequestContext
57
57
  ): Promise<T> {
58
- await new CommercetoolsClient(this.config).register(payload.username, payload.password, reqCtx);
58
+ const identity = await new CommercetoolsClient(this.config).register(payload.username, payload.password, reqCtx);
59
59
 
60
- return this.getSelf({}, reqCtx);
60
+ return identity as T;
61
61
  }
62
62
  }