PluginProbe
Pay with Vipps and MobilePay for WooCommerce / 6.3.0
Pay with Vipps and MobilePay for WooCommerce v6.3.0
6.2.6 6.3.0 6.2.5 6.2.4 6.2.3 6.2.2 6.2.1 6.2.0 6.1.10 6.1.9 6.1.8 6.1.7 6.1.6 6.1.5 6.1.4 6.1.3 6.1.2 6.1.1 6.1.0 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 6.0.0 All 189 releases
woo-vipps / payment / js / vipps-purchase.js

vipps-purchase.js in Pay with Vipps and MobilePay for WooCommerce 6.3.0, at payment/js/vipps-purchase.js

219 lines 6.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /* Coordinate the QR/cart landing page with the express flow in vipps.js. */
2 (() => {
3 const purchaseSelector = ".vipps-qr-purchase, .vipps-cart-purchase";
4 const buttonSelector = ".vipps-buy-now, .vipps-express-checkout";
5 let purchaseStarted = false;
6
7 /** Scope the auto-start marker to this page and its purchase parameters. */
8 function getAutoStartStorageKey() {
9 return `vipps-auto-start:${window.location.pathname}:${window.location.search}`;
10 }
11
12 /** Distinguish a new visit from a reload or history return. */
13 function getNavigationType() {
14 return window.performance
15 ?.getEntriesByType?.("navigation")?.[0]?.type || "navigate";
16 }
17
18 /** Check whether this tab already auto-started the same purchase page. */
19 function getAutoStartAttempted() {
20 try {
21 return window.sessionStorage.getItem(getAutoStartStorageKey()) === "true";
22 } catch (error) {
23 return false;
24 }
25 }
26
27 /** Remember the attempt before dispatching a click, avoiding reload loops. */
28 function setAutoStartAttempted() {
29 try {
30 window.sessionStorage.setItem(getAutoStartStorageKey(), "true");
31 } catch (error) {
32 // Checkout must still work when storage is unavailable.
33 }
34 }
35
36 /** Let a new navigation start a fresh automatic purchase. */
37 function clearAutoStartAttempted() {
38 try {
39 window.sessionStorage.removeItem(getAutoStartStorageKey());
40 } catch (error) {
41 // Checkout must still work when storage is unavailable.
42 }
43 }
44
45 /** Find the express button inside the QR or cart purchase container. */
46 function getAutoPurchaseButton() {
47 const purchase = document.querySelector(purchaseSelector);
48 return purchase?.querySelector(buttonSelector);
49 }
50
51 /** Start checkout once through the same click path as the fallback button. */
52 function startVippsPurchase() {
53 if (purchaseStarted) {
54 return;
55 }
56
57 const purchaseButton = getAutoPurchaseButton();
58
59 if (!purchaseButton) {
60 return;
61 }
62
63 purchaseStarted = true;
64 setAutoStartAttempted();
65 purchaseButton.dispatchEvent(new MouseEvent("click", {
66 bubbles: true,
67 cancelable: true
68 }));
69 }
70
71 /** Auto-start on a new visit, but expose manual retry after a page restore. */
72 function setupAutomaticPurchase() {
73 const purchaseButton = getAutoPurchaseButton();
74
75 if (!purchaseButton) {
76 return;
77 }
78
79 const navigationType = getNavigationType();
80 const attempted = getAutoStartAttempted();
81
82 if ((navigationType === "reload" || navigationType === "back_forward") && attempted) {
83 setPurchaseFinished();
84 return;
85 }
86
87 if (navigationType !== "reload" && navigationType !== "back_forward") {
88 clearAutoStartAttempted();
89 }
90
91 if (purchaseButton.classList.contains("initialized")) {
92 startVippsPurchase();
93 return;
94 }
95
96 document.body.addEventListener("vippsInit", startVippsPurchase, {
97 once: true
98 });
99 }
100
101 if (document.readyState === "loading") {
102 document.addEventListener("DOMContentLoaded", setupAutomaticPurchase, {
103 once: true
104 });
105 } else {
106 setupAutomaticPurchase();
107 }
108
109 /** Scope UI updates to the attempt's container, or to all purchase views. */
110 function getPurchases(wrapper) {
111 if (wrapper) {
112 const purchase = wrapper.closest?.(purchaseSelector);
113 return purchase ? [purchase] : [];
114 }
115
116 return Array.from(document.querySelectorAll(purchaseSelector));
117 }
118
119 /** Find the waiting messages used by the supported purchase templates. */
120 function getWaitingElements() {
121 return document.querySelectorAll(
122 "#waiting, [data-vipps-purchase-waiting], [data-vipps-qr-waiting]"
123 );
124 }
125
126 /** Reuse or create an accessible error message within a purchase view. */
127 function getErrorElement(purchase) {
128 let error = purchase.querySelector("[data-vipps-purchase-error]");
129
130 if (!error) {
131 error = document.createElement("p");
132 error.className = "vipps-purchase-error woocommerce-error";
133 error.setAttribute("data-vipps-purchase-error", "");
134 error.setAttribute("role", "alert");
135 purchase.append(error);
136 }
137
138 return error;
139 }
140
141 /** Show waiting state and keep the fallback button out of view during payment. */
142 function setPurchaseStarted(wrapper) {
143 getPurchases(wrapper).forEach((purchase) => {
144 const button = purchase.querySelector(buttonSelector);
145 const error = purchase.querySelector("[data-vipps-purchase-error]");
146
147 if (button) {
148 button.hidden = false;
149 button.style.visibility = "hidden";
150 }
151
152 if (error) {
153 error.hidden = true;
154 error.textContent = "";
155 }
156 });
157
158 getWaitingElements().forEach((waiting) => {
159 waiting.hidden = false;
160 });
161 }
162
163 /** End waiting state and expose the button for a possible manual retry. */
164 function setPurchaseFinished(wrapper) {
165 getPurchases(wrapper).forEach((purchase) => {
166 const button = purchase.querySelector(buttonSelector);
167
168 if (button) {
169 button.hidden = false;
170 button.style.visibility = "visible";
171 }
172 });
173
174 getWaitingElements().forEach((waiting) => {
175 waiting.hidden = true;
176 });
177 }
178
179 /** Replace waiting state with an error when checkout cannot continue. */
180 function setPurchaseError(message, wrapper) {
181 getPurchases(wrapper).forEach((purchase) => {
182 const button = purchase.querySelector(buttonSelector);
183 const error = getErrorElement(purchase);
184
185 if (button) {
186 button.hidden = true;
187 }
188
189 error.textContent = message;
190 error.hidden = false;
191 });
192
193 getWaitingElements().forEach((waiting) => {
194 waiting.hidden = true;
195 });
196 }
197
198 // vipps.js owns the transaction; this page only mirrors its UI events.
199 document.addEventListener("vippsPurchaseStarted", (event) => {
200 setPurchaseStarted(event.detail?.wrapper);
201 });
202
203 document.addEventListener("vippsPurchaseFinished", (event) => {
204 setPurchaseFinished(event.detail?.wrapper);
205 });
206
207 document.addEventListener("vippsPurchaseError", (event) => {
208 setPurchaseError(
209 event.detail?.message || "Vipps checkout failed",
210 event.detail?.wrapper
211 );
212 });
213
214 // Recover waiting state when this script loads after an attempt has begun.
215 if (document.body?.dataset.vippsPurchaseActive === "true") {
216 setPurchaseStarted();
217 }
218 })();
219