cloudcommerce 0.1.0 → 0.1.2

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 (112) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/ecomplus-stores/monocard/.vscode/settings.json +5 -1
  3. package/ecomplus-stores/monocard/functions/core/package.json +1 -1
  4. package/ecomplus-stores/monocard/functions/events/package.json +2 -2
  5. package/ecomplus-stores/monocard/functions/modules/package.json +2 -2
  6. package/ecomplus-stores/monocard/functions/passport/package.json +2 -2
  7. package/ecomplus-stores/monocard/functions/ssr/.nvmrc +1 -0
  8. package/ecomplus-stores/monocard/functions/ssr/content/settings.json +13 -10
  9. package/ecomplus-stores/monocard/functions/ssr/package.json +18 -10
  10. package/ecomplus-stores/monocard/functions/ssr/src/components/LottiePhoneNFC.vue +1 -1
  11. package/ecomplus-stores/monocard/functions/ssr/src/pages/_vue.ts +3 -0
  12. package/ecomplus-stores/monocard/package.json +1 -1
  13. package/ecomplus-stores/tia-sonia/.firebaserc +5 -0
  14. package/ecomplus-stores/tia-sonia/.vscode/settings.json +5 -1
  15. package/ecomplus-stores/tia-sonia/functions/config.json +2 -2
  16. package/ecomplus-stores/tia-sonia/functions/core/package.json +1 -1
  17. package/ecomplus-stores/tia-sonia/functions/events/package.json +2 -2
  18. package/ecomplus-stores/tia-sonia/functions/modules/package.json +2 -2
  19. package/ecomplus-stores/tia-sonia/functions/passport/package.json +2 -2
  20. package/ecomplus-stores/tia-sonia/functions/ssr/.nvmrc +1 -0
  21. package/ecomplus-stores/tia-sonia/functions/ssr/content/settings.json +14 -11
  22. package/ecomplus-stores/tia-sonia/functions/ssr/package.json +15 -7
  23. package/ecomplus-stores/tia-sonia/functions/ssr/src/pages/_vue.ts +3 -0
  24. package/ecomplus-stores/tia-sonia/functions/ssr/src/scripts/InlineScripts.astro +6 -7
  25. package/ecomplus-stores/tia-sonia/package.json +1 -1
  26. package/package.json +1 -1
  27. package/packages/api/package.json +1 -1
  28. package/packages/apps/correios/package.json +1 -1
  29. package/packages/apps/custom-payment/package.json +1 -1
  30. package/packages/apps/custom-shipping/package.json +1 -1
  31. package/packages/apps/discounts/package.json +1 -1
  32. package/packages/apps/emails/package.json +1 -1
  33. package/packages/apps/frenet/package.json +1 -1
  34. package/packages/apps/galaxpay/package.json +1 -1
  35. package/packages/apps/infinitepay/package.json +1 -1
  36. package/packages/apps/jadlog/package.json +1 -1
  37. package/packages/apps/loyalty-points/CHANGELOG.md +1 -0
  38. package/packages/apps/loyalty-points/README.md +1 -0
  39. package/packages/apps/loyalty-points/events.js +1 -0
  40. package/packages/apps/loyalty-points/lib/functions-lib/get-program-id.d.ts +5 -0
  41. package/packages/apps/loyalty-points/lib/functions-lib/get-program-id.js +20 -0
  42. package/packages/apps/loyalty-points/lib/functions-lib/get-program-id.js.map +1 -0
  43. package/packages/apps/loyalty-points/lib/functions-lib/handle-loyalty-points-event.d.ts +3 -0
  44. package/packages/apps/loyalty-points/lib/functions-lib/handle-loyalty-points-event.js +172 -0
  45. package/packages/apps/loyalty-points/lib/functions-lib/handle-loyalty-points-event.js.map +1 -0
  46. package/packages/apps/loyalty-points/lib/index.d.ts +1 -0
  47. package/packages/apps/loyalty-points/lib/index.js +2 -0
  48. package/packages/apps/loyalty-points/lib/index.js.map +1 -0
  49. package/packages/apps/loyalty-points/lib/loyalty-create-transaction.d.ts +76 -0
  50. package/packages/apps/loyalty-points/lib/loyalty-create-transaction.js +210 -0
  51. package/packages/apps/loyalty-points/lib/loyalty-create-transaction.js.map +1 -0
  52. package/packages/apps/loyalty-points/lib/loyalty-list-payments.d.ts +3 -0
  53. package/packages/apps/loyalty-points/lib/loyalty-list-payments.js +44 -0
  54. package/packages/apps/loyalty-points/lib/loyalty-list-payments.js.map +1 -0
  55. package/packages/apps/loyalty-points/lib/loyalty-points-events.d.ts +4 -0
  56. package/packages/apps/loyalty-points/lib/loyalty-points-events.js +28 -0
  57. package/packages/apps/loyalty-points/lib/loyalty-points-events.js.map +1 -0
  58. package/packages/apps/loyalty-points/lib/loyalty-points.d.ts +77 -0
  59. package/packages/apps/loyalty-points/lib/loyalty-points.js +12 -0
  60. package/packages/apps/loyalty-points/lib/loyalty-points.js.map +1 -0
  61. package/packages/apps/loyalty-points/package.json +32 -0
  62. package/packages/apps/loyalty-points/src/functions-lib/get-program-id.ts +23 -0
  63. package/packages/apps/loyalty-points/src/functions-lib/handle-loyalty-points-event.ts +207 -0
  64. package/packages/apps/loyalty-points/src/index.ts +1 -0
  65. package/packages/apps/loyalty-points/src/loyalty-create-transaction.ts +241 -0
  66. package/packages/apps/loyalty-points/src/loyalty-list-payments.ts +55 -0
  67. package/packages/apps/loyalty-points/src/loyalty-points-events.ts +41 -0
  68. package/packages/apps/loyalty-points/src/loyalty-points.ts +12 -0
  69. package/packages/apps/loyalty-points/tsconfig.json +6 -0
  70. package/packages/apps/mercadopago/package.json +1 -1
  71. package/packages/apps/pagarme/package.json +1 -1
  72. package/packages/apps/pix/package.json +1 -1
  73. package/packages/apps/tiny-erp/package.json +1 -1
  74. package/packages/cli/package.json +1 -1
  75. package/packages/config/package.json +1 -1
  76. package/packages/emails/package.json +1 -1
  77. package/packages/events/lib/firebase.js +2 -0
  78. package/packages/events/lib/firebase.js.map +1 -1
  79. package/packages/events/package.json +2 -1
  80. package/packages/events/src/firebase.ts +2 -0
  81. package/packages/firebase/lib/config.d.ts +4 -0
  82. package/packages/firebase/lib/config.js +8 -0
  83. package/packages/firebase/lib/config.js.map +1 -1
  84. package/packages/firebase/package.json +1 -1
  85. package/packages/firebase/src/config.ts +9 -0
  86. package/packages/i18n/package.json +1 -1
  87. package/packages/modules/lib/firebase/call-app-module.js +12 -0
  88. package/packages/modules/lib/firebase/call-app-module.js.map +1 -1
  89. package/packages/modules/package.json +2 -1
  90. package/packages/modules/src/firebase/call-app-module.ts +12 -0
  91. package/packages/passport/package.json +1 -1
  92. package/packages/ssr/package.json +2 -1
  93. package/packages/storefront/dist/client/HeaderButtons.8acf8a3b.js +1 -0
  94. package/packages/storefront/dist/client/Prices.9099fe99.js +1 -0
  95. package/packages/storefront/dist/client/{ProductCard.f477b7a1.js → ProductCard.7b68cc35.js} +1 -1
  96. package/packages/storefront/dist/client/assets/{_...slug_.15766f41.css → _...slug_.f19a16ca.css} +1 -1
  97. package/packages/storefront/dist/client/assets/server.55b8d73d.css +1 -0
  98. package/packages/storefront/dist/client/chunks/{HeaderButtons.27ab2180.js → HeaderButtons.cbbb059d.js} +1 -1
  99. package/packages/storefront/dist/client/chunks/{LoginForm.36774236.js → LoginForm.ac2f1b1f.js} +1 -1
  100. package/packages/storefront/dist/client/chunks/{Prices.vue_vue_type_script_setup_true_lang.522e4ac4.js → Prices.vue_vue_type_script_setup_true_lang.c37148c1.js} +1 -1
  101. package/packages/storefront/dist/client/chunks/{_plugin-vue_export-helper.979dc9e3.js → _plugin-vue_export-helper.5ccc5d75.js} +1 -1
  102. package/packages/storefront/dist/client/chunks/{runtime-core.esm-bundler.6c012e5d.js → runtime-core.esm-bundler.d5646b29.js} +1 -1
  103. package/packages/storefront/dist/client/client.9783ef49.js +1 -0
  104. package/packages/storefront/dist/client/sw.js +1 -1
  105. package/packages/storefront/dist/server/entry.mjs +1544 -28
  106. package/packages/storefront/package.json +1 -1
  107. package/packages/storefront/src/lib/pages/_vue.ts +1 -1
  108. package/packages/types/package.json +1 -1
  109. package/packages/storefront/dist/client/HeaderButtons.f1c92fd8.js +0 -1
  110. package/packages/storefront/dist/client/Prices.795d082c.js +0 -1
  111. package/packages/storefront/dist/client/assets/server.4d9646d8.css +0 -1
  112. package/packages/storefront/dist/client/client.8b4e336b.js +0 -1
