@unchainedshop/core-orders 1.1.3

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 (137) hide show
  1. package/.npm/package/README +7 -0
  2. package/.npm/package/npm-shrinkwrap.json +190 -0
  3. package/.versions +54 -0
  4. package/README.md +5 -0
  5. package/lib/db/OrderDeliveriesCollection.d.ts +3 -0
  6. package/lib/db/OrderDeliveriesCollection.js +8 -0
  7. package/lib/db/OrderDeliveriesCollection.js.map +1 -0
  8. package/lib/db/OrderDeliveriesSchema.d.ts +1 -0
  9. package/lib/db/OrderDeliveriesSchema.js +20 -0
  10. package/lib/db/OrderDeliveriesSchema.js.map +1 -0
  11. package/lib/db/OrderDiscountTrigger.d.ts +4 -0
  12. package/lib/db/OrderDiscountTrigger.js +6 -0
  13. package/lib/db/OrderDiscountTrigger.js.map +1 -0
  14. package/lib/db/OrderDiscountsCollection.d.ts +3 -0
  15. package/lib/db/OrderDiscountsCollection.js +11 -0
  16. package/lib/db/OrderDiscountsCollection.js.map +1 -0
  17. package/lib/db/OrderDiscountsSchema.d.ts +1 -0
  18. package/lib/db/OrderDiscountsSchema.js +12 -0
  19. package/lib/db/OrderDiscountsSchema.js.map +1 -0
  20. package/lib/db/OrderPaymentsCollection.d.ts +3 -0
  21. package/lib/db/OrderPaymentsCollection.js +8 -0
  22. package/lib/db/OrderPaymentsCollection.js.map +1 -0
  23. package/lib/db/OrderPaymentsSchema.d.ts +1 -0
  24. package/lib/db/OrderPaymentsSchema.js +20 -0
  25. package/lib/db/OrderPaymentsSchema.js.map +1 -0
  26. package/lib/db/OrderPositionsCollection.d.ts +3 -0
  27. package/lib/db/OrderPositionsCollection.js +11 -0
  28. package/lib/db/OrderPositionsCollection.js.map +1 -0
  29. package/lib/db/OrderPositionsSchema.d.ts +1 -0
  30. package/lib/db/OrderPositionsSchema.js +37 -0
  31. package/lib/db/OrderPositionsSchema.js.map +1 -0
  32. package/lib/db/OrdersCollection.d.ts +3 -0
  33. package/lib/db/OrdersCollection.js +33 -0
  34. package/lib/db/OrdersCollection.js.map +1 -0
  35. package/lib/db/OrdersSchema.d.ts +1 -0
  36. package/lib/db/OrdersSchema.js +30 -0
  37. package/lib/db/OrdersSchema.js.map +1 -0
  38. package/lib/director/OrderDiscountAdapter.d.ts +2 -0
  39. package/lib/director/OrderDiscountAdapter.js +3 -0
  40. package/lib/director/OrderDiscountAdapter.js.map +1 -0
  41. package/lib/director/OrderDiscountDirector.d.ts +2 -0
  42. package/lib/director/OrderDiscountDirector.js +3 -0
  43. package/lib/director/OrderDiscountDirector.js.map +1 -0
  44. package/lib/director/OrderPricingAdapter.d.ts +3 -0
  45. package/lib/director/OrderPricingAdapter.js +25 -0
  46. package/lib/director/OrderPricingAdapter.js.map +1 -0
  47. package/lib/director/OrderPricingDirector.d.ts +2 -0
  48. package/lib/director/OrderPricingDirector.js +48 -0
  49. package/lib/director/OrderPricingDirector.js.map +1 -0
  50. package/lib/director/OrderPricingSheet.d.ts +3 -0
  51. package/lib/director/OrderPricingSheet.js +113 -0
  52. package/lib/director/OrderPricingSheet.js.map +1 -0
  53. package/lib/module/configureOrderDeliveriesModule.d.ts +7 -0
  54. package/lib/module/configureOrderDeliveriesModule.js +164 -0
  55. package/lib/module/configureOrderDeliveriesModule.js.map +1 -0
  56. package/lib/module/configureOrderDiscountsModule.d.ts +7 -0
  57. package/lib/module/configureOrderDiscountsModule.js +183 -0
  58. package/lib/module/configureOrderDiscountsModule.js.map +1 -0
  59. package/lib/module/configureOrderPaymentsModule.d.ts +7 -0
  60. package/lib/module/configureOrderPaymentsModule.js +229 -0
  61. package/lib/module/configureOrderPaymentsModule.js.map +1 -0
  62. package/lib/module/configureOrderPositionsModule.d.ts +7 -0
  63. package/lib/module/configureOrderPositionsModule.js +213 -0
  64. package/lib/module/configureOrderPositionsModule.js.map +1 -0
  65. package/lib/module/configureOrdersModule-mutations.d.ts +12 -0
  66. package/lib/module/configureOrdersModule-mutations.js +162 -0
  67. package/lib/module/configureOrdersModule-mutations.js.map +1 -0
  68. package/lib/module/configureOrdersModule-processing.d.ts +13 -0
  69. package/lib/module/configureOrdersModule-processing.js +343 -0
  70. package/lib/module/configureOrdersModule-processing.js.map +1 -0
  71. package/lib/module/configureOrdersModule-queries.d.ts +5 -0
  72. package/lib/module/configureOrdersModule-queries.js +55 -0
  73. package/lib/module/configureOrdersModule-queries.js.map +1 -0
  74. package/lib/module/configureOrdersModule-transformations.d.ts +5 -0
  75. package/lib/module/configureOrdersModule-transformations.js +108 -0
  76. package/lib/module/configureOrdersModule-transformations.js.map +1 -0
  77. package/lib/module/configureOrdersModule.d.ts +3 -0
  78. package/lib/module/configureOrdersModule.js +239 -0
  79. package/lib/module/configureOrdersModule.js.map +1 -0
  80. package/lib/orders-index.d.ts +11 -0
  81. package/lib/orders-index.js +12 -0
  82. package/lib/orders-index.js.map +1 -0
  83. package/lib/orders-settings.d.ts +20 -0
  84. package/lib/orders-settings.js +17 -0
  85. package/lib/orders-settings.js.map +1 -0
  86. package/lib/service/createUserCartService.d.ts +2 -0
  87. package/lib/service/createUserCartService.js +23 -0
  88. package/lib/service/createUserCartService.js.map +1 -0
  89. package/lib/service/migrateOrderCartService.d.ts +2 -0
  90. package/lib/service/migrateOrderCartService.js +10 -0
  91. package/lib/service/migrateOrderCartService.js.map +1 -0
  92. package/lib/service/orderServices.d.ts +2 -0
  93. package/lib/service/orderServices.js +7 -0
  94. package/lib/service/orderServices.js.map +1 -0
  95. package/package.js +33 -0
  96. package/package.json +43 -0
  97. package/plugins/discount-100-off.ts +44 -0
  98. package/plugins/discount-half-price-manual.ts +43 -0
  99. package/plugins/discount-half-price.ts +46 -0
  100. package/plugins/order-delivery.ts +45 -0
  101. package/plugins/order-discount.ts +158 -0
  102. package/plugins/order-items-discount.ts +116 -0
  103. package/plugins/order-items.ts +61 -0
  104. package/plugins/order-payment.ts +46 -0
  105. package/src/db/OrderDeliveriesCollection.ts +12 -0
  106. package/src/db/OrderDeliveriesSchema.js +24 -0
  107. package/src/db/OrderDiscountTrigger.ts +4 -0
  108. package/src/db/OrderDiscountsCollection.ts +15 -0
  109. package/src/db/OrderDiscountsSchema.js +15 -0
  110. package/src/db/OrderPaymentsCollection.ts +12 -0
  111. package/src/db/OrderPaymentsSchema.js +24 -0
  112. package/src/db/OrderPositionsCollection.ts +15 -0
  113. package/src/db/OrderPositionsSchema.js +41 -0
  114. package/src/db/OrdersCollection.ts +37 -0
  115. package/src/db/OrdersSchema.js +34 -0
  116. package/src/director/OrderDiscountAdapter.ts +5 -0
  117. package/src/director/OrderDiscountDirector.ts +4 -0
  118. package/src/director/OrderPricingAdapter.ts +41 -0
  119. package/src/director/OrderPricingDirector.ts +68 -0
  120. package/src/director/OrderPricingSheet.ts +138 -0
  121. package/src/module/configureOrderDeliveriesModule.ts +240 -0
  122. package/src/module/configureOrderDiscountsModule.ts +245 -0
  123. package/src/module/configureOrderPaymentsModule.ts +331 -0
  124. package/src/module/configureOrderPositionsModule.ts +329 -0
  125. package/src/module/configureOrdersModule-mutations.ts +230 -0
  126. package/src/module/configureOrdersModule-processing.ts +506 -0
  127. package/src/module/configureOrdersModule-queries.ts +71 -0
  128. package/src/module/configureOrdersModule-transformations.ts +145 -0
  129. package/src/module/configureOrdersModule.ts +359 -0
  130. package/src/orders-index.ts +15 -0
  131. package/src/orders-settings.ts +23 -0
  132. package/src/service/createUserCartService.ts +38 -0
  133. package/src/service/migrateOrderCartService.ts +22 -0
  134. package/src/service/orderServices.ts +8 -0
  135. package/tests/orders-index.test.ts +50 -0
  136. package/tsconfig.build.json +26 -0
  137. package/tsconfig.json +8 -0
