@uappi/public-sdk 1.6.0 → 1.8.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.
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { ref, computed, onMounted, onUnmounted } from 'vue';
2
+ import { ref, shallowRef, computed, onMounted, onUnmounted } from 'vue';
3
3
 
4
4
  import api from '../../modules/Api';
5
5
  import { purchaseAction } from '../../modules/Tracking/events/actions/purchase';
@@ -20,13 +20,16 @@ const paymentToken = ref<string | null>(null);
20
20
  const loadingToken = ref(true);
21
21
  const tokenError = ref<string | null>(null);
22
22
  const iframeHeight = ref<number | null>(null);
23
+ // Controla o skeleton: só sai quando o iframe termina de carregar e emite
24
+ // UAPPI_PAYMENT_READY, não apenas quando o token foi obtido.
25
+ const paymentReady = ref(false);
23
26
 
24
27
  // Snapshot do carrinho anterior à compra, usado apenas para disparar o evento
25
28
  // `purchase` do GTM quando o `UAPPI_PAYMENT_SUCCESS` chegar. É necessário buscar
26
29
  // esse dado antes do pagamento porque, após o sucesso, o carrinho já foi
27
30
  // consumido pelo `/cart/buy` (chamado internamente pelo iframe) e não reflete
28
31
  // mais os itens/frete/descontos da compra que acabou de ser feita.
29
- const cartSnapshot = ref<CartResponse | null>(null);
32
+ const cartSnapshot = shallowRef<CartResponse | null>(null);
30
33
 
31
34
  const iframeSrc = computed(() => {
32
35
  if (!paymentToken.value) return '';
@@ -46,20 +49,24 @@ const iframeSrc = computed(() => {
46
49
  return url.toString();
47
50
  });
48
51
 
52
+ async function requestPaymentToken(): Promise<string> {
53
+ const response = await api.cart.paymentToken({ paymentOptionId: props.option.id });
54
+
55
+ if (!response?.token) {
56
+ console.error('[PaymentHosted] Token ausente na resposta:', response);
57
+ throw new Error('UAPPI_PAYMENT_TOKEN_MISSING');
58
+ }
59
+
60
+ return response.token;
61
+ }
62
+
49
63
  async function fetchPaymentToken(): Promise<void> {
50
64
  loadingToken.value = true;
51
65
  tokenError.value = null;
66
+ paymentReady.value = false;
52
67
 
53
68
  try {
54
- const response = await api.cart.paymentToken({ paymentOptionId: props.option.id });
55
-
56
- if (!response?.token) {
57
- console.error('[PaymentHosted] Token ausente na resposta:', response);
58
- tokenError.value = 'Não foi possível iniciar o formulário de pagamento.';
59
- return;
60
- }
61
-
62
- paymentToken.value = response.token;
69
+ paymentToken.value = await requestPaymentToken();
63
70
  } catch (err) {
64
71
  console.error('[PaymentHosted] Erro ao buscar token de pagamento:', err);
65
72
  tokenError.value = 'Não foi possível iniciar o formulário de pagamento.';
@@ -68,6 +75,25 @@ async function fetchPaymentToken(): Promise<void> {
68
75
  }
69
76
  }
70
77
 
78
+ /**
79
+ * Chamado quando o iframe avisa (UAPPI_PAYMENT_TOKEN_EXPIRING) que o token atual
80
+ * está perto de expirar. Busca um novo token e o injeta direto no formulário via
81
+ * postMessage (UAPPI_UPDATE_PAYMENT_TOKEN), sem trocar o `src` do iframe — preserva
82
+ * os dados que o usuário já preencheu no cartão.
83
+ */
84
+ async function refreshPaymentToken(): Promise<void> {
85
+ if (!iframeEl.value?.contentWindow) return;
86
+
87
+ try {
88
+ const token = await requestPaymentToken();
89
+ const targetOrigin = new URL(iframeSrc.value).origin;
90
+
91
+ iframeEl.value.contentWindow.postMessage({ type: 'UAPPI_UPDATE_PAYMENT_TOKEN', token }, targetOrigin);
92
+ } catch (err) {
93
+ console.error('[PaymentHosted] Erro ao renovar token de pagamento:', err);
94
+ }
95
+ }
96
+
71
97
  async function fetchCartSnapshot(): Promise<void> {
72
98
  try {
73
99
  cartSnapshot.value = await api.cart.listDetail();
@@ -95,9 +121,14 @@ function handleIframeMessage(event: MessageEvent): void {
95
121
 
96
122
  switch (type) {
97
123
  case 'UAPPI_PAYMENT_READY':
124
+ paymentReady.value = true;
98
125
  emit('payment-ready');
99
126
  break;
100
127
 
128
+ case 'UAPPI_PAYMENT_TOKEN_EXPIRING':
129
+ refreshPaymentToken();
130
+ break;
131
+
101
132
  case 'UAPPI_PAYMENT_SUCCESS':
102
133
  if (cartSnapshot.value) {
103
134
  purchaseAction(props.option.paymentType.code, cartSnapshot.value);
@@ -134,23 +165,25 @@ onUnmounted(() => {
134
165
 
135
166
  <template>
136
167
  <div class="payment-hosted">
137
- <div v-if="loadingToken" class="payment-hosted__skeleton" />
138
-
139
- <p v-else-if="tokenError" class="payment-hosted__error">
168
+ <p v-if="tokenError" class="payment-hosted__error">
140
169
  {{ tokenError }}
141
170
  </p>
142
171
 
143
- <iframe
144
- v-else-if="iframeSrc"
145
- ref="iframeEl"
146
- :src="iframeSrc"
147
- :style="iframeHeight ? { height: `${iframeHeight}px` } : undefined"
148
- class="payment-hosted__iframe"
149
- frameborder="0"
150
- scrolling="no"
151
- title="Formulário de pagamento seguro"
152
- allow="payment"
153
- />
172
+ <div v-else class="payment-hosted__stage">
173
+ <div v-if="!paymentReady" class="payment-hosted__skeleton" />
174
+
175
+ <iframe
176
+ v-if="iframeSrc"
177
+ ref="iframeEl"
178
+ :src="iframeSrc"
179
+ :style="iframeHeight ? { height: `${iframeHeight}px` } : undefined"
180
+ :class="['payment-hosted__iframe', { 'payment-hosted__iframe--hidden': !paymentReady }]"
181
+ frameborder="0"
182
+ scrolling="no"
183
+ title="Formulário de pagamento seguro"
184
+ allow="payment"
185
+ />
186
+ </div>
154
187
  </div>
155
188
  </template>
156
189
 
@@ -2,14 +2,26 @@
2
2
  width: 100%;
3
3
  }
4
4
 
5
+ .payment-hosted__stage {
6
+ position: relative;
7
+ width: 100%;
8
+ }
9
+
5
10
  .payment-hosted__iframe {
6
11
  display: block;
7
12
  width: 100%;
8
- min-height: 420px;
9
13
  border: none;
10
14
  transition: height 0.2s ease;
11
15
  }
12
16
 
17
+ .payment-hosted__iframe--hidden {
18
+ position: absolute;
19
+ top: 0;
20
+ left: 0;
21
+ opacity: 0;
22
+ pointer-events: none;
23
+ }
24
+
13
25
  .payment-hosted__skeleton {
14
26
  width: 100%;
15
27
  height: 420px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uappi/public-sdk",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Pacotes públicos para desenvolvimento do ecossistema Uappi",
5
5
  "main": "main.ts",
6
6
  "scripts": {