@@ -0,0 +1,12 @@
1
+ import '@cloudcommerce/firebase/lib/init';
2
+ import handleListPayments from './loyalty-list-payments.js';
3
+ import handleCreateTransaction from './loyalty-create-transaction.js';
4
+
5
+ export const listPayments = async (modBody) => {
6
+ return handleListPayments(modBody);
7
+ };
8
+
9
+ export const createTransaction = async (modBody) => {
10
+ return handleCreateTransaction(modBody);
11
+ };
12
+ // # sourceMappingURL=loyalty-points.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loyalty-points.js","sourceRoot":"","sources":["../src/loyalty-points.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAE1C,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AACzD,OAAO,uBAAuB,MAAM,8BAA8B,CAAC;AAEnE,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,OAAsB,EAAE,EAAE;IAC3D,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EAAE,OAAsB,EAAE,EAAE;IAChE,OAAO,uBAAuB,CAAC,OAAO,CAAC,CAAC;AAC1C,CAAC,CAAC"}
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@cloudcommerce/app-loyalty-points",
3
+ "type": "module",
4
+ "version": "0.1.2",
5
+ "description": "E-Com Plus Cloud Commerce app to handle simple loyalty points programs",
6
+ "main": "lib/loyalty-points.js",
7
+ "exports": {
8
+ ".": "./lib/loyalty-points.js",
9
+ "./events": "./lib/loyalty-points-events.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/ecomplus/cloud-commerce.git",
14
+ "directory": "packages/loyalty-points"
15
+ },
16
+ "author": "E-Com Club Softwares para E-commerce <ti@e-com.club>",
17
+ "license": "Apache 2.0 with Commons Clause",
18
+ "bugs": {
19
+ "url": "https://github.com/ecomplus/cloud-commerce/issues"
20
+ },
21
+ "homepage": "https://github.com/ecomplus/cloud-commerce/tree/main/packages/apps/loyalty-points#readme",
22
+ "scripts": {
23
+ "build": "sh ../../../scripts/build-lib.sh"
24
+ },
25
+ "dependencies": {
26
+ "@cloudcommerce/api": "workspace:*",
27
+ "@cloudcommerce/firebase": "workspace:*"
28
+ },
29
+ "devDependencies": {
30
+ "@cloudcommerce/types": "workspace:*"
31
+ }
32
+ }
@@ -0,0 +1,23 @@
1
+ export default (
2
+ programRule: { program_id: string; name: string; },
3
+ index: number,
4
+ ) => {
5
+ let programId = `lpt__${index}`;
6
+ if (programRule.program_id) {
7
+ programId = programRule.program_id;
8
+ } else if (programRule.name && typeof programRule.name === 'string') {
9
+ programId = (`p0_${programRule.name.toLowerCase()}`)
10
+ .replace(/\n\s/g, '_')
11
+ .replace(/__/g, '_')
12
+ .replace(/áàãâ/g, 'a')
13
+ .replace(/éê/g, 'e')
14
+ .replace(/óõô/g, 'o')
15
+ .replace(/í/g, 'e')
16
+ .replace(/ú/g, 'u')
17
+ .replace(/ç/g, 'c')
18
+ .replace(/[^a-z0-9_]/g, '')
19
+ .substring(0, 30);
20
+ }
21
+
22
+ return programId;
23
+ };
@@ -0,0 +1,207 @@
1
+ import type { Orders, Customers } from '@cloudcommerce/types';
2
+ import api from '@cloudcommerce/api';
3
+ import { getFirestore } from 'firebase-admin/firestore';
4
+ import logger from 'firebase-functions/logger';
5
+ import getProgramId from './get-program-id';
6
+
7
+ const ECHO_SUCCESS = 'SUCCESS';
8
+ const ECHO_SKIP = 'SKIP';
9
+
10
+ type UsedPointsEntries = Exclude<Customers['loyalty_points_entries'], undefined>[number]
11
+ & { original_active_points: number }
12
+
13
+ const responsePubSub = (response: string) => {
14
+ logger.log('(App: Loyalty Points): ', response);
15
+ return null;
16
+ };
17
+
18
+ const haveEarnedPoints = (pointsList: Customers['loyalty_points_entries'], orderId: string) => {
19
+ if (pointsList) {
20
+ let hasEarned = false;
21
+ for (let i = 0; i < pointsList.length; i++) {
22
+ const point = pointsList[i];
23
+ if (point.order_id === orderId) {
24
+ hasEarned = true;
25
+ break;
26
+ }
27
+ }
28
+ return hasEarned;
29
+ }
30
+ return false;
31
+ };
32
+
33
+ const findPointIndex = (pointsList: Customers['loyalty_points_entries'], pointEntry: UsedPointsEntries) => {
34
+ if (pointsList) {
35
+ for (let i = 0; i < pointsList.length; i++) {
36
+ const point = pointsList[i];
37
+ if (point.name === pointEntry.name) {
38
+ return i;
39
+ }
40
+ }
41
+ }
42
+ return null;
43
+ };
44
+
45
+ const handleLoyaltyPointsEvent = async (
46
+ order: Orders,
47
+ programRules: any[],
48
+ ) => {
49
+ const orderId = order._id;
50
+ const currentStatus = order.financial_status && order.financial_status.current;
51
+ let isPaid: boolean = false;
52
+ let isCancelled: boolean = false;
53
+
54
+ switch (currentStatus) {
55
+ case 'paid':
56
+ isPaid = true;
57
+ break;
58
+ case 'unauthorized':
59
+ case 'partially_refunded':
60
+ case 'refunded':
61
+ case 'voided':
62
+ isCancelled = true;
63
+ break;
64
+ case 'partially_paid':
65
+ if (order.transactions) {
66
+ order.transactions.forEach((transaction) => {
67
+ if (transaction.payment_method.code !== 'loyalty_points' && transaction.status) {
68
+ switch (transaction.status.current) {
69
+ case 'unauthorized':
70
+ case 'refunded':
71
+ case 'voided':
72
+ isCancelled = true;
73
+ break;
74
+ default:
75
+ break;
76
+ }
77
+ }
78
+ });
79
+ }
80
+ break;
81
+ default:
82
+ break;
83
+ }
84
+
85
+ try {
86
+ if (isPaid || isCancelled) {
87
+ // get app configured options
88
+ const { amount, buyers } = order;
89
+ const customerId = buyers && buyers[0] && buyers[0]._id;
90
+ if (customerId) {
91
+ const pointsList: Customers['loyalty_points_entries'] = (await api.get(
92
+ `customers/${customerId}/loyalty_points_entries`,
93
+ )
94
+ ).data;
95
+
96
+ if (pointsList) {
97
+ const hasEarnedPoints = haveEarnedPoints(pointsList, orderId);
98
+
99
+ if (isPaid && !hasEarnedPoints) {
100
+ for (let i = 0; i < programRules.length; i++) {
101
+ const rule = programRules[i];
102
+ if (amount.subtotal) {
103
+ if (!rule.min_subtotal_to_earn || rule.min_subtotal_to_earn <= amount.subtotal) {
104
+ const pointsValue = ((rule.earn_percentage || 1) / 100)
105
+ * (amount.subtotal - (amount.discount || 0));
106
+
107
+ let validThru: string | undefined;
108
+ if (rule.expiration_days > 0) {
109
+ const d = new Date();
110
+ d.setDate(d.getDate() + rule.expiration_days);
111
+ validThru = d.toISOString();
112
+ }
113
+
114
+ const data = {
115
+ name: rule.name,
116
+ program_id: getProgramId(rule, i),
117
+ earned_points: pointsValue,
118
+ active_points: pointsValue,
119
+ ratio: rule.ratio || 1,
120
+ valid_thru: validThru,
121
+ order_id: orderId,
122
+ } as any; // TODO: set the correct type
123
+
124
+ // eslint-disable-next-line no-await-in-loop
125
+ await api.post(`customers/${customerId}/loyalty_points_entries`, data);
126
+
127
+ return responsePubSub(ECHO_SUCCESS);
128
+ }
129
+ }
130
+ }
131
+ }
132
+
133
+ if (isCancelled && hasEarnedPoints) {
134
+ for (let i = 0; i < pointsList.length; i++) {
135
+ if (pointsList[i].order_id === orderId) {
136
+ // eslint-disable-next-line no-await-in-loop
137
+ await api.delete(`customers/${customerId}/loyalty_points_entries/${i}`);
138
+ }
139
+ }
140
+
141
+ return responsePubSub(ECHO_SUCCESS);
142
+ }
143
+ }
144
+
145
+ if (isCancelled) {
146
+ const documentRef = getFirestore().doc(`billedPoints/${orderId}`);
147
+ const documentSnapshot = await documentRef.get();
148
+
149
+ if (documentSnapshot.exists) {
150
+ const usedPointsEntries = documentSnapshot.get('usedPointsEntries') as UsedPointsEntries[];
151
+ documentRef.delete();
152
+
153
+ if (Array.isArray(usedPointsEntries)) {
154
+ for (let i = 0; i < usedPointsEntries.length; i++) {
155
+ const pointsEntry = usedPointsEntries[i];
156
+ const pointsToRefund = pointsEntry.original_active_points
157
+ - pointsEntry.active_points;
158
+ if (pointsToRefund > 0) {
159
+ const pointIndex = findPointIndex(pointsList, pointsEntry);
160
+
161
+ if (pointIndex && pointsList) {
162
+ const activePoints = pointsList[pointIndex].active_points;
163
+ const body = {
164
+ active_points: activePoints + pointsToRefund,
165
+ };
166
+
167
+ // eslint-disable-next-line no-await-in-loop
168
+ await api.patch(
169
+ `customers/${customerId}/loyalty_points_entries/${pointIndex}`,
170
+ body,
171
+ );
172
+
173
+ return responsePubSub(ECHO_SUCCESS);
174
+ }
175
+ }
176
+ }
177
+
178
+ const transaction = order.transactions
179
+ && order.transactions.find((transactionFound) => {
180
+ return transactionFound.payment_method.code === 'loyalty_points';
181
+ });
182
+
183
+ if (transaction) {
184
+ const bodyPaymentHistory = {
185
+ transaction_id: transaction._id,
186
+ date_time: new Date().toISOString(),
187
+ status: currentStatus?.startsWith('partially') ? 'refunded' : currentStatus,
188
+ customer_notified: true,
189
+ } as any; // TODO: incompatible type=> amount and status
190
+
191
+ await api.post(`orders/${orderId}/payments_history`, bodyPaymentHistory);
192
+ }
193
+ }
194
+ }
195
+ }
196
+ }
197
+ return responsePubSub('');
198
+ }
199
+ // not paid nor cancelled
200
+ return responsePubSub(ECHO_SKIP);
201
+ } catch (err) {
202
+ logger.error('(App Loyalty Points) Error =>', err);
203
+ throw err;
204
+ }
205
+ };
206
+
207
+ export default handleLoyaltyPointsEvent;
@@ -0,0 +1 @@
1
+ export * from './loyalty-points';
@@ -0,0 +1,241 @@
1
+ import type {
2
+ AppModuleBody,
3
+ CreateTransactionParams,
4
+ CreateTransactionResponse,
5
+ Customers,
6
+ } from '@cloudcommerce/types';
7
+ import api from '@cloudcommerce/api';
8
+ import { getFirestore } from 'firebase-admin/firestore';
9
+ import logger from 'firebase-functions/logger';
10
+ import getProgramId from './functions-lib/get-program-id';
11
+
12
+ type UsedPointsEntries = Exclude<Customers['loyalty_points_entries'], undefined>[number]
13
+ & { original_active_points: number }
14
+
15
+ const collectionRef = getFirestore().collection('billedPoints');
16
+
17
+ const updateTransactionStatus = (orderId: string) => {
18
+ setTimeout(async () => {
19
+ const order = (await api.get(`orders/${orderId}`)).data;
20
+
21
+ // appSdk.apiRequest(storeId, `/orders/${orderId}.json`).then(({ response }) => {
22
+ const { transactions } = order;
23
+ if (transactions) {
24
+ const transaction = transactions.find((transactionFound) => {
25
+ return transactionFound.payment_method.code === 'loyalty_points';
26
+ });
27
+ if (transaction) {
28
+ const bodyPaymentHistory = {
29
+ transaction_id: transaction._id,
30
+ date_time: new Date().toISOString(),
31
+ status: 'paid',
32
+ customer_notified: true,
33
+ } as any; // TODO: incompatible type=> amount and status;
34
+ await api.post(`orders/${orderId}/payments_history`, bodyPaymentHistory);
35
+ }
36
+ }
37
+ }, 500);
38
+ };
39
+
40
+ const handleUpdatedPoints = (
41
+ endpoint: any, // TODO: endpoint type not string compatible
42
+ body: { [x: string]: any },
43
+ orderId: string,
44
+ usedPointsEntries?: UsedPointsEntries[],
45
+ timeout = 400,
46
+ isLastRequest = false,
47
+ ) => {
48
+ setTimeout(async () => {
49
+ await api.patch(endpoint, body);
50
+ // appSdk.apiRequest(storeId, endpoint, 'PATCH', data).then(() => {
51
+ logger.log(`#(App Loyalty Points): ${orderId} ${endpoint} => ${JSON.stringify(body)}`);
52
+ if (isLastRequest) {
53
+ updateTransactionStatus(orderId);
54
+ collectionRef.doc(orderId).set({ usedPointsEntries });
55
+ }
56
+ // });
57
+ }, timeout);
58
+ };
59
+
60
+ export default async (appData: AppModuleBody) => {
61
+ const { application, storeId } = appData;
62
+ const params = appData.params as CreateTransactionParams;
63
+ // app configured options
64
+ const appConfig = { ...application.data, ...application.hidden_data };
65
+ const { lang, amount } = params;
66
+
67
+ // setup required `transaction` response object
68
+ const transaction: CreateTransactionResponse['transaction'] = {
69
+ amount: amount.total,
70
+ };
71
+
72
+ if (params.payment_method.code === 'loyalty_points') {
73
+ const pointsApplied = params.loyalty_points_applied;
74
+ const programsRules = appConfig.programs_rules;
75
+ if (pointsApplied && Array.isArray(programsRules) && programsRules.length) {
76
+ // for (const programId in pointsApplied) {
77
+ Object.keys(pointsApplied).forEach((programId) => {
78
+ const pointsValue = pointsApplied[programId];
79
+ if (pointsValue > 0) {
80
+ const programRule = programsRules.find((programRuleFound, index) => {
81
+ if (programRuleFound) {
82
+ programRuleFound.program_id = getProgramId(programRuleFound, index);
83
+ return programId === programRuleFound.program_id;
84
+ }
85
+ return false;
86
+ });
87
+
88
+ if (programRule) {
89
+ const ratio = programRule.ratio || 1;
90
+ transaction.loyalty_points = {
91
+ name: programRule.name,
92
+ program_id: programRule.program_id,
93
+ ratio,
94
+ points_value: programRule.max_points < pointsValue
95
+ ? programRule.max_points
96
+ : pointsValue,
97
+ };
98
+ transaction.amount = pointsValue * ratio;
99
+ }
100
+ }
101
+ });
102
+ }
103
+ }
104
+
105
+ if (transaction.amount) {
106
+ const loyaltyPoints = transaction.loyalty_points;
107
+ const customerId = params.buyer.customer_id;
108
+ const orderId = params.order_id as string;
109
+ const usedPointsEntries: UsedPointsEntries[] = [];
110
+ try {
111
+ const customer = (await api.get(`customers/${customerId}`)).data;
112
+ // return appSdk.apiRequest(storeId, `/customers/${customerId}.json`)
113
+ // .then(({ response }) => {
114
+ const pointsEntries = customer.loyalty_points_entries;
115
+ let pointsToConsume = loyaltyPoints?.points_value;
116
+
117
+ if (pointsEntries && pointsToConsume) {
118
+ pointsEntries.sort((a, b) => {
119
+ let value = 0;
120
+ if (a.valid_thru && b.valid_thru) {
121
+ if (a.valid_thru < b.valid_thru) {
122
+ value = -1;
123
+ } else if (a.valid_thru > b.valid_thru) {
124
+ value = 1;
125
+ }
126
+ }
127
+ return value;
128
+ });
129
+
130
+ for (let i = 0; i < pointsEntries.length; i++) {
131
+ if (pointsToConsume <= 0) {
132
+ break;
133
+ }
134
+ const pointsEntry = pointsEntries[i];
135
+ if (pointsEntry.program_id === loyaltyPoints?.program_id) {
136
+ const validThru = pointsEntry.valid_thru;
137
+ const activePoints = pointsEntry.active_points;
138
+ if (activePoints > 0 && (!validThru || new Date(validThru).getTime() >= Date.now())) {
139
+ const pointsDiff = activePoints - pointsToConsume;
140
+ if (pointsDiff > 0 && pointsDiff < 0.01) {
141
+ pointsToConsume = activePoints;
142
+ }
143
+ if (pointsToConsume >= activePoints) {
144
+ pointsToConsume -= activePoints;
145
+ pointsEntry.active_points = 0;
146
+ if (pointsToConsume < 0.02) {
147
+ pointsToConsume = 0;
148
+ }
149
+ } else {
150
+ pointsEntry.active_points -= pointsToConsume;
151
+ pointsToConsume = 0;
152
+ }
153
+ usedPointsEntries.push({
154
+ ...pointsEntry,
155
+ original_active_points: activePoints,
156
+ });
157
+ }
158
+ }
159
+ }
160
+
161
+ if (pointsToConsume <= 0) {
162
+ if (usedPointsEntries.length <= 3) {
163
+ usedPointsEntries.forEach((pointsEntry, i) => {
164
+ /* Obs:
165
+ Store api v2 => loyalty_points_entries do not have id,
166
+ but change can be done by index
167
+ */
168
+ const endpoint = `customers/${customerId}/loyalty_points_entries/${i}`;
169
+ const body = {
170
+ active_points: pointsEntry.active_points,
171
+ };
172
+ handleUpdatedPoints(
173
+ endpoint,
174
+ body,
175
+ orderId,
176
+ usedPointsEntries,
177
+ (i + 1) * 400,
178
+ i + 1 === usedPointsEntries.length,
179
+ );
180
+ });
181
+ } else {
182
+ const endpoint = `customers/${customerId}`;
183
+ const body = {
184
+ loyalty_points_entries: pointsEntries,
185
+ };
186
+ handleUpdatedPoints(endpoint, body, orderId, usedPointsEntries, 400, true);
187
+ }
188
+ }
189
+ }
190
+
191
+ transaction.status = {
192
+ current: pointsToConsume && pointsToConsume <= 0 ? 'authorized' : 'unauthorized',
193
+ };
194
+
195
+ return {
196
+ status: 200,
197
+ redirect_to_payment: false,
198
+ transaction,
199
+ };
200
+ } catch (error: any) {
201
+ // try to debug request error
202
+ const errCode = 'POINTS_TRANSACTION_ERR';
203
+ let { message } = error;
204
+ const err = {
205
+ message: `CREATE_TRANSACTION_ERR #${storeId} - ${orderId} => ${message}`,
206
+ payment: '',
207
+ status: 0,
208
+ response: '',
209
+ usedPointsEntries,
210
+ };
211
+
212
+ if (error.response) {
213
+ const { status, data } = error.response;
214
+ err.status = status;
215
+ if (status !== 401 && status !== 403) {
216
+ if (typeof data === 'object' && data) {
217
+ err.response = JSON.stringify(data);
218
+ } else {
219
+ err.response = data;
220
+ }
221
+ }
222
+ if (data && data.user_message) {
223
+ message = lang ? data.user_message[lang] : data.user_message.en_us;
224
+ }
225
+ }
226
+
227
+ logger.error(err);
228
+
229
+ return {
230
+ error: errCode,
231
+ message,
232
+ };
233
+ }
234
+ }
235
+
236
+ return {
237
+ status: 200,
238
+ redirect_to_payment: false,
239
+ transaction,
240
+ };
241
+ };
@@ -0,0 +1,55 @@
1
+ import type {
2
+ AppModuleBody,
3
+ ListPaymentsResponse,
4
+ ListPaymentsParams,
5
+ } from '@cloudcommerce/types';
6
+ import getProgramId from './functions-lib/get-program-id';
7
+
8
+ export default (data: AppModuleBody) => {
9
+ const { application } = data;
10
+ const params = data.params as ListPaymentsParams;
11
+
12
+ const appData = {
13
+ ...application.data,
14
+ ...application.hidden_data,
15
+ };
16
+
17
+ // const { storeId } = req
18
+ // setup basic required response object
19
+ const response: ListPaymentsResponse = {
20
+ payment_gateways: [],
21
+ };
22
+
23
+ const label = params.lang === 'pt_br' || !params.lang
24
+ ? 'Pontos de fidelidade'
25
+ : 'Loyalty points';
26
+
27
+ response.payment_gateways.push({
28
+ type: 'payment',
29
+ payment_method: {
30
+ code: 'loyalty_points',
31
+ name: label,
32
+ },
33
+ label,
34
+ });
35
+
36
+ if (Array.isArray(appData.programs_rules)) {
37
+ const pointsPrograms = {};
38
+ appData.programs_rules.forEach((programRule, index) => {
39
+ const programId = getProgramId(programRule, index);
40
+ pointsPrograms[programId] = {
41
+ name: programRule.name,
42
+ ratio: programRule.ratio || 1,
43
+ max_points: programRule.max_points,
44
+ min_subtotal_to_earn: programRule.min_subtotal_to_earn,
45
+ earn_percentage: programRule.earn_percentage,
46
+ };
47
+ });
48
+
49
+ if (Object.keys(pointsPrograms).length) {
50
+ response.loyalty_points_programs = pointsPrograms;
51
+ }
52
+ }
53
+
54
+ return response;
55
+ };
@@ -0,0 +1,41 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+ import '@cloudcommerce/firebase/lib/init';
3
+ import type { Orders } from '@cloudcommerce/types';
4
+ import {
5
+ createAppEventsFunction,
6
+ ApiEventHandler,
7
+ } from '@cloudcommerce/firebase/lib/helpers/pubsub';
8
+ import logger from 'firebase-functions/logger';
9
+ import handleLoyaltyPointsEvent from './functions-lib/handle-loyalty-points-event';
10
+
11
+ const handleApiEvent: ApiEventHandler = async ({
12
+ evName,
13
+ apiEvent,
14
+ apiDoc,
15
+ app,
16
+ }) => {
17
+ const resourceId = apiEvent.resource_id;
18
+ logger.info('>> ', resourceId, ' - Action: ', apiEvent.action);
19
+ const key = `${evName}_${resourceId}`;
20
+ const appData = { ...app.data, ...app.hidden_data };
21
+ const programRules = appData.programs_rules;
22
+
23
+ if (
24
+ (Array.isArray(appData.ignore_events)
25
+ && appData.ignore_events.includes(evName))
26
+ || (!Array.isArray(programRules) || !programRules.length)
27
+ ) {
28
+ logger.info('>> ', key, ' - Ignored event');
29
+ return null;
30
+ }
31
+ logger.info(`> Webhook ${resourceId} [${evName}]`);
32
+
33
+ return handleLoyaltyPointsEvent(apiDoc as Orders, programRules);
34
+ };
35
+
36
+ export const loyaltypoints = {
37
+ onStoreEvent: createAppEventsFunction(
38
+ 'loyaltyPoints',
39
+ handleApiEvent,
40
+ ) as any,
41
+ };
@@ -0,0 +1,12 @@
1
+ import '@cloudcommerce/firebase/lib/init';
2
+ import type { AppModuleBody } from '@cloudcommerce/types';
3
+ import handleListPayments from './loyalty-list-payments';
4
+ import handleCreateTransaction from './loyalty-create-transaction';
5
+
6
+ export const listPayments = async (modBody: AppModuleBody) => {
7
+ return handleListPayments(modBody);
8
+ };
9
+
10
+ export const createTransaction = async (modBody: AppModuleBody) => {
11
+ return handleCreateTransaction(modBody);
12
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "declaration": true
5
+ }
6
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/app-mercadopago",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "description": "E-Com Plus Cloud Commerce app to integrate Mercado Pago",
6
6
  "main": "lib/mercadopago.js",
7
7
  "exports": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/app-pagarme",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "description": "E-Com Plus Cloud Commerce app to integrate Pagar.me",
6
6
  "main": "lib/pagarme.js",
7
7
  "exports": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/app-pix",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "description": "E-Com Plus Cloud Commerce app to integrate Pix API (Bacen)",
6
6
  "main": "lib/pix.js",
7
7
  "exports": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcommerce/app-tiny-erp",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "description": "E-Com Plus Cloud Commerce app for Tiny ERP",
6
6
  "main": "lib/tiny-erp.js",
7
7
  "repository": {