@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.
- package/.npm/package/README +7 -0
- package/.npm/package/npm-shrinkwrap.json +190 -0
- package/.versions +54 -0
- package/README.md +5 -0
- package/lib/db/OrderDeliveriesCollection.d.ts +3 -0
- package/lib/db/OrderDeliveriesCollection.js +8 -0
- package/lib/db/OrderDeliveriesCollection.js.map +1 -0
- package/lib/db/OrderDeliveriesSchema.d.ts +1 -0
- package/lib/db/OrderDeliveriesSchema.js +20 -0
- package/lib/db/OrderDeliveriesSchema.js.map +1 -0
- package/lib/db/OrderDiscountTrigger.d.ts +4 -0
- package/lib/db/OrderDiscountTrigger.js +6 -0
- package/lib/db/OrderDiscountTrigger.js.map +1 -0
- package/lib/db/OrderDiscountsCollection.d.ts +3 -0
- package/lib/db/OrderDiscountsCollection.js +11 -0
- package/lib/db/OrderDiscountsCollection.js.map +1 -0
- package/lib/db/OrderDiscountsSchema.d.ts +1 -0
- package/lib/db/OrderDiscountsSchema.js +12 -0
- package/lib/db/OrderDiscountsSchema.js.map +1 -0
- package/lib/db/OrderPaymentsCollection.d.ts +3 -0
- package/lib/db/OrderPaymentsCollection.js +8 -0
- package/lib/db/OrderPaymentsCollection.js.map +1 -0
- package/lib/db/OrderPaymentsSchema.d.ts +1 -0
- package/lib/db/OrderPaymentsSchema.js +20 -0
- package/lib/db/OrderPaymentsSchema.js.map +1 -0
- package/lib/db/OrderPositionsCollection.d.ts +3 -0
- package/lib/db/OrderPositionsCollection.js +11 -0
- package/lib/db/OrderPositionsCollection.js.map +1 -0
- package/lib/db/OrderPositionsSchema.d.ts +1 -0
- package/lib/db/OrderPositionsSchema.js +37 -0
- package/lib/db/OrderPositionsSchema.js.map +1 -0
- package/lib/db/OrdersCollection.d.ts +3 -0
- package/lib/db/OrdersCollection.js +33 -0
- package/lib/db/OrdersCollection.js.map +1 -0
- package/lib/db/OrdersSchema.d.ts +1 -0
- package/lib/db/OrdersSchema.js +30 -0
- package/lib/db/OrdersSchema.js.map +1 -0
- package/lib/director/OrderDiscountAdapter.d.ts +2 -0
- package/lib/director/OrderDiscountAdapter.js +3 -0
- package/lib/director/OrderDiscountAdapter.js.map +1 -0
- package/lib/director/OrderDiscountDirector.d.ts +2 -0
- package/lib/director/OrderDiscountDirector.js +3 -0
- package/lib/director/OrderDiscountDirector.js.map +1 -0
- package/lib/director/OrderPricingAdapter.d.ts +3 -0
- package/lib/director/OrderPricingAdapter.js +25 -0
- package/lib/director/OrderPricingAdapter.js.map +1 -0
- package/lib/director/OrderPricingDirector.d.ts +2 -0
- package/lib/director/OrderPricingDirector.js +48 -0
- package/lib/director/OrderPricingDirector.js.map +1 -0
- package/lib/director/OrderPricingSheet.d.ts +3 -0
- package/lib/director/OrderPricingSheet.js +113 -0
- package/lib/director/OrderPricingSheet.js.map +1 -0
- package/lib/module/configureOrderDeliveriesModule.d.ts +7 -0
- package/lib/module/configureOrderDeliveriesModule.js +164 -0
- package/lib/module/configureOrderDeliveriesModule.js.map +1 -0
- package/lib/module/configureOrderDiscountsModule.d.ts +7 -0
- package/lib/module/configureOrderDiscountsModule.js +183 -0
- package/lib/module/configureOrderDiscountsModule.js.map +1 -0
- package/lib/module/configureOrderPaymentsModule.d.ts +7 -0
- package/lib/module/configureOrderPaymentsModule.js +229 -0
- package/lib/module/configureOrderPaymentsModule.js.map +1 -0
- package/lib/module/configureOrderPositionsModule.d.ts +7 -0
- package/lib/module/configureOrderPositionsModule.js +213 -0
- package/lib/module/configureOrderPositionsModule.js.map +1 -0
- package/lib/module/configureOrdersModule-mutations.d.ts +12 -0
- package/lib/module/configureOrdersModule-mutations.js +162 -0
- package/lib/module/configureOrdersModule-mutations.js.map +1 -0
- package/lib/module/configureOrdersModule-processing.d.ts +13 -0
- package/lib/module/configureOrdersModule-processing.js +343 -0
- package/lib/module/configureOrdersModule-processing.js.map +1 -0
- package/lib/module/configureOrdersModule-queries.d.ts +5 -0
- package/lib/module/configureOrdersModule-queries.js +55 -0
- package/lib/module/configureOrdersModule-queries.js.map +1 -0
- package/lib/module/configureOrdersModule-transformations.d.ts +5 -0
- package/lib/module/configureOrdersModule-transformations.js +108 -0
- package/lib/module/configureOrdersModule-transformations.js.map +1 -0
- package/lib/module/configureOrdersModule.d.ts +3 -0
- package/lib/module/configureOrdersModule.js +239 -0
- package/lib/module/configureOrdersModule.js.map +1 -0
- package/lib/orders-index.d.ts +11 -0
- package/lib/orders-index.js +12 -0
- package/lib/orders-index.js.map +1 -0
- package/lib/orders-settings.d.ts +20 -0
- package/lib/orders-settings.js +17 -0
- package/lib/orders-settings.js.map +1 -0
- package/lib/service/createUserCartService.d.ts +2 -0
- package/lib/service/createUserCartService.js +23 -0
- package/lib/service/createUserCartService.js.map +1 -0
- package/lib/service/migrateOrderCartService.d.ts +2 -0
- package/lib/service/migrateOrderCartService.js +10 -0
- package/lib/service/migrateOrderCartService.js.map +1 -0
- package/lib/service/orderServices.d.ts +2 -0
- package/lib/service/orderServices.js +7 -0
- package/lib/service/orderServices.js.map +1 -0
- package/package.js +33 -0
- package/package.json +43 -0
- package/plugins/discount-100-off.ts +44 -0
- package/plugins/discount-half-price-manual.ts +43 -0
- package/plugins/discount-half-price.ts +46 -0
- package/plugins/order-delivery.ts +45 -0
- package/plugins/order-discount.ts +158 -0
- package/plugins/order-items-discount.ts +116 -0
- package/plugins/order-items.ts +61 -0
- package/plugins/order-payment.ts +46 -0
- package/src/db/OrderDeliveriesCollection.ts +12 -0
- package/src/db/OrderDeliveriesSchema.js +24 -0
- package/src/db/OrderDiscountTrigger.ts +4 -0
- package/src/db/OrderDiscountsCollection.ts +15 -0
- package/src/db/OrderDiscountsSchema.js +15 -0
- package/src/db/OrderPaymentsCollection.ts +12 -0
- package/src/db/OrderPaymentsSchema.js +24 -0
- package/src/db/OrderPositionsCollection.ts +15 -0
- package/src/db/OrderPositionsSchema.js +41 -0
- package/src/db/OrdersCollection.ts +37 -0
- package/src/db/OrdersSchema.js +34 -0
- package/src/director/OrderDiscountAdapter.ts +5 -0
- package/src/director/OrderDiscountDirector.ts +4 -0
- package/src/director/OrderPricingAdapter.ts +41 -0
- package/src/director/OrderPricingDirector.ts +68 -0
- package/src/director/OrderPricingSheet.ts +138 -0
- package/src/module/configureOrderDeliveriesModule.ts +240 -0
- package/src/module/configureOrderDiscountsModule.ts +245 -0
- package/src/module/configureOrderPaymentsModule.ts +331 -0
- package/src/module/configureOrderPositionsModule.ts +329 -0
- package/src/module/configureOrdersModule-mutations.ts +230 -0
- package/src/module/configureOrdersModule-processing.ts +506 -0
- package/src/module/configureOrdersModule-queries.ts +71 -0
- package/src/module/configureOrdersModule-transformations.ts +145 -0
- package/src/module/configureOrdersModule.ts +359 -0
- package/src/orders-index.ts +15 -0
- package/src/orders-settings.ts +23 -0
- package/src/service/createUserCartService.ts +38 -0
- package/src/service/migrateOrderCartService.ts +22 -0
- package/src/service/orderServices.ts +8 -0
- package/tests/orders-index.test.ts +50 -0
- package/tsconfig.build.json +26 -0
- 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
|
+
};
|