@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 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({ paymentOptionId: props.option.id });
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 (cartSnapshot.value) {
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
+ }
@@ -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 { ApiParams, CustomerErrorCode, CustomerMFAErrorCode } from '..';
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
  }
@@ -162,6 +162,7 @@ export interface BuyCartResponse {
162
162
 
163
163
  export interface PaymentTokenCartRequest {
164
164
  paymentOptionId: number;
165
+ orderGroupPaymentCode?: string;
165
166
  }
166
167
 
167
168
  export interface PaymentTokenCartResponse {
@@ -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': 'token',
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uappi/public-sdk",
3
- "version": "1.20.1",
3
+ "version": "1.22.0",
4
4
  "description": "Pacotes públicos para desenvolvimento do ecossistema Uappi",
5
5
  "main": "main.ts",
6
6
  "scripts": {