@cloudcommerce/app-galaxpay 0.1.0

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 (66) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +1 -0
  3. package/LICENSE.md +230 -0
  4. package/README.md +1 -0
  5. package/assets/onload-expression.js +23 -0
  6. package/events.js +1 -0
  7. package/lib/functions-lib/all-parses.d.ts +5 -0
  8. package/lib/functions-lib/all-parses.js +79 -0
  9. package/lib/functions-lib/all-parses.js.map +1 -0
  10. package/lib/functions-lib/ecom/events-to-galaxpay.d.ts +3 -0
  11. package/lib/functions-lib/ecom/events-to-galaxpay.js +75 -0
  12. package/lib/functions-lib/ecom/events-to-galaxpay.js.map +1 -0
  13. package/lib/functions-lib/galaxpay/auth/create-access.d.ts +11 -0
  14. package/lib/functions-lib/galaxpay/auth/create-access.js +60 -0
  15. package/lib/functions-lib/galaxpay/auth/create-access.js.map +1 -0
  16. package/lib/functions-lib/galaxpay/auth/create-axios.d.ts +2 -0
  17. package/lib/functions-lib/galaxpay/auth/create-axios.js +20 -0
  18. package/lib/functions-lib/galaxpay/auth/create-axios.js.map +1 -0
  19. package/lib/functions-lib/galaxpay/auth/gerate-token.d.ts +2 -0
  20. package/lib/functions-lib/galaxpay/auth/gerate-token.js +32 -0
  21. package/lib/functions-lib/galaxpay/auth/gerate-token.js.map +1 -0
  22. package/lib/functions-lib/galaxpay/handle-plans.d.ts +30 -0
  23. package/lib/functions-lib/galaxpay/handle-plans.js +73 -0
  24. package/lib/functions-lib/galaxpay/handle-plans.js.map +1 -0
  25. package/lib/functions-lib/galaxpay/update-subscription.d.ts +8 -0
  26. package/lib/functions-lib/galaxpay/update-subscription.js +56 -0
  27. package/lib/functions-lib/galaxpay/update-subscription.js.map +1 -0
  28. package/lib/functions-lib/galaxpay/webhook.d.ts +3 -0
  29. package/lib/functions-lib/galaxpay/webhook.js +291 -0
  30. package/lib/functions-lib/galaxpay/webhook.js.map +1 -0
  31. package/lib/functions-lib/utils.d.ts +8 -0
  32. package/lib/functions-lib/utils.js +17 -0
  33. package/lib/functions-lib/utils.js.map +1 -0
  34. package/lib/galaxpay-create-transaction.d.ts +71 -0
  35. package/lib/galaxpay-create-transaction.js +199 -0
  36. package/lib/galaxpay-create-transaction.js.map +1 -0
  37. package/lib/galaxpay-events.d.ts +6 -0
  38. package/lib/galaxpay-events.js +21 -0
  39. package/lib/galaxpay-events.js.map +1 -0
  40. package/lib/galaxpay-list-payments.d.ts +7 -0
  41. package/lib/galaxpay-list-payments.js +105 -0
  42. package/lib/galaxpay-list-payments.js.map +1 -0
  43. package/lib/galaxpay.d.ts +76 -0
  44. package/lib/galaxpay.js +12 -0
  45. package/lib/galaxpay.js.map +1 -0
  46. package/lib/index.d.ts +1 -0
  47. package/lib/index.js +3 -0
  48. package/lib/index.js.map +1 -0
  49. package/package.json +36 -0
  50. package/scripts/build.sh +5 -0
  51. package/src/functions-lib/all-parses.ts +91 -0
  52. package/src/functions-lib/ecom/events-to-galaxpay.ts +85 -0
  53. package/src/functions-lib/galaxpay/auth/create-access.ts +80 -0
  54. package/src/functions-lib/galaxpay/auth/create-axios.ts +21 -0
  55. package/src/functions-lib/galaxpay/auth/gerate-token.ts +36 -0
  56. package/src/functions-lib/galaxpay/handle-plans.ts +92 -0
  57. package/src/functions-lib/galaxpay/update-subscription.ts +73 -0
  58. package/src/functions-lib/galaxpay/webhook.ts +381 -0
  59. package/src/functions-lib/utils.ts +23 -0
  60. package/src/galaxpay-create-transaction.ts +240 -0
  61. package/src/galaxpay-events.ts +28 -0
  62. package/src/galaxpay-list-payments.ts +130 -0
  63. package/src/galaxpay.ts +12 -0
  64. package/src/index.ts +2 -0
  65. package/tsconfig.json +6 -0
  66. package/types/config-app.d.ts +99 -0
