@uappi/public-sdk 1.20.1 → 1.22.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/README.md +12 -0
- package/components/PaymentHosted/PaymentHosted.vue +15 -2
- package/components/PaymentHosted/types.ts +6 -1
- package/modules/Api/cookieName.ts +23 -0
- package/modules/Api/endpoints/OrderPaymentCodeComposable.ts +10 -0
- package/modules/Api/index.ts +12 -0
- package/modules/Api/types/context/api-config.types.ts +8 -2
- package/modules/Api/types/endpoints/cart/cart.interfaces.ts +1 -0
- package/modules/Api/types/endpoints/order/order-payment-code.interfaces.ts +13 -0
- package/modules/Api/types/endpoints/order/order-payment-code.types.ts +9 -0
- package/modules/Api/types/index.ts +8 -0
- package/modules/Api/usePublicApi.ts +2 -1
- package/modules/Tracking/facebook/events/actions/purchaseOrder.ts +18 -0
- package/modules/Tracking/gtm/events/actions/purchaseOrder.ts +49 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,3 +65,15 @@ git push origin v1.2.9
|
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
Não repita esse passo — é só histórico de como o processo começou.
|
|
68
|
+
|
|
69
|
+
## Nome do cookie de autenticação
|
|
70
|
+
|
|
71
|
+
O SDK envia o header `Uappi-Cookie-Name` em toda requisição, com o valor `token` por padrão.
|
|
72
|
+
|
|
73
|
+
Para usar outro cookie, acesse a loja com `?setCookieName=<nome>`. O SDK captura o valor, grava em `sessionStorage` sob a chave `uappi-cookie-name` e passa a enviá-lo no header a partir dali. Como `sessionStorage` é por aba, cada aba do navegador mantém sua própria sessão na mesma URL de projeto.
|
|
74
|
+
|
|
75
|
+
Nomes aceitos: `^[A-Za-z0-9_-]{1,64}$`. Valor fora desse formato é ignorado e o SDK segue com o anterior (ou com `token`).
|
|
76
|
+
|
|
77
|
+
Temas que compartilham a aba (iframe same-origin, ou navegação na mesma aba) leem o nome em uso por `Api.getCookieName()`, sem reimplementar a chave do storage.
|
|
78
|
+
|
|
79
|
+
A captura da URL acontece sozinha na primeira requisição. Se o tema limpa a query string no boot — router SPA com `replace`, por exemplo — chame `Api.captureCookieName()` antes disso.
|
|
@@ -3,7 +3,9 @@ import { ref, shallowRef, computed, onMounted, onUnmounted } from 'vue';
|
|
|
3
3
|
|
|
4
4
|
import api from '../../modules/Api';
|
|
5
5
|
import { purchaseAction } from '../../modules/Tracking/gtm/events/actions/purchase';
|
|
6
|
+
import { purchaseOrderAction } from '../../modules/Tracking/gtm/events/actions/purchaseOrder';
|
|
6
7
|
import { purchaseAction as facebookPurchaseAction } from '../../modules/Tracking/facebook/events/actions/purchase';
|
|
8
|
+
import { purchaseOrderAction as facebookPurchaseOrderAction } from '../../modules/Tracking/facebook/events/actions/purchaseOrder';
|
|
7
9
|
|
|
8
10
|
import type { Props, Emits, PaymentSuccessData, PaymentRedirectData } from './types';
|
|
9
11
|
import type { CartResponse } from '../../modules/Api/types';
|
|
@@ -51,7 +53,10 @@ const iframeSrc = computed(() => {
|
|
|
51
53
|
});
|
|
52
54
|
|
|
53
55
|
async function requestPaymentToken(): Promise<string> {
|
|
54
|
-
const response = await api.cart.paymentToken({
|
|
56
|
+
const response = await api.cart.paymentToken({
|
|
57
|
+
paymentOptionId: props.option.id,
|
|
58
|
+
...(props.orderGroupPaymentCode ? { orderGroupPaymentCode: props.orderGroupPaymentCode } : {}),
|
|
59
|
+
});
|
|
55
60
|
|
|
56
61
|
if (!response?.token) {
|
|
57
62
|
console.error('[PaymentHosted] Token ausente na resposta:', response);
|
|
@@ -70,6 +75,7 @@ async function fetchPaymentToken(): Promise<void> {
|
|
|
70
75
|
paymentToken.value = await requestPaymentToken();
|
|
71
76
|
} catch (err) {
|
|
72
77
|
console.error('[PaymentHosted] Erro ao buscar token de pagamento:', err);
|
|
78
|
+
emit('token-error', (err as { errorCode?: string })?.errorCode);
|
|
73
79
|
tokenError.value = 'Não foi possível iniciar o formulário de pagamento.';
|
|
74
80
|
} finally {
|
|
75
81
|
loadingToken.value = false;
|
|
@@ -96,6 +102,10 @@ async function refreshPaymentToken(): Promise<void> {
|
|
|
96
102
|
}
|
|
97
103
|
|
|
98
104
|
async function fetchCartSnapshot(): Promise<void> {
|
|
105
|
+
// No fluxo de link o carrinho ativo não é a compra: o CartBroker devolveria um
|
|
106
|
+
// carrinho vazio recém-criado, e o evento sairia com itens que não existem
|
|
107
|
+
if (props.orderGroupPaymentCode) return;
|
|
108
|
+
|
|
99
109
|
try {
|
|
100
110
|
cartSnapshot.value = await api.cart.listDetail();
|
|
101
111
|
} catch (err) {
|
|
@@ -131,7 +141,10 @@ function handleIframeMessage(event: MessageEvent): void {
|
|
|
131
141
|
break;
|
|
132
142
|
|
|
133
143
|
case 'UAPPI_PAYMENT_SUCCESS':
|
|
134
|
-
if (
|
|
144
|
+
if (props.orderGroup) {
|
|
145
|
+
purchaseOrderAction(props.option.paymentType.code, props.orderGroup);
|
|
146
|
+
facebookPurchaseOrderAction(props.orderGroup.id, props.orderGroup.values?.totalValue ?? 0);
|
|
147
|
+
} else if (cartSnapshot.value) {
|
|
135
148
|
purchaseAction(props.option.paymentType.code, cartSnapshot.value);
|
|
136
149
|
facebookPurchaseAction(data.id, cartSnapshot.value);
|
|
137
150
|
} else {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PaymentOption, BuyCartResponse } from '../../modules/Api/types';
|
|
1
|
+
import type { PaymentOption, BuyCartResponse, CustomerOrderResponse } from '../../modules/Api/types';
|
|
2
2
|
|
|
3
3
|
export interface Props {
|
|
4
4
|
/** Opção de pagamento selecionada (ex: item retornado por `api.cart.listPayments`). */
|
|
@@ -11,10 +11,15 @@ export interface Props {
|
|
|
11
11
|
countryCode?: string;
|
|
12
12
|
/** Host do serviço render.uappi. Sobrescreve `VITE_PAYMENT_IFRAME_HOST` quando informado. */
|
|
13
13
|
iframeHost?: string;
|
|
14
|
+
/** Código do link de pagamento. Informado, o token sai para o pedido do link. */
|
|
15
|
+
orderGroupPaymentCode?: string;
|
|
16
|
+
/** Grupo de pedido do link. Obrigatório junto de `orderGroupPaymentCode`. */
|
|
17
|
+
orderGroup?: CustomerOrderResponse;
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
export interface Emits {
|
|
17
21
|
(e: 'payment-ready'): void;
|
|
22
|
+
(e: 'token-error', errorCode: string | undefined): void;
|
|
18
23
|
(e: 'payment-success', data: PaymentSuccessData): void;
|
|
19
24
|
(e: 'payment-redirect', url: string, data: PaymentRedirectData): void;
|
|
20
25
|
(e: 'payment-error', errorCode: string | undefined, message: string): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const STORAGE_KEY = 'uappi-cookie-name';
|
|
2
|
+
const DEFAULT_COOKIE_NAME = 'token';
|
|
3
|
+
const VALID_COOKIE_NAME = /^[A-Za-z0-9_-]{1,64}$/;
|
|
4
|
+
|
|
5
|
+
export function captureCookieName() {
|
|
6
|
+
const param = new URLSearchParams(window.location.search).get(
|
|
7
|
+
'setCookieName',
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
if (!param || !VALID_COOKIE_NAME.test(param)) return;
|
|
11
|
+
|
|
12
|
+
sessionStorage.setItem(STORAGE_KEY, param);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function getCookieName() {
|
|
16
|
+
captureCookieName();
|
|
17
|
+
|
|
18
|
+
const stored = sessionStorage.getItem(STORAGE_KEY);
|
|
19
|
+
|
|
20
|
+
return stored && VALID_COOKIE_NAME.test(stored)
|
|
21
|
+
? stored
|
|
22
|
+
: DEFAULT_COOKIE_NAME;
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { usePublicApi } from '..';
|
|
2
|
+
|
|
3
|
+
import type { OrderPaymentCodeDetailResponse } from '../types';
|
|
4
|
+
|
|
5
|
+
export function detailOrderPaymentCode(code: string) {
|
|
6
|
+
return usePublicApi<OrderPaymentCodeDetailResponse>({
|
|
7
|
+
endpoint: `/publics/order/payment-code/${code}`,
|
|
8
|
+
method: 'GET',
|
|
9
|
+
});
|
|
10
|
+
}
|
package/modules/Api/index.ts
CHANGED
|
@@ -58,6 +58,7 @@ import { listMenu } from './endpoints/MenuComposable';
|
|
|
58
58
|
import { listPairOffers } from './endpoints/PairOfferComposable';
|
|
59
59
|
import { getPromotionalForecast } from './endpoints/PromotionalForecastComposable';
|
|
60
60
|
import { paymentConditions } from './endpoints/PaymentComposable';
|
|
61
|
+
import { detailOrderPaymentCode } from './endpoints/OrderPaymentCodeComposable';
|
|
61
62
|
import {
|
|
62
63
|
deleteProductComment,
|
|
63
64
|
createProductComment,
|
|
@@ -102,6 +103,7 @@ import { sendMfa, confirmMfa, loginMfa, deleteMFA, mfaSetup } from './endpoints/
|
|
|
102
103
|
|
|
103
104
|
|
|
104
105
|
import { usePublicApi } from './usePublicApi';
|
|
106
|
+
import { captureCookieName, getCookieName } from './cookieName';
|
|
105
107
|
|
|
106
108
|
const thirdParty = {
|
|
107
109
|
oauthRedirect,
|
|
@@ -216,16 +218,23 @@ const promotional = {
|
|
|
216
218
|
getPromotionalForecast,
|
|
217
219
|
};
|
|
218
220
|
|
|
221
|
+
const order = {
|
|
222
|
+
detailOrderPaymentCode
|
|
223
|
+
};
|
|
224
|
+
|
|
219
225
|
const notify = {
|
|
220
226
|
contact
|
|
221
227
|
}
|
|
222
228
|
|
|
223
229
|
export {
|
|
224
230
|
usePublicApi,
|
|
231
|
+
captureCookieName,
|
|
232
|
+
getCookieName,
|
|
225
233
|
common,
|
|
226
234
|
customer,
|
|
227
235
|
customization,
|
|
228
236
|
cart,
|
|
237
|
+
order,
|
|
229
238
|
product,
|
|
230
239
|
promotional,
|
|
231
240
|
notify,
|
|
@@ -233,11 +242,14 @@ export {
|
|
|
233
242
|
};
|
|
234
243
|
export default {
|
|
235
244
|
usePublicApi,
|
|
245
|
+
captureCookieName,
|
|
246
|
+
getCookieName,
|
|
236
247
|
|
|
237
248
|
common,
|
|
238
249
|
customer,
|
|
239
250
|
customization,
|
|
240
251
|
cart,
|
|
252
|
+
order,
|
|
241
253
|
product,
|
|
242
254
|
promotional,
|
|
243
255
|
notify,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { Ref } from 'vue';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ApiParams,
|
|
5
|
+
CustomerErrorCode,
|
|
6
|
+
CustomerMFAErrorCode,
|
|
7
|
+
OrderPaymentCodeErrorCode,
|
|
8
|
+
} from '..';
|
|
4
9
|
|
|
5
10
|
export type ApiStatus = 'pending' | 'success' | 'error';
|
|
6
11
|
|
|
@@ -63,6 +68,7 @@ export interface ApiErrorData {
|
|
|
63
68
|
| 'UAPPI_TOOLS_ITEM_EVALUATED'
|
|
64
69
|
| 'UAPPI_TOOLS_REVIEW_NOT_FOUND'
|
|
65
70
|
| CustomerErrorCode
|
|
66
|
-
| CustomerMFAErrorCode
|
|
71
|
+
| CustomerMFAErrorCode
|
|
72
|
+
| OrderPaymentCodeErrorCode;
|
|
67
73
|
message: string;
|
|
68
74
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CustomerOrderResponse, PaymentConfiguration, PaymentResponse } from '../..';
|
|
2
|
+
|
|
3
|
+
export interface OrderPaymentCodeAvailableOption {
|
|
4
|
+
option: PaymentResponse[number];
|
|
5
|
+
settings: Array<PaymentConfiguration>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface OrderPaymentCodeDetailResponse {
|
|
9
|
+
code: string;
|
|
10
|
+
expiresAt: string;
|
|
11
|
+
orderGroup: CustomerOrderResponse | null;
|
|
12
|
+
paymentOptions: Array<OrderPaymentCodeAvailableOption>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type OrderPaymentCodeErrorCode =
|
|
2
|
+
| 'UAPPI_STORE_ORDER_PAYMENT_CODE_NOT_FOUND'
|
|
3
|
+
| 'UAPPI_STORE_ORDER_PAYMENT_CODE_EXPIRED'
|
|
4
|
+
| 'UAPPI_STORE_ORDER_PAYMENT_CODE_INVALID_ORDER_STATUS'
|
|
5
|
+
| 'UAPPI_STORE_ORDER_PAYMENT_CODE_ALREADY_PAID'
|
|
6
|
+
| 'UAPPI_STORE_ORDER_INVALID_PAYMENT_OPTION'
|
|
7
|
+
| 'UAPPI_STORE_ORDER_PAYMENT_FAIL'
|
|
8
|
+
| 'UAPPI_STORE_ORDER_ANTI_FRAUD_FAIL'
|
|
9
|
+
| 'UAPPI_RENDER_PAYMENT_EXPIRED_TOKEN';
|
|
@@ -137,6 +137,11 @@ import type { Media } from './endpoints/media/media.interfaces';
|
|
|
137
137
|
import type { Target, Type } from './endpoints/menu/menu.enums';
|
|
138
138
|
import type { Menu, MenuItem } from './endpoints/menu/menu.interfaces';
|
|
139
139
|
import type { Order, OrderItem } from './endpoints/order/order.interfaces';
|
|
140
|
+
import type {
|
|
141
|
+
OrderPaymentCodeAvailableOption,
|
|
142
|
+
OrderPaymentCodeDetailResponse,
|
|
143
|
+
} from './endpoints/order/order-payment-code.interfaces';
|
|
144
|
+
import type { OrderPaymentCodeErrorCode } from './endpoints/order/order-payment-code.types';
|
|
140
145
|
import type { Page } from './endpoints/page/page.interfaces';
|
|
141
146
|
import type { Pagination } from './endpoints/pagination/pagination.interfaces';
|
|
142
147
|
import type {
|
|
@@ -522,6 +527,9 @@ export type {
|
|
|
522
527
|
// Order
|
|
523
528
|
Order,
|
|
524
529
|
OrderItem,
|
|
530
|
+
OrderPaymentCodeAvailableOption,
|
|
531
|
+
OrderPaymentCodeDetailResponse,
|
|
532
|
+
OrderPaymentCodeErrorCode,
|
|
525
533
|
Storage,
|
|
526
534
|
|
|
527
535
|
// COUNTRY TYPES
|
|
@@ -3,6 +3,7 @@ import axios, { type AxiosError, AxiosHeaders } from 'axios';
|
|
|
3
3
|
import type { ApiContext, ApiError, ApiErrorData } from './types';
|
|
4
4
|
|
|
5
5
|
import { encodeWafNormalizer } from './paramsSerializer/encodeWafNormalizer';
|
|
6
|
+
import { getCookieName } from './cookieName';
|
|
6
7
|
|
|
7
8
|
export async function usePublicApi<
|
|
8
9
|
ResponseType,
|
|
@@ -26,7 +27,7 @@ export async function usePublicApi<
|
|
|
26
27
|
params,
|
|
27
28
|
}: ApiContext<RequestType, FieldOptions, OrderOptions, ConditionOptions>) {
|
|
28
29
|
const headers = new AxiosHeaders({
|
|
29
|
-
'Uappi-Cookie-Name':
|
|
30
|
+
'Uappi-Cookie-Name': getCookieName(),
|
|
30
31
|
Accept: 'application/json',
|
|
31
32
|
});
|
|
32
33
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import api from '../../../../Api';
|
|
2
|
+
import { pushFbq } from '../../../utils/pushFbq';
|
|
3
|
+
|
|
4
|
+
export function purchaseOrderAction(orderGroupId: number, value: number) {
|
|
5
|
+
if (!window.fbq) return;
|
|
6
|
+
|
|
7
|
+
api.thirdParty
|
|
8
|
+
.facebookPurchase({ orderGroupId })
|
|
9
|
+
.then(eventId =>
|
|
10
|
+
pushFbq(
|
|
11
|
+
'track',
|
|
12
|
+
'Purchase',
|
|
13
|
+
{ value, currency: 'BRL' },
|
|
14
|
+
{ eventID: eventId },
|
|
15
|
+
),
|
|
16
|
+
)
|
|
17
|
+
.catch(() => {});
|
|
18
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { pushDatalayer } from '../../../utils/pushDatalayer';
|
|
2
|
+
|
|
3
|
+
import type { CustomerOrderResponse } from '../../../../Api/types';
|
|
4
|
+
|
|
5
|
+
export function purchaseOrderAction(
|
|
6
|
+
paymentMethod: string,
|
|
7
|
+
orderGroup: CustomerOrderResponse,
|
|
8
|
+
) {
|
|
9
|
+
const coupon =
|
|
10
|
+
orderGroup.discounts?.find(discount => discount.type === 'coupon')
|
|
11
|
+
?.entityId ?? '';
|
|
12
|
+
|
|
13
|
+
pushDatalayer({
|
|
14
|
+
event: 'purchase',
|
|
15
|
+
ecommerce: {
|
|
16
|
+
currency: 'BRL',
|
|
17
|
+
affiliation: 'Loja Virtual',
|
|
18
|
+
|
|
19
|
+
payment_status: 'em aberto',
|
|
20
|
+
payment_type: paymentMethod,
|
|
21
|
+
|
|
22
|
+
shipping: orderGroup.values?.shippingValue ?? 0,
|
|
23
|
+
shipping_tier: orderGroup.orders[0]?.shipping?.label ?? '',
|
|
24
|
+
tax: 0,
|
|
25
|
+
transaction_id: orderGroup.code,
|
|
26
|
+
|
|
27
|
+
value: orderGroup.values?.totalValue ?? 0,
|
|
28
|
+
coupon,
|
|
29
|
+
items: orderGroup.orders
|
|
30
|
+
.flatMap(order => order.items)
|
|
31
|
+
.map(item => {
|
|
32
|
+
return {
|
|
33
|
+
item_category: '',
|
|
34
|
+
coupon,
|
|
35
|
+
// o item do pedido não carrega a marca, diferente do item do carrinho
|
|
36
|
+
item_brand: '',
|
|
37
|
+
item_id: item.productId,
|
|
38
|
+
item_name: item.name,
|
|
39
|
+
item_variant:
|
|
40
|
+
item.simpleProduct?.currentAttributes
|
|
41
|
+
?.map(attribute => attribute.value.label)
|
|
42
|
+
.join(', ') ?? '',
|
|
43
|
+
price: item.unitValue,
|
|
44
|
+
quantity: item.quantity,
|
|
45
|
+
};
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|