cloudcommerce 2.11.2 → 2.12.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.
- package/CHANGELOG.md +12 -0
- package/ecomplus-stores/barradoce/functions/many/package.json +3 -3
- package/ecomplus-stores/barradoce/functions/ssr/content/pages/home.json +50 -86
- package/ecomplus-stores/barradoce/functions/ssr/package.json +6 -6
- package/ecomplus-stores/barradoce/functions/ssr/public/img/uploads/banner-pistache.jpg +0 -0
- package/ecomplus-stores/barradoce/functions/ssr/src/components/Banner.vue +7 -8
- package/ecomplus-stores/barradoce/functions/with-apps/package.json +3 -3
- package/ecomplus-stores/barradoce/package.json +2 -2
- package/package.json +2 -2
- package/packages/__skeleton/package.json +2 -1
- package/packages/api/package.json +2 -2
- package/packages/apps/affiliate-program/package.json +2 -2
- package/packages/apps/correios/package.json +2 -2
- package/packages/apps/custom-payment/package.json +2 -2
- package/packages/apps/custom-shipping/package.json +2 -2
- package/packages/apps/datafrete/package.json +2 -2
- package/packages/apps/discounts/package.json +2 -2
- package/packages/apps/emails/package.json +2 -2
- package/packages/apps/fb-conversions/package.json +2 -2
- package/packages/apps/flash-courier/package.json +2 -2
- package/packages/apps/frenet/package.json +2 -2
- package/packages/apps/galaxpay/package.json +3 -2
- package/packages/apps/google-analytics/package.json +2 -2
- package/packages/apps/jadlog/package.json +2 -2
- package/packages/apps/loyalty-points/package.json +2 -2
- package/packages/apps/mandae/package.json +2 -2
- package/packages/apps/melhor-envio/package.json +2 -2
- package/packages/apps/mercadopago/package.json +3 -2
- package/packages/apps/pagaleve/CHANGELOG.md +1 -0
- package/packages/apps/pagaleve/README.md +1 -0
- package/packages/apps/pagaleve/lib/index.d.ts +1 -0
- package/packages/apps/pagaleve/lib/index.js +3 -0
- package/packages/apps/pagaleve/lib/index.js.map +1 -0
- package/packages/apps/pagaleve/lib/pagaleve-constructor.d.ts +7 -0
- package/packages/apps/pagaleve/lib/pagaleve-constructor.js +78 -0
- package/packages/apps/pagaleve/lib/pagaleve-constructor.js.map +1 -0
- package/packages/apps/pagaleve/lib/pagaleve-create-transaction.d.ts +7 -0
- package/packages/apps/pagaleve/lib/pagaleve-create-transaction.js +148 -0
- package/packages/apps/pagaleve/lib/pagaleve-create-transaction.js.map +1 -0
- package/packages/apps/pagaleve/lib/pagaleve-list-payments.d.ts +7 -0
- package/packages/apps/pagaleve/lib/pagaleve-list-payments.js +113 -0
- package/packages/apps/pagaleve/lib/pagaleve-list-payments.js.map +1 -0
- package/packages/apps/pagaleve/lib/pagaleve-webhook.d.ts +4 -0
- package/packages/apps/pagaleve/lib/pagaleve-webhook.js +135 -0
- package/packages/apps/pagaleve/lib/pagaleve-webhook.js.map +1 -0
- package/packages/apps/pagaleve/lib/pagaleve.d.ts +12 -0
- package/packages/apps/pagaleve/lib/pagaleve.js +12 -0
- package/packages/apps/pagaleve/lib/pagaleve.js.map +1 -0
- package/packages/apps/pagaleve/package.json +43 -0
- package/packages/apps/pagaleve/src/index.ts +2 -0
- package/packages/apps/pagaleve/src/pagaleve-constructor.ts +85 -0
- package/packages/apps/pagaleve/src/pagaleve-create-transaction.ts +161 -0
- package/packages/apps/pagaleve/src/pagaleve-list-payments.ts +122 -0
- package/packages/apps/pagaleve/src/pagaleve-webhook.ts +150 -0
- package/packages/apps/pagaleve/src/pagaleve.ts +12 -0
- package/packages/apps/pagaleve/tsconfig.json +6 -0
- package/packages/apps/pagaleve/webhook.js +1 -0
- package/packages/apps/pagarme/package.json +3 -2
- package/packages/apps/pagarme-v5/package.json +3 -2
- package/packages/apps/paghiper/package.json +2 -2
- package/packages/apps/pix/package.json +2 -2
- package/packages/apps/tiny-erp/package.json +2 -2
- package/packages/apps/webhooks/package.json +2 -2
- package/packages/cli/package.json +2 -2
- package/packages/config/package.json +2 -2
- package/packages/emails/package.json +2 -2
- package/packages/eslint/package.json +2 -2
- package/packages/events/lib/firebase.js +2 -0
- package/packages/events/lib/firebase.js.map +1 -1
- package/packages/events/package.json +3 -2
- package/packages/events/src/firebase.ts +2 -0
- package/packages/feeds/package.json +2 -2
- package/packages/firebase/lib/config.d.ts +3 -0
- package/packages/firebase/lib/config.js +3 -0
- package/packages/firebase/lib/config.js.map +1 -1
- package/packages/firebase/package.json +2 -2
- package/packages/firebase/src/config.ts +3 -0
- package/packages/i18n/package.json +2 -2
- package/packages/modules/lib/firebase/call-app-module.js +12 -0
- package/packages/modules/lib/firebase/call-app-module.js.map +1 -1
- package/packages/modules/package.json +3 -2
- package/packages/modules/src/firebase/call-app-module.ts +12 -0
- package/packages/passport/lib/firebase.js +20 -22
- package/packages/passport/lib/firebase.js.map +1 -1
- package/packages/passport/package.json +2 -2
- package/packages/passport/src/firebase.ts +25 -25
- package/packages/ssr/package.json +2 -2
- package/packages/storefront/package.json +2 -2
- package/packages/test-base/package.json +2 -2
- package/packages/types/package.json +2 -2
- package/ecomplus-stores/barradoce/functions/ssr/public/img/uploads/banner-forma-decora.jpg +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { getFirestore, Timestamp } from 'firebase-admin/firestore';
|
|
4
|
+
import logger from 'firebase-functions/logger';
|
|
5
|
+
|
|
6
|
+
const createAxios = (token?: string | null, isSandbox = false) => {
|
|
7
|
+
const headers = {
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
'Idempotency-Key': `${Date.now() + Math.random()}`,
|
|
10
|
+
Authorization: token,
|
|
11
|
+
};
|
|
12
|
+
const baseURL = isSandbox
|
|
13
|
+
? 'https://sandbox-api.pagaleve.io'
|
|
14
|
+
: 'https://api.pagaleve.com.br';
|
|
15
|
+
return axios.create({
|
|
16
|
+
baseURL,
|
|
17
|
+
headers,
|
|
18
|
+
validateStatus(status) {
|
|
19
|
+
return status >= 200 && status <= 301;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const createToken = (username: string, password: string, isSandbox = false) => {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
// https://api-docs.addi-staging-br.com/#/Authentication/createAuthToken
|
|
27
|
+
const pagaleveAxios = createAxios(null, isSandbox);
|
|
28
|
+
const request = (isRetry = false) => {
|
|
29
|
+
pagaleveAxios.post('/v1/authentication', { username, password })
|
|
30
|
+
.then(({ data }) => resolve(data))
|
|
31
|
+
.catch((err) => {
|
|
32
|
+
if (!isRetry && err.response && err.response.status >= 429) {
|
|
33
|
+
setTimeout(() => request(true), 7000);
|
|
34
|
+
}
|
|
35
|
+
reject(err);
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
request();
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const Pagaleve = function newPagaleve(
|
|
43
|
+
this: { preparing: Promise<void>, axios: AxiosInstance },
|
|
44
|
+
username: string,
|
|
45
|
+
password: string,
|
|
46
|
+
isSandbox = false,
|
|
47
|
+
) {
|
|
48
|
+
const self = this;
|
|
49
|
+
const documentRef = getFirestore().doc('pagaleve/token');
|
|
50
|
+
this.preparing = new Promise((resolve, reject) => {
|
|
51
|
+
const authenticate = (token: string) => {
|
|
52
|
+
self.axios = createAxios(token, isSandbox);
|
|
53
|
+
resolve();
|
|
54
|
+
};
|
|
55
|
+
const handleAuth = () => {
|
|
56
|
+
createToken(username, password, isSandbox)
|
|
57
|
+
.then((data: any) => {
|
|
58
|
+
authenticate(data.token);
|
|
59
|
+
if (documentRef) {
|
|
60
|
+
documentRef.set({
|
|
61
|
+
...data,
|
|
62
|
+
isSandbox,
|
|
63
|
+
updatedAt: Timestamp.now(),
|
|
64
|
+
}).catch(logger.error);
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
.catch(reject);
|
|
68
|
+
};
|
|
69
|
+
documentRef.get()
|
|
70
|
+
.then((documentSnapshot) => {
|
|
71
|
+
const data = documentSnapshot.data();
|
|
72
|
+
if (data) {
|
|
73
|
+
const updatedAt = (data.updatedAt as Timestamp).toMillis();
|
|
74
|
+
if (updatedAt && Date.now() - updatedAt <= 50 * 60 * 1000) {
|
|
75
|
+
authenticate(data!.token);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
handleAuth();
|
|
80
|
+
})
|
|
81
|
+
.catch(logger.error);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default Pagaleve;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AppModuleBody,
|
|
3
|
+
CreateTransactionResponse,
|
|
4
|
+
} from '@cloudcommerce/types';
|
|
5
|
+
import { createHmac } from 'node:crypto';
|
|
6
|
+
import logger from 'firebase-functions/logger';
|
|
7
|
+
import { img as getImg } from '@ecomplus/utils';
|
|
8
|
+
import config from '@cloudcommerce/firebase/lib/config';
|
|
9
|
+
import Pagaleve from './pagaleve-constructor';
|
|
10
|
+
|
|
11
|
+
const { region } = config.get().httpsFunctionOptions;
|
|
12
|
+
const baseUri = `https://${region}-${process.env.GCLOUD_PROJECT}.cloudfunctions.net`;
|
|
13
|
+
|
|
14
|
+
const pagaleveCreateTransaction = async (body: AppModuleBody<'create_transaction'>) => {
|
|
15
|
+
const { params, application } = body;
|
|
16
|
+
const appData = { ...application.data, ...application.hidden_data };
|
|
17
|
+
const {
|
|
18
|
+
PAGALEVE_USERNAME,
|
|
19
|
+
PAGALEVE_PASSWORD,
|
|
20
|
+
PAGALEVE_SANDBOX,
|
|
21
|
+
} = process.env;
|
|
22
|
+
if (PAGALEVE_USERNAME) appData.username = PAGALEVE_USERNAME;
|
|
23
|
+
if (PAGALEVE_PASSWORD) appData.password = PAGALEVE_PASSWORD;
|
|
24
|
+
const isSandbox = !!PAGALEVE_SANDBOX;
|
|
25
|
+
const pagaleve = new Pagaleve(appData.username, appData.password, isSandbox);
|
|
26
|
+
|
|
27
|
+
const orderId = params.order_id || '';
|
|
28
|
+
const orderNumber = params.order_number;
|
|
29
|
+
const { amount, buyer, items } = params;
|
|
30
|
+
const isPix = params.payment_method.code === 'account_deposit';
|
|
31
|
+
const transaction: CreateTransactionResponse['transaction'] = {
|
|
32
|
+
intermediator: {
|
|
33
|
+
payment_method: params.payment_method,
|
|
34
|
+
},
|
|
35
|
+
currency_id: params.currency_id,
|
|
36
|
+
currency_symbol: params.currency_symbol,
|
|
37
|
+
amount: amount.total,
|
|
38
|
+
status: {
|
|
39
|
+
current: 'pending',
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const finalAmount = Math.floor(amount.total * 100);
|
|
43
|
+
const finalFreight = amount.freight ? Math.floor(amount.freight * 100) : 0;
|
|
44
|
+
|
|
45
|
+
const parseAddress = (_to: Exclude<(typeof params)['to'], undefined>) => ({
|
|
46
|
+
name: _to.name,
|
|
47
|
+
city: _to.city,
|
|
48
|
+
state: _to.province_code,
|
|
49
|
+
street: _to.street,
|
|
50
|
+
zip_code: _to.zip,
|
|
51
|
+
neighborhood: _to.borough,
|
|
52
|
+
number: String(_to.number) || 's/n',
|
|
53
|
+
complement: _to.complement || undefined,
|
|
54
|
+
});
|
|
55
|
+
const shippingAddr = params.to && parseAddress(params.to);
|
|
56
|
+
const billingAddr = params.billing_address
|
|
57
|
+
? parseAddress(params.billing_address)
|
|
58
|
+
: shippingAddr;
|
|
59
|
+
|
|
60
|
+
const hash = createHmac('sha256', appData.password)
|
|
61
|
+
.update(orderId).digest('hex');
|
|
62
|
+
const pagaleveTransaction: Record<string, any> = {
|
|
63
|
+
cancel_url: `https://${params.domain}/app/#/order/${orderNumber}/${orderId}`,
|
|
64
|
+
approve_url: `https://${params.domain}/app/#/order/${orderNumber}/${orderId}`,
|
|
65
|
+
webhook_url: `${baseUri}/pagaleve-webhook?order_id=${orderId}&hash=${hash}`,
|
|
66
|
+
is_pix_upfront: !!isPix,
|
|
67
|
+
order: {
|
|
68
|
+
reference: orderId,
|
|
69
|
+
description: `Order from e-com.plus ${orderNumber}`,
|
|
70
|
+
shipping: {
|
|
71
|
+
amount: finalFreight || 0,
|
|
72
|
+
address: shippingAddr,
|
|
73
|
+
},
|
|
74
|
+
amount: finalAmount,
|
|
75
|
+
items: [],
|
|
76
|
+
timestamp: new Date().toISOString(),
|
|
77
|
+
type: 'ORDINARY',
|
|
78
|
+
},
|
|
79
|
+
shopper: {
|
|
80
|
+
cpf: String(buyer.doc_number),
|
|
81
|
+
first_name: buyer.fullname.replace(/\s.*/, ''),
|
|
82
|
+
last_name: buyer.fullname.replace(/[^\s]+\s/, ''),
|
|
83
|
+
email: buyer.email,
|
|
84
|
+
phone: buyer.phone.number,
|
|
85
|
+
billing_address: billingAddr,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
let totalQuantity = 0;
|
|
89
|
+
items.forEach((item) => {
|
|
90
|
+
if (item.quantity > 0) {
|
|
91
|
+
totalQuantity += item.quantity;
|
|
92
|
+
const objImg = getImg(item);
|
|
93
|
+
pagaleveTransaction.order.items.push({
|
|
94
|
+
name: item.name || item.sku,
|
|
95
|
+
sku: item.sku,
|
|
96
|
+
quantity: item.quantity,
|
|
97
|
+
price: Math.floor((item.final_price || item.price) * 100),
|
|
98
|
+
url: `https://${params.domain}`,
|
|
99
|
+
reference: item.product_id,
|
|
100
|
+
image: objImg && objImg.url ? objImg.url : `https://${params.domain}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const birthDate = buyer.birth_date;
|
|
105
|
+
if (birthDate && birthDate.year && birthDate.month && birthDate.day) {
|
|
106
|
+
pagaleveTransaction.birth_date = `${birthDate.year}-`
|
|
107
|
+
+ `${birthDate.month.toString().padStart(2, '0')}-`
|
|
108
|
+
+ birthDate.day.toString().padStart(2, '0');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (totalQuantity > 0) {
|
|
112
|
+
try {
|
|
113
|
+
await pagaleve.preparing;
|
|
114
|
+
const { data } = pagaleve.axios.post('/v1/checkouts', pagaleveTransaction, {
|
|
115
|
+
maxRedirects: 0,
|
|
116
|
+
});
|
|
117
|
+
transaction.payment_link = data.redirect_url || data.checkout_url;
|
|
118
|
+
if (isPix && data.timestamp) {
|
|
119
|
+
transaction.account_deposit = {
|
|
120
|
+
valid_thru: data.timestamp,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
redirect_to_payment: true,
|
|
125
|
+
transaction,
|
|
126
|
+
} as CreateTransactionResponse;
|
|
127
|
+
} catch (error: any) {
|
|
128
|
+
const errCode = 'PAGALEVE_TRANSACTION_ERR';
|
|
129
|
+
let { message } = error;
|
|
130
|
+
const err: any = new Error(`${errCode} #${orderId} => ${message}`);
|
|
131
|
+
if (error.response) {
|
|
132
|
+
const { status, data } = error.response;
|
|
133
|
+
if (status !== 401 && status !== 403) {
|
|
134
|
+
err.transaction = transaction;
|
|
135
|
+
err.status = status;
|
|
136
|
+
if (typeof data === 'object' && data) {
|
|
137
|
+
err.response = JSON.stringify(data);
|
|
138
|
+
} else {
|
|
139
|
+
err.response = data;
|
|
140
|
+
}
|
|
141
|
+
} else if (Array.isArray(data?.errors) && data.errors[0]?.message) {
|
|
142
|
+
message = data.errors[0].message;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
logger.error(err);
|
|
146
|
+
return {
|
|
147
|
+
status: 409,
|
|
148
|
+
error: errCode,
|
|
149
|
+
message,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
status: 400,
|
|
156
|
+
error: 'PAGALEVE_TRANSACTION_INVALID',
|
|
157
|
+
message: 'Não há itens disponíveis no pedido',
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export default pagaleveCreateTransaction;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { AppModuleBody, ListPaymentsResponse } from '@cloudcommerce/types';
|
|
2
|
+
import logger from 'firebase-functions/logger';
|
|
3
|
+
|
|
4
|
+
const pagaleveListPayments = (body: AppModuleBody<'list_payments'>) => {
|
|
5
|
+
const { application } = body;
|
|
6
|
+
const params = body.params;
|
|
7
|
+
const response: ListPaymentsResponse = {
|
|
8
|
+
payment_gateways: [],
|
|
9
|
+
};
|
|
10
|
+
const appData = { ...application.data, ...application.hidden_data };
|
|
11
|
+
const {
|
|
12
|
+
PAGALEVE_USERNAME,
|
|
13
|
+
PAGALEVE_PASSWORD,
|
|
14
|
+
} = process.env;
|
|
15
|
+
if (PAGALEVE_USERNAME) appData.username = PAGALEVE_USERNAME;
|
|
16
|
+
if (PAGALEVE_PASSWORD) appData.password = PAGALEVE_PASSWORD;
|
|
17
|
+
|
|
18
|
+
if (!appData.username || !appData.password) {
|
|
19
|
+
logger.warn('Missign Pagaleve username/password');
|
|
20
|
+
return {
|
|
21
|
+
status: 409,
|
|
22
|
+
error: 'NO_PAGALEVE_KEYS',
|
|
23
|
+
message: 'Usuário e/ou senha não configurados pelo lojista',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const amount = params.amount || {} as Exclude<(typeof params)['amount'], undefined>;
|
|
28
|
+
const intermediator = {
|
|
29
|
+
name: 'Pagaleve',
|
|
30
|
+
link: 'https://api.pagaleve.com.br',
|
|
31
|
+
code: 'pagaleve',
|
|
32
|
+
};
|
|
33
|
+
const { discount } = appData;
|
|
34
|
+
if (discount && discount.value) {
|
|
35
|
+
if (discount.apply_at !== 'freight') {
|
|
36
|
+
const { value } = discount;
|
|
37
|
+
response.discount_option = {
|
|
38
|
+
label: 'Pix',
|
|
39
|
+
value,
|
|
40
|
+
};
|
|
41
|
+
['type', 'min_amount'].forEach((prop) => {
|
|
42
|
+
if (discount[prop]) {
|
|
43
|
+
response.discount_option![prop] = discount[prop];
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (amount.total) {
|
|
49
|
+
if (amount.total < discount.min_amount) {
|
|
50
|
+
discount.value = 0;
|
|
51
|
+
} else {
|
|
52
|
+
delete discount.min_amount;
|
|
53
|
+
const maxDiscount = amount[discount.apply_at || 'subtotal'];
|
|
54
|
+
let discountValue: number | undefined;
|
|
55
|
+
if (discount.type === 'percentage') {
|
|
56
|
+
discountValue = maxDiscount * (discount.value / 100);
|
|
57
|
+
} else {
|
|
58
|
+
discountValue = discount.value;
|
|
59
|
+
if (discountValue! > maxDiscount) {
|
|
60
|
+
discountValue = maxDiscount;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (discountValue) {
|
|
64
|
+
amount.discount = (amount.discount || 0) + discountValue;
|
|
65
|
+
amount.total -= discountValue;
|
|
66
|
+
if (amount.total < 0) {
|
|
67
|
+
amount.total = 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const listPaymentMethods = ['payment_link', 'account_deposit'] as const;
|
|
75
|
+
listPaymentMethods.forEach((paymentMethod) => {
|
|
76
|
+
const isLinkPayment = paymentMethod === 'payment_link';
|
|
77
|
+
const methodConfig = (appData[paymentMethod] || {});
|
|
78
|
+
const minAmount = Number(methodConfig.min_amount || 1);
|
|
79
|
+
let validateAmount = false;
|
|
80
|
+
if (amount.total && minAmount >= 0) {
|
|
81
|
+
validateAmount = amount.total >= minAmount;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Workaround for showcase
|
|
85
|
+
const validatePayment = amount.total ? validateAmount : true;
|
|
86
|
+
const methodEnable = !methodConfig.disable;
|
|
87
|
+
if (validatePayment && methodEnable) {
|
|
88
|
+
const label = methodConfig.label || (isLinkPayment ? 'Pix Parcelado' : 'Pagar com Pix');
|
|
89
|
+
const gateway: ListPaymentsResponse['payment_gateways'][0] = {
|
|
90
|
+
label,
|
|
91
|
+
icon: methodConfig.icon,
|
|
92
|
+
text: methodConfig.text,
|
|
93
|
+
payment_method: {
|
|
94
|
+
code: isLinkPayment ? 'balance_on_intermediary' : paymentMethod,
|
|
95
|
+
name: `${label} - ${intermediator.name} `,
|
|
96
|
+
},
|
|
97
|
+
intermediator,
|
|
98
|
+
};
|
|
99
|
+
if (!gateway.icon) {
|
|
100
|
+
if (isLinkPayment) {
|
|
101
|
+
gateway.icon = 'https://ecom-pagaleve.web.app/pagaleve-parcelado.png';
|
|
102
|
+
} else {
|
|
103
|
+
gateway.icon = 'https://ecom-pagaleve.web.app/pagaleve-pix.png';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if ((discount && discount.value && discount[paymentMethod] !== false)) {
|
|
107
|
+
gateway.discount = {};
|
|
108
|
+
['apply_at', 'type', 'value'].forEach((field) => {
|
|
109
|
+
gateway.discount![field] = discount[field];
|
|
110
|
+
});
|
|
111
|
+
if (response.discount_option && !response.discount_option.label) {
|
|
112
|
+
response.discount_option.label = label;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
response.payment_gateways.push(gateway);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return response;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export default pagaleveListPayments;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/* eslint-disable import/prefer-default-export */
|
|
2
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
3
|
+
import functions from 'firebase-functions/v1';
|
|
4
|
+
import logger from 'firebase-functions/logger';
|
|
5
|
+
import api from '@cloudcommerce/api';
|
|
6
|
+
import config from '@cloudcommerce/firebase/lib/config';
|
|
7
|
+
import Pagaleve from './pagaleve-constructor';
|
|
8
|
+
|
|
9
|
+
const { httpsFunctionOptions } = config.get();
|
|
10
|
+
|
|
11
|
+
const getAppHiddenData = async () => {
|
|
12
|
+
const { appId } = config.get().apps.pagaleve;
|
|
13
|
+
const { data } = await api.get(`applications?app_id=${appId}&fields=hidden_data`);
|
|
14
|
+
return data.result[0]?.hidden_data;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const parseStatusToEcom = (pagaleveTransactionStatus: string) => {
|
|
18
|
+
switch (pagaleveTransactionStatus.toLowerCase()) {
|
|
19
|
+
case 'pending':
|
|
20
|
+
case 'new':
|
|
21
|
+
case 'accepted':
|
|
22
|
+
return 'pending';
|
|
23
|
+
case 'authorized':
|
|
24
|
+
case 'completed':
|
|
25
|
+
return 'paid';
|
|
26
|
+
case 'expired':
|
|
27
|
+
case 'DECLINED':
|
|
28
|
+
case 'ABANDONED':
|
|
29
|
+
case 'canceled':
|
|
30
|
+
return 'voided';
|
|
31
|
+
default:
|
|
32
|
+
}
|
|
33
|
+
return 'unknown';
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const pagarme = {
|
|
37
|
+
webhook: functions
|
|
38
|
+
.region(httpsFunctionOptions.region)
|
|
39
|
+
.runWith(httpsFunctionOptions)
|
|
40
|
+
.https.onRequest(async (req, res) => {
|
|
41
|
+
const { body, query: { hash } } = req;
|
|
42
|
+
if (typeof hash !== 'string') {
|
|
43
|
+
res.sendStatus(403);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const {
|
|
47
|
+
id,
|
|
48
|
+
orderReference,
|
|
49
|
+
state,
|
|
50
|
+
amount,
|
|
51
|
+
} = (body || {}) as { [k: string]: any, orderReference?: string & { length: 24 } };
|
|
52
|
+
if (typeof orderReference !== 'string' || orderReference.length !== 24) {
|
|
53
|
+
res.sendStatus(400);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let appData: Record<string, any> | undefined;
|
|
57
|
+
try {
|
|
58
|
+
appData = await getAppHiddenData();
|
|
59
|
+
} catch (err) {
|
|
60
|
+
logger.error(err);
|
|
61
|
+
res.sendStatus(500);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!appData?.password) {
|
|
65
|
+
res.sendStatus(409);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const {
|
|
69
|
+
PAGALEVE_USERNAME,
|
|
70
|
+
PAGALEVE_PASSWORD,
|
|
71
|
+
PAGALEVE_SANDBOX,
|
|
72
|
+
} = process.env;
|
|
73
|
+
if (PAGALEVE_USERNAME) appData.username = PAGALEVE_USERNAME;
|
|
74
|
+
if (PAGALEVE_PASSWORD) appData.password = PAGALEVE_PASSWORD;
|
|
75
|
+
const isSandbox = !!PAGALEVE_SANDBOX;
|
|
76
|
+
|
|
77
|
+
const validHash = createHmac('sha256', appData.password)
|
|
78
|
+
.update(orderReference).digest('hex');
|
|
79
|
+
if (
|
|
80
|
+
validHash.length !== hash.length
|
|
81
|
+
|| !timingSafeEqual(Buffer.from(hash), Buffer.from(validHash))
|
|
82
|
+
) {
|
|
83
|
+
res.sendStatus(401);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const orderEndpoint = `orders/${orderReference}` as const;
|
|
88
|
+
try {
|
|
89
|
+
const { data: order } = await api.get(orderEndpoint);
|
|
90
|
+
if (order?.transactions) {
|
|
91
|
+
const transactionIndex = order.transactions.findIndex(({ app }) => {
|
|
92
|
+
return app?.intermediator?.code === 'pagaleve';
|
|
93
|
+
});
|
|
94
|
+
const transactionId = order.transactions[transactionIndex]?._id;
|
|
95
|
+
if (!transactionId) {
|
|
96
|
+
res.sendStatus(404);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await api.post(`${orderEndpoint}/payments_history`, {
|
|
101
|
+
date_time: new Date().toISOString(),
|
|
102
|
+
status: parseStatusToEcom(state),
|
|
103
|
+
transaction_id: transactionId,
|
|
104
|
+
flags: ['pagaleve'],
|
|
105
|
+
} as Exclude<(typeof order)['payments_history'], undefined>[0]);
|
|
106
|
+
|
|
107
|
+
await api.patch(`${orderEndpoint}/transactions/${transactionId}`, {
|
|
108
|
+
intermediator: {
|
|
109
|
+
transaction_id: id || '',
|
|
110
|
+
transaction_code: id || '',
|
|
111
|
+
},
|
|
112
|
+
} as Partial<Exclude<(typeof order)['transactions'], undefined>[0]>);
|
|
113
|
+
|
|
114
|
+
if (state.toLowerCase() === 'authorized') {
|
|
115
|
+
const pagaleve = new Pagaleve(appData.username, appData.password, isSandbox);
|
|
116
|
+
await pagaleve.preparing;
|
|
117
|
+
const pagalevePayment = {
|
|
118
|
+
checkout_id: id,
|
|
119
|
+
currency: 'BRL',
|
|
120
|
+
amount,
|
|
121
|
+
intent: 'CAPTURE',
|
|
122
|
+
};
|
|
123
|
+
await pagaleve.axios.post('/v1/payments', pagalevePayment, {
|
|
124
|
+
maxRedirects: 0,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch (error: any) {
|
|
129
|
+
const { response } = error;
|
|
130
|
+
let status: number | undefined;
|
|
131
|
+
if (response) {
|
|
132
|
+
status = response.status;
|
|
133
|
+
const err: any = new Error(`Webhook failed with ${status} for #${orderReference}`);
|
|
134
|
+
err.url = error.config?.url;
|
|
135
|
+
err.body = error.config?.body;
|
|
136
|
+
err.status = status;
|
|
137
|
+
err.response = JSON.stringify(response.data);
|
|
138
|
+
logger.error(err);
|
|
139
|
+
} else {
|
|
140
|
+
logger.error(error);
|
|
141
|
+
}
|
|
142
|
+
if (!res.headersSent) {
|
|
143
|
+
res.send({
|
|
144
|
+
status: status || 500,
|
|
145
|
+
msg: `Pagaleve webhook error for ${orderReference}`,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}),
|
|
150
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import '@cloudcommerce/firebase/lib/init';
|
|
2
|
+
import type { AppModuleBody } from '@cloudcommerce/types';
|
|
3
|
+
import handleListPayments from './pagaleve-list-payments';
|
|
4
|
+
import handleCreateTransaction from './pagaleve-create-transaction';
|
|
5
|
+
|
|
6
|
+
export const listPayments = async (modBody: AppModuleBody<'list_payments'>) => {
|
|
7
|
+
return handleListPayments(modBody);
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const createTransaction = async (modBody: AppModuleBody<'create_transaction'>) => {
|
|
11
|
+
return handleCreateTransaction(modBody);
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/pagaleve-webhook';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-pagarme",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app to integrate Pagar.me",
|
|
6
6
|
"main": "lib/pagarme.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/pagarme.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"/lib",
|
|
13
13
|
"/lib-mjs",
|
|
14
|
+
"/assets",
|
|
14
15
|
"/types",
|
|
15
16
|
"/*.{js,mjs,ts}"
|
|
16
17
|
],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-pagarme-v5",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app to integrate Pagar.me API v5 with recurring payments",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/index.js",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"/lib",
|
|
13
13
|
"/lib-mjs",
|
|
14
|
+
"/assets",
|
|
14
15
|
"/types",
|
|
15
16
|
"/*.{js,mjs,ts}"
|
|
16
17
|
],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-paghiper",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app to integrate PagHiper",
|
|
6
6
|
"main": "lib/paghiper.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/paghiper.js",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-pix",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app to integrate Pix API (Bacen)",
|
|
6
6
|
"main": "lib/pix.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/pix.js",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-tiny-erp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app for Tiny ERP",
|
|
6
6
|
"main": "lib/tiny-erp.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"/lib",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/app-webhooks",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce app for general order webhooks",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"/lib",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/cli",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce CLI tools",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cloudcommerce": "./bin/run.mjs"
|
|
8
8
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/config",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce base config",
|
|
6
6
|
"main": "lib/config.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./lib/config.js",
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/emails",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce email sender",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|
|
8
8
|
"files": [
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudcommerce/eslint",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "2.12.0",
|
|
5
|
+
"description": "e-com.plus Cloud Commerce ESLint config",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"/**/*.eslintrc.*"
|