@@ -0,0 +1,381 @@
1
+ import type {
2
+ Orders,
3
+ Applications,
4
+ ResourceListResult,
5
+ } from '@cloudcommerce/types';
6
+ import type { Request, Response } from 'firebase-functions';
7
+ import api from '@cloudcommerce/api';
8
+ import { getFirestore } from 'firebase-admin/firestore';
9
+ import logger from 'firebase-functions/logger';
10
+ import { parseStatus, parsePeriodicityToEcom, gerateId } from '../all-parses';
11
+ import { updateValueSubscription, checkAmountItemsOrder } from './update-subscription';
12
+
13
+ type FinancialStatusCurrent = Exclude<Orders['financial_status'], undefined>['current']
14
+
15
+ const collectionSubscription = getFirestore().collection('subscriptions');
16
+
17
+ const getApp = async (): Promise<Applications> => {
18
+ return new Promise((resolve, reject) => {
19
+ api.get(
20
+ 'applications?app_id=123188&fields=hidden_data',
21
+ )
22
+ .then(({ data: result }) => {
23
+ resolve(result[0]);
24
+ })
25
+ .catch((err) => {
26
+ reject(err);
27
+ });
28
+ });
29
+ };
30
+
31
+ const checkStatus = (
32
+ financialStatus: Exclude<Orders['financial_status'], undefined>,
33
+ GalaxPayTransaction: { [x: string]: any },
34
+ ) => {
35
+ if (financialStatus.current === parseStatus(GalaxPayTransaction.status)) {
36
+ return true;
37
+ } if ((financialStatus.current === 'paid' || financialStatus.current === 'authorized')
38
+ && parseStatus(GalaxPayTransaction.status) !== 'refunded') {
39
+ return true;
40
+ }
41
+ return false;
42
+ };
43
+
44
+ const checkStatusNotValid = (status: string) => {
45
+ const parsedStatus = parseStatus(status);
46
+ // logger.log('>> Status (', status, ')=> ', parsedStatus);
47
+ if (parsedStatus === 'unauthorized' || parsedStatus === 'refunded' || parsedStatus === 'voided') {
48
+ return true;
49
+ }
50
+ return false;
51
+ };
52
+
53
+ const checkPayDay = (strDate: string) => {
54
+ // check if today is 3 days before payday.
55
+ const payDay = new Date(strDate);
56
+ const nowTime = new Date().getTime() + 259200000; // add 3day to today
57
+ const now = new Date(nowTime);
58
+ return (now >= payDay);
59
+ };
60
+
61
+ const findOrderByTransactionId = (transactionId: string): Promise<ResourceListResult<'orders'>> => {
62
+ return new Promise((resolve, reject) => {
63
+ api.get(`orders?transactions._id=${transactionId}`)
64
+ .then(({ data: response }) => {
65
+ const resp = response as unknown as ResourceListResult<'orders'>; // TODO:
66
+ resolve(resp);
67
+ })
68
+ .catch((err) => {
69
+ reject(err);
70
+ });
71
+ });
72
+ };
73
+
74
+ const findOrderById = async (orderId: string): Promise<Orders> => new Promise((resolve) => {
75
+ api.get(
76
+ `orders/${orderId}?fields=transactions,financial_status`,
77
+ ).then(({ data: order }) => {
78
+ resolve(order);
79
+ });
80
+ });
81
+
82
+ const createTransaction = async (
83
+ res: Response,
84
+ subscriptionLabel: string,
85
+ plan: { [x: string]: any },
86
+ orderNumber: string,
87
+ galaxpayFristTransactionId: string,
88
+ GalaxPayTransaction: { [x: string]: any },
89
+ GalaxPaySubscription: { [x: string]: any },
90
+ GalaxPayTransactionValue: number,
91
+ originalOrderId: string,
92
+ ) => {
93
+ if (galaxpayFristTransactionId !== GalaxPayTransaction.galaxPayId) {
94
+ // let body;
95
+ const originalOrder = (await api.get(`orders/${originalOrderId}`)).data;
96
+
97
+ // logger.log('> Create new Order ')
98
+ if (originalOrder.transactions && originalOrder.items) {
99
+ const { installment } = GalaxPayTransaction;
100
+ const {
101
+ buyers, items, domain, amount,
102
+ } = originalOrder;
103
+ const channelType = originalOrder.channel_type;
104
+ const shippingLines = originalOrder.shipping_lines;
105
+ const shippingMethodLabel = originalOrder.shipping_method_label;
106
+ const paymentMethodLabel = originalOrder.payment_method_label;
107
+ const originalTransaction = originalOrder.transactions[0];
108
+ const quantity = installment;
109
+ const periodicity = parsePeriodicityToEcom(GalaxPaySubscription.periodicity);
110
+ const dateUpdate = GalaxPayTransaction.datetimeLastSentToOperator
111
+ ? new Date(GalaxPayTransaction.datetimeLastSentToOperator).toISOString()
112
+ : new Date().toISOString();
113
+
114
+ // remove items free in new orders subscription
115
+ checkAmountItemsOrder(amount, items, plan);
116
+ if (amount.balance) {
117
+ delete amount.balance;
118
+ }
119
+
120
+ const transactionId = String(gerateId(GalaxPayTransaction.galaxPayId));
121
+
122
+ const transactions: Orders['transactions'] = [
123
+ {
124
+ amount: originalTransaction.amount,
125
+ status: {
126
+ updated_at: dateUpdate,
127
+ current: parseStatus(GalaxPayTransaction.status),
128
+ },
129
+ intermediator: {
130
+ transaction_id: GalaxPayTransaction.tid || '',
131
+ transaction_code: GalaxPayTransaction.authorizationCode || '',
132
+ },
133
+ payment_method: originalTransaction.payment_method,
134
+ app: originalTransaction.app,
135
+ _id: transactionId,
136
+ notes: `Parcela #${quantity} referente à ${subscriptionLabel} ${periodicity}`,
137
+ custom_fields: originalTransaction.custom_fields,
138
+ },
139
+ ];
140
+
141
+ transactions[0].payment_link = GalaxPaySubscription.paymentLink;
142
+
143
+ const financialStatus = {
144
+ updated_at: dateUpdate,
145
+ current: parseStatus(GalaxPayTransaction.status) as FinancialStatusCurrent,
146
+ };
147
+ const body = {
148
+ opened_at: new Date().toISOString(),
149
+ items,
150
+ shipping_lines: shippingLines,
151
+ buyers,
152
+ channel_type: channelType,
153
+ domain,
154
+ amount,
155
+ shipping_method_label: shippingMethodLabel,
156
+ payment_method_label: paymentMethodLabel,
157
+ transactions,
158
+ financial_status: financialStatus,
159
+ subscription_order: {
160
+ _id: originalOrderId as string & { length: 24 },
161
+ number: parseInt(orderNumber, 10),
162
+ },
163
+ notes: `Pedido #${quantity} referente à ${subscriptionLabel} ${periodicity}`,
164
+ staff_notes: `Valor cobrado no GalaxPay R$${GalaxPayTransactionValue}`,
165
+ };
166
+
167
+ const { result } = await findOrderByTransactionId(transactionId);
168
+
169
+ if (!result.length) {
170
+ await api.post('orders', body);
171
+ // logger.log('> Created new order API')
172
+ return res.sendStatus(200);
173
+ }
174
+ // Order Exists
175
+ return res.sendStatus(200);
176
+ }
177
+ }
178
+ // logger.log('> Not Found Subscritpion or Transaction exists')
179
+ return res.sendStatus(404);
180
+ };
181
+
182
+ const handleWehook = async (req: Request, res: Response) => {
183
+ // https://docs.galaxpay.com.br/webhooks
184
+
185
+ // POST transaction.updateStatus update Transation status
186
+ // POST subscription.addTransaction add transation in subscription
187
+
188
+ const galaxpayHook = req.body;
189
+ const type = galaxpayHook.event;
190
+ const GalaxPaySubscription = galaxpayHook.Subscription;
191
+ const GalaxPaySubscriptionQuantity = GalaxPaySubscription.quantity;
192
+ const originalOrderId = GalaxPaySubscription.myId;
193
+ const GalaxPayTransaction = galaxpayHook.Transaction;
194
+ const GalaxPayTransactionValue = GalaxPayTransaction.value / 100;
195
+
196
+ logger.log(
197
+ `> (App GalaxPay) WebHook ${type}, Body ${JSON.stringify(galaxpayHook)}, quantity:
198
+ ${GalaxPaySubscriptionQuantity}, status: ${GalaxPayTransaction.status} <`,
199
+ );
200
+
201
+ // if (galaxpayHook.confirmHash) {
202
+ // logger.log('> ', galaxpayHook.confirmHash);
203
+ // }
204
+ try {
205
+ if (type === 'transaction.updateStatus') {
206
+ const documentSnapshot = await collectionSubscription.doc(originalOrderId).get();
207
+ if (documentSnapshot && documentSnapshot.exists) {
208
+ const docSubscription = documentSnapshot.data();
209
+ if (docSubscription) {
210
+ const {
211
+ galaxpayFristTransactionId,
212
+ plan,
213
+ orderNumber,
214
+ subscriptionLabel,
215
+ } = docSubscription.data();
216
+
217
+ if (galaxpayFristTransactionId === GalaxPayTransaction.galaxPayId) {
218
+ // update frist payment
219
+ const order = await findOrderById(originalOrderId);
220
+ // Update value Subscription in GalaxPay
221
+
222
+ // logger.log('plan-> ', JSON.stringify(plan));
223
+ // not update subscripton canceled
224
+ if (!checkStatusNotValid(GalaxPayTransaction.status) && order.items) {
225
+ const app = await getApp();
226
+ await updateValueSubscription(app, originalOrderId, order.amount, order.items, plan);
227
+ }
228
+ // logger.log('ORDER: ', JSON.stringify(order.amount), ' **');
229
+ // logger.log('> order ', order)
230
+
231
+ if (order.financial_status
232
+ && checkStatus(order.financial_status, GalaxPayTransaction)) {
233
+ return res.sendStatus(200);
234
+ } if (order.transactions) {
235
+ // update payment
236
+ const transactionId = order.transactions[0]._id;
237
+ const bodyPaymentHistory = {
238
+ date_time: new Date().toISOString(),
239
+ status: parseStatus(GalaxPayTransaction.status),
240
+ transaction_id: transactionId,
241
+ notification_code: `${type};${galaxpayHook.webhookId}`,
242
+ flags: ['GalaxPay'],
243
+ } as any; // TODO: incompatible type=> amount and status;;
244
+
245
+ await api.post(`orders/${order._id}/payments_history`, bodyPaymentHistory);
246
+
247
+ await api.patch(
248
+ `orders/${order._id}/transactions/${transactionId}`,
249
+ {
250
+ intermediator: {
251
+ transaction_id: GalaxPayTransaction.tid || '',
252
+ transaction_code: GalaxPayTransaction.authorizationCode || '',
253
+ },
254
+ },
255
+ );
256
+
257
+ return res.sendStatus(200);
258
+ }
259
+ } else {
260
+ /*
261
+ add order, because recurrence creates all transactions in the
262
+ first transaction when quantity is non-zero,search for the order by ID,
263
+ if not found, create the transaction, and if found, check if it will be
264
+ necessary to update the transaction status
265
+ */
266
+ const transactionId = String(gerateId(GalaxPayTransaction.galaxPayId));
267
+
268
+ const { result } = await findOrderByTransactionId(transactionId);
269
+
270
+ if (!result || !result.length) {
271
+ // logger.log('> Not found Transaction in API')
272
+ if (!checkStatusNotValid(GalaxPayTransaction.status)
273
+ && checkPayDay(GalaxPayTransaction.payday)) {
274
+ // necessary to create order
275
+ return createTransaction(
276
+ res,
277
+ subscriptionLabel,
278
+ plan,
279
+ orderNumber,
280
+ galaxpayFristTransactionId,
281
+ GalaxPayTransaction,
282
+ GalaxPaySubscription,
283
+ GalaxPayTransactionValue,
284
+ originalOrderId,
285
+ );
286
+ }
287
+ // TODO: status 400 or 500?
288
+ return res.status(404).send('Status or checkPayDay invalid');
289
+ }
290
+ const order = result[0];
291
+ if (order.financial_status
292
+ && checkStatus(order.financial_status, GalaxPayTransaction)) {
293
+ // logger.log('> Equals Status')
294
+ return res.sendStatus(200);
295
+ }
296
+ // logger.log('> Order id ')
297
+ // update payment
298
+ const bodyPaymentHistory = {
299
+ date_time: new Date().toISOString(),
300
+ status: parseStatus(GalaxPayTransaction.status),
301
+ transaction_id: transactionId,
302
+ notification_code: `${type};${galaxpayHook.webhookId}`,
303
+ flags: ['GalaxPay'],
304
+ } as any; // TODO: incompatible type=> amount and status;
305
+
306
+ await api.post(`orders/${order._id}/payments_history`, bodyPaymentHistory);
307
+
308
+ // logger.log('> create Payment History')
309
+
310
+ await api.patch(
311
+ `orders/${order._id}/transactions/${transactionId}`,
312
+ {
313
+ intermediator: {
314
+ transaction_id: GalaxPayTransaction.tid || '',
315
+ transaction_code: GalaxPayTransaction.authorizationCode || '',
316
+ },
317
+ },
318
+ );
319
+
320
+ if (parseStatus(GalaxPayTransaction.status) === 'voided'
321
+ || parseStatus(GalaxPayTransaction.status) === 'refunded') {
322
+ await api.patch(`orders/${order._id}`, { status: 'cancelled' });
323
+
324
+ // logger.log('> UPDATE ORDER OK')
325
+ return res.sendStatus(200);
326
+ }
327
+ // logger.log('> UPDATE Transaction OK')
328
+ return res.sendStatus(200);
329
+ }
330
+ }
331
+ }
332
+ //
333
+ return res.status(404).send('Document not found in firestore');
334
+ } if (type === 'subscription.addTransaction') {
335
+ if (GalaxPaySubscriptionQuantity === 0) {
336
+ // find transaction in firebase
337
+ const documentSnapshot = await collectionSubscription.doc(originalOrderId).get();
338
+ if (documentSnapshot && documentSnapshot.exists) {
339
+ const docSubscription = documentSnapshot.data();
340
+
341
+ if (docSubscription) {
342
+ const {
343
+ galaxpayFristTransactionId,
344
+ plan,
345
+ orderNumber,
346
+ subscriptionLabel,
347
+ } = docSubscription.data();
348
+
349
+ if (checkPayDay(GalaxPayTransaction.payday)) {
350
+ return createTransaction(
351
+ res,
352
+ subscriptionLabel,
353
+ plan,
354
+ orderNumber,
355
+ galaxpayFristTransactionId,
356
+ GalaxPayTransaction,
357
+ GalaxPaySubscription,
358
+ GalaxPayTransactionValue,
359
+ originalOrderId,
360
+ );
361
+ }
362
+ }
363
+ }
364
+ //
365
+ return res.status(404).send('Document not found in firestore');
366
+ }
367
+ // Avoid retries of this GalaxPay webhook
368
+ return res.status(200)
369
+ .send(`Subscription webhook with non-zero quantity.
370
+ The Order will be analyzed with the updateStatus webhook.`);
371
+ }
372
+ //
373
+ return res.status(404).send('Unidentified webhook type');
374
+ } catch (err: any) {
375
+ // verificar catch
376
+ logger.error(err);
377
+ return res.sendStatus(500);
378
+ }
379
+ };
380
+
381
+ export default handleWehook;
@@ -0,0 +1,23 @@
1
+ import * as fs from 'fs';
2
+ import url from 'url';
3
+ import { join as joinPath } from 'path';
4
+
5
+ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
6
+
7
+ const readFile = (path: string) => fs.readFileSync(joinPath(__dirname, path), 'utf8');
8
+
9
+ const responseError = (status: number | null, error: string, message: string) => {
10
+ return {
11
+ status: status || 409,
12
+ error,
13
+ message,
14
+ };
15
+ };
16
+
17
+ const isSandbox = false; // TODO: false
18
+
19
+ export {
20
+ readFile,
21
+ responseError,
22
+ isSandbox,
23
+ };
@@ -0,0 +1,240 @@
1
+ import type {
2
+ AppModuleBody,
3
+ CreateTransactionParams,
4
+ CreateTransactionResponse,
5
+ } from '@cloudcommerce/types';
6
+ import type { GalaxpayApp, GalaxPaySubscriptions } from '../types/config-app';
7
+ import logger from 'firebase-functions/logger';
8
+ import { getFirestore } from 'firebase-admin/firestore';
9
+ import Galaxpay from './functions-lib/galaxpay/auth/create-access';
10
+ import { responseError, isSandbox } from './functions-lib/utils';
11
+ import { findPlanToCreateTransction } from './functions-lib/galaxpay/handle-plans';
12
+ import { parseStatus, parsePeriodicityToGalaxPay } from './functions-lib/all-parses';
13
+
14
+ type To = Exclude<CreateTransactionParams['to'], undefined>
15
+
16
+ const firestoreColl = 'galaxpaySubscriptions';
17
+
18
+ const parseAddress = (to: To) => ({
19
+ zipCode: to.zip,
20
+ street: to.street,
21
+ number: String(to.number) || 's/n',
22
+ complementary: to.complement || undefined,
23
+ neighborhood: to.borough,
24
+ city: to.city,
25
+ state: to.province || to.province_code,
26
+ });
27
+
28
+ export default async (appData: AppModuleBody) => {
29
+ // treat module request body
30
+ const { application } = appData;
31
+ const params = appData.params as CreateTransactionParams;
32
+ // app configured options
33
+ const configApp = {
34
+ ...application.data,
35
+ ...application.hidden_data,
36
+ } as GalaxpayApp;
37
+
38
+ const orderId = params.order_id;
39
+ const orderNumber = params.order_number;
40
+ const {
41
+ amount,
42
+ // items,
43
+ buyer,
44
+ to,
45
+ type,
46
+ } = params;
47
+
48
+ logger.log('>(App:GalaxPay) Transaction #order:', orderId, ` ${isSandbox ? ' Sandbox' : ''} <`);
49
+
50
+ const transaction: CreateTransactionResponse['transaction'] = {
51
+ // type,
52
+ amount: amount.total,
53
+ };
54
+
55
+ // setup required `transaction` response object
56
+ const galaxpayAxios = new Galaxpay({
57
+ galaxpayId: configApp.galaxpay_id,
58
+ galaxpayHash: configApp.galaxpay_hash,
59
+ });
60
+
61
+ // indicates whether the buyer should be redirected to payment link right after checkout
62
+ let redirectToPayment = false;
63
+
64
+ switch (params.payment_method.code) {
65
+ case 'online_debit':
66
+ redirectToPayment = true;
67
+ break;
68
+ default:
69
+ break;
70
+ }
71
+
72
+ // https://docs.galaxpay.com.br/subscriptions/create-without-plan
73
+
74
+ const extraFields = [
75
+ {
76
+ tagName: 'order_number',
77
+ tagValue: `${orderNumber}`,
78
+ }];
79
+
80
+ const galaxpayCustomer = {
81
+ myId: buyer.customer_id,
82
+ name: buyer.fullname,
83
+ document: buyer.doc_number,
84
+ emails: [buyer.email],
85
+ phones: [parseInt(`${buyer.phone.number}`, 10)],
86
+ };
87
+
88
+ let methodConfigName = params.payment_method.code === 'credit_card' ? configApp.credit_card?.label
89
+ : 'Cartão de crédito';
90
+
91
+ methodConfigName = (params.payment_method.code === 'account_deposit'
92
+ ? (configApp.pix?.label || 'Pix') : (configApp.banking_billet?.label || 'Boleto bancário'));
93
+
94
+ // handle plan label to find plan by name (label)
95
+ let labelPaymentGateway = params.payment_method.name?.replace('- GalaxPay', '');
96
+ labelPaymentGateway = labelPaymentGateway?.replace(methodConfigName, '');
97
+
98
+ let plan = findPlanToCreateTransction(labelPaymentGateway, configApp);
99
+
100
+ if (!plan && configApp.plans) {
101
+ [plan] = configApp.plans;
102
+ }
103
+
104
+ const finalAmount = amount.total;
105
+ const fristPayment = new Date();
106
+
107
+ const quantity = plan?.quantity || 0;
108
+ const galaxpaySubscriptions: GalaxPaySubscriptions = {
109
+ myId: `${orderId}`, // requered
110
+ value: Math.floor(finalAmount * 100),
111
+ quantity, // recorrence quantity
112
+ periodicity: parsePeriodicityToGalaxPay(plan?.periodicity) || 'monthly',
113
+ Customer: galaxpayCustomer,
114
+ ExtraFields: extraFields,
115
+ mainPaymentMethodId: 'creditcard',
116
+ firstPayDayDate: fristPayment.toISOString().split('T')[0],
117
+ };
118
+
119
+ if (params.payment_method.code === 'credit_card') {
120
+ const card = {
121
+ hash: params.credit_card?.hash,
122
+ };
123
+
124
+ const PaymentMethodCreditCard = {
125
+ Card: card,
126
+ preAuthorize: false,
127
+ };
128
+
129
+ galaxpaySubscriptions.PaymentMethodCreditCard = PaymentMethodCreditCard;
130
+ } else if (params.payment_method.code === 'banking_billet') {
131
+ if (to) {
132
+ Object.assign(galaxpayCustomer, { Address: parseAddress(to) });
133
+ } else if (params.billing_address) {
134
+ Object.assign(galaxpayCustomer, { Address: parseAddress(params.billing_address) });
135
+ }
136
+
137
+ fristPayment.setDate(fristPayment.getDate() + (configApp.banking_billet?.add_days || 0));
138
+
139
+ galaxpaySubscriptions.mainPaymentMethodId = 'boleto';
140
+ galaxpaySubscriptions.Customer = galaxpayCustomer;
141
+ [galaxpaySubscriptions.firstPayDayDate] = fristPayment.toISOString().split('T');
142
+ } else if (params.payment_method.code === 'account_deposit') {
143
+ // other is PIX
144
+ if (to) {
145
+ Object.assign(galaxpayCustomer, { Address: parseAddress(to) });
146
+ } else if (params.billing_address) {
147
+ Object.assign(galaxpayCustomer, { Address: parseAddress(params.billing_address) });
148
+ }
149
+
150
+ const PaymentMethodPix = {
151
+ instructions: configApp.pix?.instructions || 'Pix',
152
+ };
153
+
154
+ fristPayment.setDate(fristPayment.getDate() + (configApp.pix?.add_days || 0));
155
+
156
+ galaxpaySubscriptions.mainPaymentMethodId = 'pix';
157
+ galaxpaySubscriptions.Customer = galaxpayCustomer;
158
+ [galaxpaySubscriptions.firstPayDayDate] = fristPayment.toISOString().split('T');
159
+ galaxpaySubscriptions.PaymentMethodPix = PaymentMethodPix;
160
+ }
161
+
162
+ logger.log('>>(App:GalaxPay): subscriptions ', JSON.stringify(galaxpaySubscriptions), ' <<');
163
+
164
+ try {
165
+ await galaxpayAxios.preparing;
166
+ } catch (err: any) {
167
+ logger.error('>(App: GalaxPay) Error =>', err);
168
+ return responseError(409, 'NO_GALAXPAY_AUTH', 'Error getting authentication');
169
+ }
170
+
171
+ const { axios } = galaxpayAxios;
172
+ if (axios) {
173
+ try {
174
+ if (type === 'recurrence') {
175
+ const { data: { Subscription } } = await axios.post('/subscriptions', galaxpaySubscriptions);
176
+
177
+ logger.log('>(App: GalaxPay) New Subscription ', Subscription, ' <');
178
+ transaction.payment_link = Subscription.paymentLink;
179
+ const transactionGalaxPay = Subscription.Transactions[0];
180
+
181
+ transaction.status = {
182
+ updated_at: Subscription.datetimeLastSentToOperator || new Date().toISOString(),
183
+ current: parseStatus(transactionGalaxPay.status),
184
+ };
185
+
186
+ transaction.intermediator = {
187
+ transaction_id: transactionGalaxPay.tid,
188
+ transaction_code: transactionGalaxPay.authorizationCode,
189
+ };
190
+
191
+ const documentRef = getFirestore().doc(`${firestoreColl}/${orderId}`);
192
+ if (documentRef) {
193
+ documentRef.set({
194
+ subscriptionLabel: plan?.label ? plan.label : 'Plano',
195
+ status: 'open',
196
+ orderNumber: params.order_number,
197
+ galaxpayFristTransactionId: transactionGalaxPay.galaxPayId,
198
+ quantity,
199
+ create_at: new Date().toISOString(),
200
+ plan,
201
+ })
202
+ .catch(logger.error);
203
+ }
204
+
205
+ return {
206
+ redirect_to_payment: redirectToPayment,
207
+ transaction,
208
+ };
209
+ }
210
+ return responseError(409, 'GALAXPAY_TYPE_ERR_', 'Invalid transaction type');
211
+ } catch (error: any) {
212
+ // logger.log(error.response);
213
+ // try to debug request error
214
+ let { message } = error;
215
+ const err = {
216
+ message: `GALAXPAY_TRANSACTION_ERR Order: #${orderId} => ${message}`,
217
+ payment: '',
218
+ status: 0,
219
+ response: '',
220
+ };
221
+ if (error.response) {
222
+ const { status, data } = error.response;
223
+ if (status !== 401 && status !== 403) {
224
+ err.payment = JSON.stringify(transaction);
225
+ err.status = status;
226
+ if (typeof data === 'object' && data) {
227
+ err.response = JSON.stringify(data);
228
+ } else {
229
+ err.response = data;
230
+ }
231
+ } else if (data && Array.isArray(data.errors) && data.errors[0] && data.errors[0].message) {
232
+ message = data.errors[0].message;
233
+ }
234
+ }
235
+ logger.error(err);
236
+ return responseError(409, 'GALAXPAY_TRANSACTION_ERR', message);
237
+ }
238
+ }
239
+ return responseError(409, 'GALAXPAY_REQUEST_ERR_', 'Unexpected error creating charge');
240
+ };
@@ -0,0 +1,28 @@
1
+ /* eslint-disable import/prefer-default-export */
2
+
3
+ import '@cloudcommerce/firebase/lib/init';
4
+ import * as functions from 'firebase-functions/v1';
5
+ import config from '@cloudcommerce/firebase/lib/config';
6
+ import {
7
+ createAppEventsFunction,
8
+ ApiEventHandler,
9
+ } from '@cloudcommerce/firebase/lib/helpers/pubsub';
10
+ import handleApiEvent from './functions-lib/ecom/events-to-galaxpay';
11
+ import handleGalaxpayWebhook from './functions-lib/galaxpay/webhook';
12
+
13
+ export const galaxpay = {
14
+ onStoreEvent: createAppEventsFunction(
15
+ 'galaxPay',
16
+ handleApiEvent as ApiEventHandler,
17
+ ),
18
+
19
+ webhook: functions
20
+ .region(config.get().httpsFunctionOptions.region)
21
+ .https.onRequest((req, res) => {
22
+ if (req.method !== 'POST') {
23
+ res.sendStatus(405);
24
+ } else {
25
+ handleGalaxpayWebhook(req, res);
26
+ }
27
+ }),
28
+ };