@@ -0,0 +1,245 @@
1
+ import { Context } from '@unchainedshop/types/api';
2
+ import { Collection, Filter, ModuleMutations } from '@unchainedshop/types/common';
3
+ import { OrdersModule } from '@unchainedshop/types/orders';
4
+ import { OrderDiscount, OrderDiscountsModule } from '@unchainedshop/types/orders.discounts';
5
+ import { emit, registerEvents } from '@unchainedshop/events';
6
+ import { log } from '@unchainedshop/logger';
7
+ import { generateDbFilterById, generateDbMutations } from '@unchainedshop/utils';
8
+ import { OrderDiscountsSchema } from '../db/OrderDiscountsSchema';
9
+ import { OrderDiscountTrigger } from '../db/OrderDiscountTrigger';
10
+ import { OrderDiscountDirector } from '../director/OrderDiscountDirector';
11
+
12
+ const ORDER_DISCOUNT_EVENTS: string[] = [
13
+ 'ORDER_CREATE_DISCOUNT',
14
+ 'ORDER_UPDATE_DISCOUNT',
15
+ 'ORDER_REMOVE_DISCOUNT',
16
+ 'ORDER_ADD_DISCOUNT',
17
+ ];
18
+
19
+ const OrderDiscountErrorCode = {
20
+ CODE_ALREADY_PRESENT: 'CODE_ALREADY_PRESENT',
21
+ CODE_NOT_VALID: 'CODE_NOT_VALID',
22
+ };
23
+
24
+ const buildFindByIdSelector = (orderDiscountId: string) =>
25
+ generateDbFilterById(orderDiscountId) as Filter<OrderDiscount>;
26
+
27
+ export const configureOrderDiscountsModule = ({
28
+ OrderDiscounts,
29
+ updateCalculation,
30
+ }: {
31
+ OrderDiscounts: Collection<OrderDiscount>;
32
+ updateCalculation: OrdersModule['updateCalculation'];
33
+ }): OrderDiscountsModule => {
34
+ registerEvents(ORDER_DISCOUNT_EVENTS);
35
+
36
+ const mutations = generateDbMutations<OrderDiscount>(OrderDiscounts, OrderDiscountsSchema, {
37
+ permanentlyDeleteByDefault: true,
38
+ hasCreateOnly: false,
39
+ }) as ModuleMutations<OrderDiscount>;
40
+
41
+ const getAdapter = async (orderDiscount: OrderDiscount, requestContext: Context) => {
42
+ const order = await requestContext.modules.orders.findOrder({
43
+ orderId: orderDiscount.orderId,
44
+ });
45
+ const Adapter = OrderDiscountDirector.getAdapter(orderDiscount.discountKey);
46
+ if (!Adapter) return null;
47
+ const adapter = Adapter.actions({
48
+ context: { order, orderDiscount, ...requestContext },
49
+ });
50
+ return adapter;
51
+ };
52
+
53
+ const createDiscount: OrderDiscountsModule['create'] = async (doc, userId) => {
54
+ const normalizedTrigger = doc.trigger || OrderDiscountTrigger.USER;
55
+
56
+ log(`Create Order Discount: ${doc.discountKey} with trigger ${normalizedTrigger}`, {
57
+ orderId: doc.orderId,
58
+ });
59
+
60
+ const discountId = await mutations.create({ ...doc, trigger: normalizedTrigger }, userId);
61
+ const discount = await OrderDiscounts.findOne(buildFindByIdSelector(discountId));
62
+ return discount;
63
+ };
64
+
65
+ const deleteDiscount: OrderDiscountsModule['delete'] = async (orderDiscountId, requestContext) => {
66
+ const selector = buildFindByIdSelector(orderDiscountId);
67
+ const discount = await OrderDiscounts.findOne(selector, {});
68
+
69
+ log(`OrderDiscounts -> Remove Discount ${orderDiscountId}`, {
70
+ orderId: discount.orderId,
71
+ });
72
+
73
+ if (discount.trigger === OrderDiscountTrigger.USER) {
74
+ // Release
75
+ const adapter = await getAdapter(discount, requestContext);
76
+ if (!adapter) return null;
77
+ await adapter.release();
78
+
79
+ await OrderDiscounts.deleteOne(selector);
80
+ await updateCalculation(discount.orderId, requestContext);
81
+ } else {
82
+ await OrderDiscounts.deleteOne(selector);
83
+ emit('ORDER_REMOVE_DISCOUNT', { discount });
84
+ }
85
+ return discount;
86
+ };
87
+
88
+ const updateDiscount: OrderDiscountsModule['update'] = async (orderDiscountId, doc, userId) => {
89
+ await mutations.update(orderDiscountId, doc, userId);
90
+
91
+ const selector = buildFindByIdSelector(orderDiscountId);
92
+ const discount = await OrderDiscounts.findOne(selector, {});
93
+ emit('ORDER_UPDATE_DISCOUNT', { discount });
94
+ return discount;
95
+ };
96
+
97
+ const reserveDiscount = async (orderDiscount: OrderDiscount, requestContext: Context) => {
98
+ const adapter = await getAdapter(orderDiscount, requestContext);
99
+ if (!adapter) return null;
100
+
101
+ const reservation = await adapter.reserve({
102
+ code: orderDiscount.code,
103
+ });
104
+
105
+ return updateDiscount(
106
+ orderDiscount._id,
107
+ { orderId: orderDiscount.orderId, reservation },
108
+ requestContext.userId,
109
+ );
110
+ };
111
+
112
+ const grabDiscount = async ({ code, orderId }: OrderDiscount, requestContext: Context) => {
113
+ log(`OrderDiscounts -> Try to grab ${code}`, { orderId });
114
+
115
+ const existingDiscount = await OrderDiscounts.findOne({ code, orderId });
116
+
117
+ if (existingDiscount) throw new Error(OrderDiscountErrorCode.CODE_ALREADY_PRESENT);
118
+
119
+ const discount = await OrderDiscounts.findOne({ code, orderId: null });
120
+ if (!discount) return null;
121
+
122
+ const discountId = discount._id;
123
+ try {
124
+ const updatedDiscount = await updateDiscount(discountId, { orderId }, requestContext.userId);
125
+ return reserveDiscount(updatedDiscount, requestContext);
126
+ } catch (error) {
127
+ // Rollback
128
+ await updateDiscount(discountId, { orderId: discount.orderId }, requestContext.userId);
129
+
130
+ throw error;
131
+ }
132
+ };
133
+
134
+ return {
135
+ // Queries
136
+ findOrderDiscount: async ({ discountId }, options) => {
137
+ return OrderDiscounts.findOne(buildFindByIdSelector(discountId), options);
138
+ },
139
+ findOrderDiscounts: async ({ orderId }) => {
140
+ const discounts = OrderDiscounts.find({ orderId });
141
+ return discounts.toArray();
142
+ },
143
+
144
+ // Transformations
145
+ interface: async (orderDiscount, requestContext) => {
146
+ const adapter = await getAdapter(orderDiscount, requestContext);
147
+ return adapter;
148
+ },
149
+
150
+ isValid: async (orderDiscount, requestContext) => {
151
+ const adapter = await getAdapter(orderDiscount, requestContext);
152
+ if (!adapter) return null;
153
+
154
+ if (orderDiscount.trigger === OrderDiscountTrigger.SYSTEM) {
155
+ return adapter.isValidForSystemTriggering();
156
+ }
157
+
158
+ return adapter.isValidForCodeTriggering({
159
+ code: orderDiscount.code,
160
+ });
161
+ },
162
+
163
+ // Adapter
164
+ configurationForPricingAdapterKey: async (
165
+ orderDiscount,
166
+ adapterKey,
167
+ calculationSheet,
168
+ requestContext,
169
+ ) => {
170
+ const adapter = await getAdapter(orderDiscount, requestContext);
171
+ if (!adapter) return null;
172
+
173
+ return adapter.discountForPricingAdapterKey({
174
+ pricingAdapterKey: adapterKey,
175
+ calculationSheet,
176
+ });
177
+ },
178
+
179
+ // Mutations
180
+ createManualOrderDiscount: async (doc, requestContext) => {
181
+ const { code, orderId } = doc;
182
+ // Try to grab single-usage-discount
183
+ if (!code) throw new Error(OrderDiscountErrorCode.CODE_NOT_VALID);
184
+
185
+ const fetchedDiscount = await grabDiscount(doc, requestContext);
186
+ if (fetchedDiscount) return fetchedDiscount;
187
+
188
+ const order = await requestContext.modules.orders.findOrder({ orderId });
189
+ const director = await OrderDiscountDirector.actions(
190
+ { order, orderDiscount: doc },
191
+ requestContext,
192
+ );
193
+ const discountKey = await director.resolveDiscountKeyFromStaticCode({
194
+ code,
195
+ });
196
+
197
+ if (discountKey) {
198
+ const newDiscount = await createDiscount(
199
+ {
200
+ ...doc,
201
+ discountKey,
202
+ },
203
+ requestContext.userId,
204
+ );
205
+
206
+ const reservedDiscount = await reserveDiscount(newDiscount, requestContext).catch(
207
+ async (error) => {
208
+ // Rollback
209
+ await deleteDiscount(newDiscount._id, requestContext);
210
+ throw error;
211
+ },
212
+ );
213
+
214
+ await updateCalculation(orderId, requestContext);
215
+
216
+ emit('ORDER_ADD_DISCOUNT', { discount: reserveDiscount });
217
+
218
+ return reservedDiscount;
219
+ }
220
+
221
+ throw new Error(OrderDiscountErrorCode.CODE_NOT_VALID);
222
+ },
223
+
224
+ create: async (doc, userId) => {
225
+ const discount = await createDiscount(doc, userId);
226
+
227
+ if (discount.trigger === OrderDiscountTrigger.USER) {
228
+ emit('ORDER_CREATE_DISCOUNT', { discount });
229
+ }
230
+
231
+ return discount;
232
+ },
233
+
234
+ delete: deleteDiscount,
235
+
236
+ update: async (orderDiscountId, doc, userId) => {
237
+ await mutations.update(orderDiscountId, doc, userId);
238
+
239
+ const selector = buildFindByIdSelector(orderDiscountId);
240
+ const discount = await OrderDiscounts.findOne(selector, {});
241
+ emit('ORDER_UPDATE_DISCOUNT', { discount });
242
+ return discount;
243
+ },
244
+ };
245
+ };
@@ -0,0 +1,331 @@
1
+ import { Collection, Filter, ModuleMutations, Query, Update } from '@unchainedshop/types/common';
2
+ import { OrdersModule } from '@unchainedshop/types/orders';
3
+ import {
4
+ OrderPayment,
5
+ OrderPaymentsModule,
6
+ OrderPaymentStatus,
7
+ } from '@unchainedshop/types/orders.payments';
8
+ import { emit, registerEvents } from '@unchainedshop/events';
9
+ import { log } from '@unchainedshop/logger';
10
+ import { generateDbFilterById, generateDbMutations } from '@unchainedshop/utils';
11
+ import { OrderPaymentsSchema } from '../db/OrderPaymentsSchema';
12
+
13
+ const ORDER_PAYMENT_EVENTS: string[] = ['ORDER_UPDATE_PAYMENT', 'ORDER_SIGN_PAYMENT', 'ORDER_PAY'];
14
+
15
+ const buildFindByIdSelector = (orderPaymentId: string) =>
16
+ generateDbFilterById(orderPaymentId) as Filter<OrderPayment>;
17
+
18
+ const buildFindByContextDataSelector = (context: any): Query => {
19
+ const contextKeys = Object.keys(context);
20
+
21
+ if (contextKeys.length === 0) return null;
22
+
23
+ const selector: Query = contextKeys.reduce(
24
+ (currentSelector, key) =>
25
+ context[key] !== undefined
26
+ ? {
27
+ ...currentSelector,
28
+ [`context.${key}`]: context[key],
29
+ }
30
+ : currentSelector,
31
+ {},
32
+ );
33
+ return selector;
34
+ };
35
+
36
+ export const configureOrderPaymentsModule = ({
37
+ OrderPayments,
38
+ updateCalculation,
39
+ }: {
40
+ OrderPayments: Collection<OrderPayment>;
41
+ updateCalculation: OrdersModule['updateCalculation'];
42
+ }): OrderPaymentsModule => {
43
+ registerEvents(ORDER_PAYMENT_EVENTS);
44
+
45
+ const mutations = generateDbMutations<OrderPayment>(OrderPayments, OrderPaymentsSchema, {
46
+ permanentlyDeleteByDefault: true,
47
+ hasCreateOnly: false,
48
+ }) as ModuleMutations<OrderPayment>;
49
+
50
+ const normalizedStatus: OrderPaymentsModule['normalizedStatus'] = (orderPayment) => {
51
+ return orderPayment.status === null
52
+ ? OrderPaymentStatus.OPEN
53
+ : (orderPayment.status as OrderPaymentStatus);
54
+ };
55
+
56
+ const buildPaymentProviderActionsContext = (orderPayment: OrderPayment, transactionContext) => ({
57
+ paymentProviderId: orderPayment.paymentProviderId,
58
+ paymentContext: {
59
+ orderPayment,
60
+ transactionContext: {
61
+ ...(transactionContext || {}),
62
+ ...(orderPayment.context || {}),
63
+ },
64
+ },
65
+ });
66
+
67
+ const updateStatus: OrderPaymentsModule['updateStatus'] = async (
68
+ orderPaymentId,
69
+ { status, transactionId, info },
70
+ userId,
71
+ ) => {
72
+ log(`OrderPayment ${orderPaymentId} -> New Status: ${status}`);
73
+
74
+ const date = new Date();
75
+ const modifier: Update<OrderPayment> = {
76
+ $set: { status, updated: new Date(), updatedBy: userId },
77
+ $push: {
78
+ log: {
79
+ date,
80
+ status,
81
+ info,
82
+ },
83
+ },
84
+ };
85
+ if (transactionId) {
86
+ modifier.$set.transactionId = transactionId;
87
+ }
88
+ if (status === OrderPaymentStatus.PAID) {
89
+ modifier.$set.paid = date;
90
+ }
91
+
92
+ const selector = buildFindByIdSelector(orderPaymentId);
93
+ await OrderPayments.updateOne(selector, modifier);
94
+ return OrderPayments.findOne(selector, {});
95
+ };
96
+
97
+ return {
98
+ // Queries
99
+ findOrderPayment: async ({ orderPaymentId }, options) => {
100
+ return OrderPayments.findOne(buildFindByIdSelector(orderPaymentId), options);
101
+ },
102
+ findOrderPaymentByContextData: async ({ context }, options) => {
103
+ const selector = buildFindByContextDataSelector(context);
104
+
105
+ return OrderPayments.findOne(selector, options);
106
+ },
107
+ countOrderPaymentsByContextData: async ({ context }, options) => {
108
+ const selector = buildFindByContextDataSelector(context);
109
+
110
+ return OrderPayments.countDocuments(selector, options);
111
+ },
112
+ // Transformations
113
+ discounts: (orderPayment, { order, orderDiscount }, context) => {
114
+ const { modules } = context;
115
+ if (!orderPayment) return [];
116
+ const pricingSheet = modules.orders.payments.pricingSheet(orderPayment, order.currency, context);
117
+ return pricingSheet.discountPrices(orderDiscount._id).map((discount) => ({
118
+ payment: orderPayment,
119
+ ...discount,
120
+ }));
121
+ },
122
+
123
+ isBlockingOrderConfirmation: async (orderPayment, requestContext) => {
124
+ if (orderPayment.status === OrderPaymentStatus.PAID) return false;
125
+
126
+ const provider = await requestContext.modules.payment.paymentProviders.findProvider({
127
+ paymentProviderId: orderPayment.paymentProviderId,
128
+ });
129
+
130
+ const isPayLaterAllowed = await requestContext.modules.payment.paymentProviders.isPayLaterAllowed(
131
+ provider,
132
+ requestContext,
133
+ );
134
+
135
+ return !isPayLaterAllowed;
136
+ },
137
+ isBlockingOrderFullfillment: (orderPayment) => {
138
+ if (orderPayment.status === OrderPaymentStatus.PAID) return false;
139
+ return true;
140
+ },
141
+
142
+ normalizedStatus,
143
+
144
+ pricingSheet: (orderPayment, currency, { modules }) => {
145
+ return modules.payment.paymentProviders.pricingSheet({
146
+ calculation: orderPayment.calculation,
147
+ currency,
148
+ });
149
+ },
150
+
151
+ // Mutations
152
+
153
+ create: async (doc, userId) => {
154
+ const orderPaymentId = await mutations.create(
155
+ { ...doc, status: null, context: doc.context || {} },
156
+ userId,
157
+ );
158
+
159
+ const orderPayment = await OrderPayments.findOne(buildFindByIdSelector(orderPaymentId));
160
+
161
+ return orderPayment;
162
+ },
163
+
164
+ confirm: async (orderPayment, { transactionContext }, requestContext) => {
165
+ const { services } = requestContext;
166
+
167
+ if (normalizedStatus(orderPayment) !== OrderPaymentStatus.PAID) {
168
+ return orderPayment;
169
+ }
170
+
171
+ const arbitraryResponseData = await services.payment.confirm(
172
+ buildPaymentProviderActionsContext(orderPayment, transactionContext),
173
+ requestContext,
174
+ );
175
+
176
+ if (arbitraryResponseData) {
177
+ return updateStatus(
178
+ orderPayment._id,
179
+ {
180
+ status: OrderPaymentStatus.PAID,
181
+ info: JSON.stringify(arbitraryResponseData),
182
+ },
183
+ requestContext.userId,
184
+ );
185
+ }
186
+
187
+ return orderPayment;
188
+ },
189
+
190
+ cancel: async (orderPayment, { transactionContext }, requestContext) => {
191
+ const { services } = requestContext;
192
+
193
+ if (normalizedStatus(orderPayment) !== OrderPaymentStatus.PAID) {
194
+ return orderPayment;
195
+ }
196
+
197
+ const arbitraryResponseData = await services.payment.cancel(
198
+ buildPaymentProviderActionsContext(orderPayment, transactionContext),
199
+ requestContext,
200
+ );
201
+
202
+ if (arbitraryResponseData) {
203
+ return updateStatus(
204
+ orderPayment._id,
205
+ {
206
+ status: OrderPaymentStatus.REFUNDED,
207
+ info: JSON.stringify(arbitraryResponseData),
208
+ },
209
+ requestContext.userId,
210
+ );
211
+ }
212
+
213
+ return orderPayment;
214
+ },
215
+
216
+ charge: async (orderPayment, { transactionContext }, requestContext) => {
217
+ const { services } = requestContext;
218
+
219
+ if (normalizedStatus(orderPayment) !== OrderPaymentStatus.OPEN) {
220
+ return orderPayment;
221
+ }
222
+
223
+ const arbitraryResponseData = await services.payment.charge(
224
+ buildPaymentProviderActionsContext(orderPayment, transactionContext),
225
+ requestContext,
226
+ );
227
+
228
+ if (arbitraryResponseData) {
229
+ const { transactionId, ...info } = arbitraryResponseData;
230
+ return updateStatus(
231
+ orderPayment._id,
232
+ {
233
+ transactionId,
234
+ status: OrderPaymentStatus.PAID,
235
+ info: JSON.stringify(info),
236
+ },
237
+ requestContext.userId,
238
+ );
239
+ }
240
+
241
+ return orderPayment;
242
+ },
243
+
244
+ logEvent: async (orderPaymentId, event) => {
245
+ const date = new Date();
246
+ const modifier = {
247
+ $push: {
248
+ log: {
249
+ date,
250
+ status: undefined,
251
+ info: JSON.stringify(event),
252
+ },
253
+ },
254
+ };
255
+ await OrderPayments.updateOne(generateDbFilterById(orderPaymentId), modifier);
256
+
257
+ return true;
258
+ },
259
+
260
+ markAsPaid: async (orderPayment, meta, userId) => {
261
+ if (normalizedStatus(orderPayment) !== OrderPaymentStatus.OPEN) return;
262
+
263
+ await updateStatus(
264
+ orderPayment._id,
265
+ {
266
+ status: OrderPaymentStatus.PAID,
267
+ info: meta ? JSON.stringify(meta) : 'mark paid manually',
268
+ },
269
+ userId,
270
+ );
271
+ emit('ORDER_PAY', { orderPayment });
272
+ },
273
+
274
+ updateContext: async (orderPaymentId, context, requestContext) => {
275
+ const selector = buildFindByIdSelector(orderPaymentId);
276
+ const orderPayment = await OrderPayments.findOne(selector, {});
277
+ const { orderId } = orderPayment;
278
+
279
+ log(`OrderPayment ${orderPaymentId} -> Update Context`, {
280
+ orderId,
281
+ context,
282
+ });
283
+ const result = await OrderPayments.updateOne(selector, {
284
+ $set: {
285
+ context: { ...(orderPayment.context || {}), ...context },
286
+ updated: new Date(),
287
+ updatedBy: requestContext.userId,
288
+ },
289
+ });
290
+
291
+ if (result.modifiedCount) {
292
+ await updateCalculation(orderId, requestContext);
293
+ emit('ORDER_UPDATE_PAYMENT', {
294
+ orderPayment: {
295
+ ...orderPayment,
296
+ context: { ...(orderPayment.context || {}), ...context },
297
+ },
298
+ });
299
+ return true;
300
+ }
301
+
302
+ return false;
303
+ },
304
+
305
+ updateStatus,
306
+
307
+ updateCalculation: async (orderPayment, requestContext) => {
308
+ log(`OrderPayment ${orderPayment._id} -> Update Calculation`, {
309
+ orderId: orderPayment.orderId,
310
+ });
311
+
312
+ const calculation = await requestContext.modules.payment.paymentProviders.calculate(
313
+ {
314
+ item: orderPayment,
315
+ },
316
+ requestContext,
317
+ );
318
+
319
+ const selector = buildFindByIdSelector(orderPayment._id);
320
+ await OrderPayments.updateOne(selector, {
321
+ $set: {
322
+ calculation,
323
+ updated: new Date(),
324
+ updatedBy: requestContext.userId,
325
+ },
326
+ });
327
+
328
+ return OrderPayments.findOne(selector);
329
+ },
330
+ };
331
+ };