cloudcommerce 0.0.91 → 0.0.93

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 (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/packages/api/package.json +1 -1
  4. package/packages/apps/correios/package.json +1 -1
  5. package/packages/apps/custom-shipping/package.json +1 -1
  6. package/packages/apps/discounts/package.json +1 -1
  7. package/packages/apps/frenet/package.json +1 -1
  8. package/packages/apps/tiny-erp/package.json +1 -1
  9. package/packages/cli/config/firebase.json +4 -2
  10. package/packages/cli/package.json +1 -1
  11. package/packages/config/package.json +1 -1
  12. package/packages/events/package.json +1 -1
  13. package/packages/firebase/package.json +1 -1
  14. package/packages/modules/lib/firebase/checkout.js +162 -0
  15. package/packages/modules/lib/firebase/checkout.js.map +1 -1
  16. package/packages/modules/lib/firebase/functions-checkout/fix-items.js +200 -0
  17. package/packages/modules/lib/firebase/functions-checkout/fix-items.js.map +1 -0
  18. package/packages/modules/lib/firebase/functions-checkout/get-custumerId.js +34 -0
  19. package/packages/modules/lib/firebase/functions-checkout/get-custumerId.js.map +1 -0
  20. package/packages/modules/lib/firebase/functions-checkout/handle-order-transaction.js +123 -0
  21. package/packages/modules/lib/firebase/functions-checkout/handle-order-transaction.js.map +1 -0
  22. package/packages/modules/lib/firebase/functions-checkout/new-order.js +191 -0
  23. package/packages/modules/lib/firebase/functions-checkout/new-order.js.map +1 -0
  24. package/packages/modules/lib/firebase/functions-checkout/request-to-module.js +67 -0
  25. package/packages/modules/lib/firebase/functions-checkout/request-to-module.js.map +1 -0
  26. package/packages/modules/lib/firebase/functions-checkout/utils.js +227 -0
  27. package/packages/modules/lib/firebase/functions-checkout/utils.js.map +1 -0
  28. package/packages/modules/lib/firebase/serve-modules-api.js +9 -4
  29. package/packages/modules/lib/firebase/serve-modules-api.js.map +1 -1
  30. package/packages/modules/package.json +1 -1
  31. package/packages/modules/schemas/@checkout.cjs +1 -1
  32. package/packages/modules/src/firebase/checkout.ts +235 -0
  33. package/packages/modules/src/firebase/functions-checkout/fix-items.ts +219 -0
  34. package/packages/modules/src/firebase/functions-checkout/get-custumerId.ts +33 -0
  35. package/packages/modules/src/firebase/functions-checkout/handle-order-transaction.ts +195 -0
  36. package/packages/modules/src/firebase/functions-checkout/new-order.ts +273 -0
  37. package/packages/modules/src/firebase/functions-checkout/request-to-module.ts +76 -0
  38. package/packages/modules/src/firebase/functions-checkout/utils.ts +301 -0
  39. package/packages/modules/src/firebase/serve-modules-api.ts +9 -4
  40. package/packages/modules/src/types/index.d.ts +67 -0
  41. package/packages/passport/lib/firebase/handle-passport.js +3 -1
  42. package/packages/passport/lib/firebase/handle-passport.js.map +1 -1
  43. package/packages/passport/lib/index.js +1 -0
  44. package/packages/passport/lib/index.js.map +1 -1
  45. package/packages/passport/package.json +1 -1
  46. package/packages/passport/src/firebase/handle-passport.ts +2 -0
  47. package/packages/passport/src/index.ts +1 -0
  48. package/packages/ssr/package.json +1 -1
  49. package/packages/storefront/package.json +1 -1
  50. package/packages/types/package.json +1 -1
  51. package/pnpm-workspace.yaml +1 -0
@@ -1,5 +1,6 @@
1
1
  import { schemas } from '../index.js';
2
2
  import handleModule from './handle-module.js';
3
+ import checkout from './checkout.js';
3
4
 
4
5
  export default (req, res) => {
5
6
  const { method } = req;
@@ -23,10 +24,14 @@ export default (req, res) => {
23
24
  };
24
25
  if (modName === '@checkout') {
25
26
  if (url === '/@checkout') {
26
- return res.status(200).send({
27
- status: 200,
28
- message: 'CHECKOUT',
29
- });
27
+ if (method === 'GET') {
28
+ return res.status(405)
29
+ .send({
30
+ error_code: 'CKT101',
31
+ message: 'GET is acceptable only to JSON schema, at /@checkout/schema',
32
+ });
33
+ }
34
+ return checkout(schemas[modName].params, req, res, req.hostname);
30
35
  }
31
36
  if (url === '/@checkout/schema') {
32
37
  return sendSchema();
@@ -1 +1 @@
1
- {"version":3,"file":"serve-modules-api.js","sourceRoot":"","sources":["../../src/firebase/serve-modules-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve-modules-api.js","sourceRoot":"","sources":["../../src/firebase/serve-modules-api.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/modules",
3
3
  "type": "module",
4
- "version": "0.0.91",
4
+ "version": "0.0.93",
5
5
  "description": "E-Com Plus Cloud Commerce modules API",
6
6
  "main": "lib/index.cjs",
7
7
  "exports": {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable quote-props, comma-dangle, array-bracket-spacing */
2
2
 
3
3
  exports.params = {
4
- '$schema': 'http://json-schema.org/draft-06/schema#',
4
+ '$schema': 'http://json-schema.org/draft-07/schema#',
5
5
  'title': 'Checkout body',
6
6
  'description': 'Triggered to handle checkout with billing and shipping and create new order',
7
7
  'type': 'object',
@@ -0,0 +1,235 @@
1
+ import type { Request, Response } from 'firebase-functions';
2
+ import type { ValidateFunction } from 'ajv';
3
+ import type { CheckoutBody } from '@cloudcommerce/types';
4
+ import type {
5
+ CheckoutBodyWithItems,
6
+ BodyOrder,
7
+ Amount,
8
+ Item,
9
+ BodyPayment,
10
+ } from '../types/index';
11
+ import {
12
+ ajv,
13
+ sendRequestError,
14
+ } from './ajv';
15
+ import fixItems from './functions-checkout/fix-items';
16
+ import getCustomerId from './functions-checkout/get-custumerId';
17
+ import requestModule from './functions-checkout/request-to-module';
18
+ import {
19
+ sendError,
20
+ fixAmount,
21
+ getValidResults,
22
+ handleShippingServices,
23
+ handleApplyDiscount,
24
+ handleListPayments,
25
+ } from './functions-checkout/utils';
26
+ import createOrder from './functions-checkout/new-order';
27
+
28
+ const runCheckout = async (
29
+ checkoutBody: CheckoutBody,
30
+ accessToken:string,
31
+ res:Response,
32
+ validate: ValidateFunction,
33
+ hostname: string,
34
+ ) => {
35
+ if (!validate(checkoutBody)) {
36
+ return sendRequestError(res, '@checkout', validate.errors);
37
+ }
38
+ const { items, ...newBody } = checkoutBody;
39
+ const newItems = await fixItems(items);
40
+ const amount: Amount = {
41
+ subtotal: 0,
42
+ discount: 0,
43
+ freight: 0,
44
+ total: 0,
45
+ };
46
+ const body: CheckoutBodyWithItems = {
47
+ ...newBody,
48
+ items: [...newItems],
49
+ subtotal: 0,
50
+ amount,
51
+ };
52
+
53
+ const countCheckoutItems = body.items.length;
54
+ const { customer } = body;
55
+ const customerId = await getCustomerId(accessToken, customer);
56
+ if (customerId && customer) {
57
+ if (newItems.length) {
58
+ const { _id, ...newCustomer } = customer;
59
+ // start mounting order body
60
+ // https://developers.e-com.plus/docs/api/#/store/orders/orders
61
+ const dateTime = new Date().toISOString();
62
+ const orderBody: BodyOrder = {
63
+ opened_at: dateTime,
64
+ buyers: [
65
+ // received customer info
66
+ {
67
+ _id: customerId,
68
+ ...newCustomer,
69
+ },
70
+ ],
71
+ items: [],
72
+ amount: {
73
+ total: 0,
74
+ },
75
+ };
76
+ // bypass some order fields
77
+ const fields = [
78
+ 'utm',
79
+ 'affiliate_code',
80
+ 'browser_ip',
81
+ 'channel_id',
82
+ 'channel_type',
83
+ 'domain',
84
+ 'notes',
85
+ ];
86
+ fields.forEach((field) => {
87
+ if (body[field]) {
88
+ orderBody[field] = body[field];
89
+ }
90
+ });
91
+ if (orderBody.domain) {
92
+ // consider default Storefront app routes
93
+ if (!orderBody.checkout_link) {
94
+ orderBody.checkout_link = `https://${orderBody.domain}/app/#/checkout/(_id)`;
95
+ }
96
+ if (!orderBody.status_link) {
97
+ orderBody.status_link = `https://${orderBody.domain}/app/#/order/(_id)`;
98
+ }
99
+ }
100
+
101
+ // count subtotal value
102
+ let subtotal = 0;
103
+ newItems.forEach(
104
+ (item:Item) => {
105
+ subtotal += (item.final_price || item.price * item.quantity);
106
+ // pass each item to prevent object overwrite
107
+ if (orderBody.items) {
108
+ orderBody.items.push({ ...item });
109
+ }
110
+ },
111
+ );
112
+ if (subtotal <= 0 && items.length < countCheckoutItems) {
113
+ return sendError(res, 400, 'CKT801', 'Cannot handle checkout, any valid cart item');
114
+ }
115
+ amount.subtotal = subtotal;
116
+ body.subtotal = subtotal;
117
+ fixAmount(amount, body, orderBody);
118
+
119
+ const transactions = Array.isArray(body.transaction) ? body.transaction : [body.transaction];
120
+ // add customer ID to order and transaction
121
+ customer._id = customerId;
122
+ transactions.forEach(({ buyer }) => {
123
+ if (buyer) {
124
+ buyer.customer_id = customerId;
125
+ }
126
+ });
127
+
128
+ let listShipping = await requestModule(body, hostname, 'shipping');
129
+ if (listShipping) {
130
+ listShipping = getValidResults(listShipping, 'shipping_services');
131
+ handleShippingServices(body, listShipping, amount, orderBody);
132
+ } else {
133
+ // problem with shipping response object
134
+ return sendError(
135
+ res,
136
+ 400,
137
+ 'CKT901',
138
+ 'Any valid shipping service from /calculate_shipping module',
139
+ {
140
+ en_us: 'Shipping method not available, please choose another',
141
+ pt_br: 'Forma de envio indisponível, por favor escolha outra',
142
+ },
143
+ );
144
+ }
145
+
146
+ let discounts = await requestModule(body, hostname, 'discount');
147
+ if (discounts) {
148
+ discounts = getValidResults(discounts);
149
+ handleApplyDiscount(body, discounts, amount, orderBody);
150
+ }
151
+
152
+ const { transaction, ...bodyPayment } = body;
153
+ let paymentsBody: BodyPayment;
154
+ if (Array.isArray(transaction)) {
155
+ paymentsBody = {
156
+ ...bodyPayment,
157
+ transaction: transaction[0],
158
+ };
159
+ } else {
160
+ paymentsBody = {
161
+ ...bodyPayment,
162
+ transaction,
163
+ };
164
+ }
165
+
166
+ let listPaymentGateways = await requestModule(paymentsBody, hostname, 'payment');
167
+
168
+ if (listPaymentGateways) {
169
+ listPaymentGateways = getValidResults(listPaymentGateways, 'payment_gateways');
170
+ handleListPayments(body, listPaymentGateways, paymentsBody, amount, orderBody);
171
+ } else {
172
+ return sendError(
173
+ res,
174
+ 409,
175
+ 'CKT902',
176
+ 'Any valid payment gateway from /list_payments module',
177
+ {
178
+ en_us: 'Payment method not available, please choose another',
179
+ pt_br: 'Forma de pagamento indisponível, por favor escolha outra',
180
+ },
181
+ );
182
+ }
183
+
184
+ return createOrder(
185
+ res,
186
+ accessToken,
187
+ hostname,
188
+ amount,
189
+ checkoutBody,
190
+ orderBody,
191
+ transactions,
192
+ dateTime,
193
+ );
194
+ }
195
+ return sendError(res, 400, 'CKT801', 'Cannot handle checkout, any valid cart item');
196
+ }
197
+ return sendError(
198
+ res,
199
+ 404,
200
+ -404,
201
+ 'Not found',
202
+ {
203
+ en_us: 'No customers found with ID or email provided',
204
+ pt_br: 'Nenhum cliente encontrado com ID ou e-mail fornecido',
205
+ },
206
+ );
207
+ };
208
+
209
+ export default (
210
+ schema: {[key:string] : any},
211
+ req: Request,
212
+ res:Response,
213
+ hostname: string,
214
+ ) => {
215
+ const validate = ajv.compile(schema);
216
+ const ip = req.headers['x-real-ip'];
217
+ if (!req.body.browser_ip && ip) {
218
+ req.body.browser_ip = ip;
219
+ }
220
+ let acessToken = req.headers.authorization;
221
+ if (acessToken) {
222
+ acessToken = acessToken.replace(/Bearer /i, '');
223
+ return runCheckout(req.body, acessToken, res, validate, hostname);
224
+ }
225
+ return sendError(
226
+ res,
227
+ 401,
228
+ 109,
229
+ "Token is required on 'Authorization'",
230
+ {
231
+ en_us: 'No authorization for the requested method and resource',
232
+ pt_br: 'Sem autorização para o método e recurso solicitado',
233
+ },
234
+ );
235
+ };
@@ -0,0 +1,219 @@
1
+ import type { Products, CheckoutBody } from '@cloudcommerce/types';
2
+ import type { Items } from '../../types';
3
+ import api from '@cloudcommerce/api';
4
+
5
+ export default async (
6
+ checkoutItems: CheckoutBody['items'],
7
+ ): Promise<Items> => {
8
+ const items = [...checkoutItems] as Items;
9
+ // get each cart item
10
+ // count done processes
11
+ let itemsDone = 0;
12
+ const itemsTodo = items.length;
13
+ // eslint-disable-next-line consistent-return
14
+ const doFinally = () => {
15
+ // after each item
16
+ itemsDone += 1;
17
+ if (itemsDone === itemsTodo) {
18
+ return items;
19
+ }
20
+ };
21
+
22
+ // run item by item
23
+ for (let i = 0; i < items.length; i++) {
24
+ // i, item scoped
25
+ const item = items[i] as Items[number];
26
+ const removeItem = () => {
27
+ // remove invalid item from list
28
+ items.splice(i, 1);
29
+ doFinally();
30
+ };
31
+ if (!item.quantity) {
32
+ // ignore items without quantity or zero
33
+ removeItem();
34
+ i -= 1;
35
+ continue;
36
+ }
37
+
38
+ const proceedItem = () => {
39
+ // additions to final price
40
+ if (Array.isArray(item.customizations)) {
41
+ item.customizations.forEach((customization) => {
42
+ if (item.final_price) {
43
+ if (customization.add_to_price) {
44
+ const { type, addition } = customization.add_to_price;
45
+ item.final_price += type === 'fixed'
46
+ ? addition
47
+ : ((item.price * addition) / 100);
48
+ }
49
+ }
50
+ });
51
+ }
52
+ // done
53
+ doFinally();
54
+ };
55
+
56
+ const checkItem = (product:Products) => {
57
+ if (!product.available) {
58
+ removeItem();
59
+ } else {
60
+ let body: Products | undefined;
61
+
62
+ // check variation if any
63
+ if (!item.variation_id) {
64
+ body = product;
65
+ } else {
66
+ // find respective variation
67
+ let variation:Exclude<Products['variations'], undefined>[number] | undefined;
68
+ if (product.variations) {
69
+ variation = product.variations.find(
70
+ (variationFind) => variationFind._id === item.variation_id,
71
+ );
72
+ }
73
+ if (variation) {
74
+ // merge product body with variation object
75
+ body = Object.assign(product, variation);
76
+ }
77
+ }
78
+ // logger.log(body._id)
79
+
80
+ if (!body || (body.min_quantity && body.min_quantity > item.quantity)) {
81
+ // cannot handle current item
82
+ // invalid variation or quantity lower then minimum
83
+ removeItem();
84
+ } else {
85
+ // check quantity
86
+ if (body.quantity && body.quantity < item.quantity) {
87
+ // reduce to max available quantity
88
+ item.quantity = body.quantity;
89
+ }
90
+
91
+ // extend item properties with body
92
+ [
93
+ 'sku',
94
+ 'name',
95
+ 'currency_id',
96
+ 'currency_symbol',
97
+ 'price',
98
+ 'dimensions',
99
+ 'weight',
100
+ 'production_time',
101
+ ].forEach((prop) => {
102
+ if (body && body[prop] !== undefined) {
103
+ item[prop] = body[prop];
104
+ }
105
+ });
106
+ // price is required
107
+ if (!item.price) {
108
+ item.price = 0;
109
+ }
110
+
111
+ if (Array.isArray(item.flags)) {
112
+ // prevent error with repeated flags
113
+ const flags: string[] = [];
114
+ item.flags.forEach((flag) => {
115
+ if (!flags.includes(flag)) {
116
+ flags.push(flag);
117
+ }
118
+ });
119
+ item.flags = flags;
120
+ }
121
+ //
122
+ // item.final_price = getPrice(body);
123
+ proceedItem();
124
+ }
125
+ }
126
+ };
127
+
128
+ const checkKitProduct = (kitProduct: Products, kitProductId: string) => {
129
+ if (item.kit_product) {
130
+ if (kitProduct.available && kitProduct.kit_composition) {
131
+ // check kit composition and quantities
132
+ let packQuantity = 0;
133
+ let isFixedQuantity = true;
134
+ let kitItem: Exclude<Products['kit_composition'], undefined>[number] | undefined;
135
+
136
+ kitProduct.kit_composition.forEach((currentKitItem) => {
137
+ if (currentKitItem.quantity) {
138
+ packQuantity += currentKitItem.quantity;
139
+ } else if (isFixedQuantity) {
140
+ isFixedQuantity = false;
141
+ }
142
+ if (currentKitItem._id === item.product_id) {
143
+ kitItem = currentKitItem;
144
+ }
145
+ });
146
+ if (!isFixedQuantity) {
147
+ // use parent product min quantity
148
+ packQuantity = kitProduct.min_quantity || 0;
149
+ }
150
+
151
+ if (kitItem && (kitItem.quantity === undefined
152
+ || item.quantity % kitItem.quantity === 0)) {
153
+ // valid kit item and quantity
154
+ let kitTotalQuantity = 0;
155
+ items.forEach((itemFind) => {
156
+ if (itemFind.kit_product && itemFind.kit_product._id === kitProductId) {
157
+ kitTotalQuantity += itemFind.quantity;
158
+ }
159
+ });
160
+
161
+ const minPacks = kitItem.quantity
162
+ ? item.quantity / kitItem.quantity
163
+ : 1;
164
+ if (kitTotalQuantity && kitTotalQuantity % (minPacks * packQuantity) === 0) {
165
+ // matched pack quantity
166
+ // item.kit_product.price = getPrice(kitProduct);
167
+ item.kit_product.pack_quantity = packQuantity;
168
+ if (item.kit_product.price) {
169
+ // set final price from kit
170
+ item.final_price = item.kit_product.price / packQuantity;
171
+ }
172
+ proceedItem();
173
+ }
174
+ }
175
+ }
176
+
177
+ // remove items with invalid kit
178
+ let index = 0;
179
+ while (index < items.length) {
180
+ const itemKit = items[index].kit_product;
181
+ if (itemKit && itemKit._id === kitProductId) {
182
+ items.splice(index, 1);
183
+ } else {
184
+ index += 1;
185
+ }
186
+ }
187
+ doFinally();
188
+ }
189
+ };
190
+
191
+ if (item.kit_product) {
192
+ // GET public kit product object
193
+ const kitProductId = item.kit_product._id;
194
+ // eslint-disable-next-line no-await-in-loop
195
+ const kitProduct = (await api.get(`products/${kitProductId}`, {
196
+ isNoAuth: true,
197
+ })).data;
198
+ if (kitProduct) {
199
+ checkKitProduct(kitProduct, kitProductId);
200
+ } else {
201
+ removeItem();
202
+ }
203
+ } else {
204
+ // GET public product object
205
+ // eslint-disable-next-line no-await-in-loop
206
+ const product = (await api.get(`products/${item.product_id}`, {
207
+ isNoAuth: true,
208
+ })).data;
209
+
210
+ if (product) {
211
+ checkItem(product);
212
+ } else {
213
+ // remove cart item
214
+ removeItem();
215
+ }
216
+ }
217
+ }
218
+ return items;
219
+ };
@@ -0,0 +1,33 @@
1
+ import type { CustomerCheckout } from '../../types/index';
2
+ import api from '@cloudcommerce/api';
3
+ import { logger } from 'firebase-functions';
4
+
5
+ const findCustomerByEmail = async (email: string, accessToken: string) => {
6
+ try {
7
+ const { data } = await api.get(`customers?main_email=${email}`, {
8
+ accessToken,
9
+ });
10
+ if (data.result.length) {
11
+ return data.result[0]._id;
12
+ }
13
+ return null;
14
+ } catch (e) {
15
+ logger.error(e);
16
+ return null;
17
+ }
18
+ };
19
+
20
+ export default (accessToken: string, customer?: CustomerCheckout) => {
21
+ if (customer) {
22
+ if (!customer._id) {
23
+ // try to find customer by e-mail
24
+ // GET customer object
25
+ return customer.main_email ? findCustomerByEmail(customer.main_email, accessToken) : null;
26
+ // use first resulted customer ID
27
+ } if (customer._id && (typeof customer._id === 'string') && customer._id.length === 24) {
28
+ // customer ID already defined
29
+ return customer._id as string & { length: 24; };
30
+ }
31
+ }
32
+ return null;
33
+ };