tonder-web-sdk 1.4.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.
Files changed (83) hide show
  1. package/.env-example +1 -0
  2. package/.htaccess +1 -0
  3. package/README.md +204 -0
  4. package/cypress/e2e/1-getting-started/todo.cy.js +143 -0
  5. package/cypress/e2e/2-advanced-examples/actions.cy.js +299 -0
  6. package/cypress/e2e/2-advanced-examples/aliasing.cy.js +39 -0
  7. package/cypress/e2e/2-advanced-examples/assertions.cy.js +176 -0
  8. package/cypress/e2e/2-advanced-examples/connectors.cy.js +98 -0
  9. package/cypress/e2e/2-advanced-examples/cookies.cy.js +118 -0
  10. package/cypress/e2e/2-advanced-examples/cypress_api.cy.js +185 -0
  11. package/cypress/e2e/2-advanced-examples/files.cy.js +85 -0
  12. package/cypress/e2e/2-advanced-examples/location.cy.js +32 -0
  13. package/cypress/e2e/2-advanced-examples/misc.cy.js +104 -0
  14. package/cypress/e2e/2-advanced-examples/navigation.cy.js +56 -0
  15. package/cypress/e2e/2-advanced-examples/network_requests.cy.js +163 -0
  16. package/cypress/e2e/2-advanced-examples/querying.cy.js +114 -0
  17. package/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js +201 -0
  18. package/cypress/e2e/2-advanced-examples/storage.cy.js +110 -0
  19. package/cypress/e2e/2-advanced-examples/traversal.cy.js +121 -0
  20. package/cypress/e2e/2-advanced-examples/utilities.cy.js +108 -0
  21. package/cypress/e2e/2-advanced-examples/viewport.cy.js +58 -0
  22. package/cypress/e2e/2-advanced-examples/waiting.cy.js +30 -0
  23. package/cypress/e2e/2-advanced-examples/window.cy.js +22 -0
  24. package/cypress/fixtures/example.json +5 -0
  25. package/cypress/support/commands.js +25 -0
  26. package/cypress/support/e2e.js +20 -0
  27. package/cypress.config.js +9 -0
  28. package/index.html +178 -0
  29. package/index.js.example +50 -0
  30. package/package.json +29 -0
  31. package/samples/react/README.md +70 -0
  32. package/samples/react/build/asset-manifest.json +16 -0
  33. package/samples/react/build/favicon.ico +0 -0
  34. package/samples/react/build/index.html +1 -0
  35. package/samples/react/build/logo192.png +0 -0
  36. package/samples/react/build/logo512.png +0 -0
  37. package/samples/react/build/manifest.json +25 -0
  38. package/samples/react/build/robots.txt +3 -0
  39. package/samples/react/build/static/css/main.073c9b0a.css +2 -0
  40. package/samples/react/build/static/css/main.073c9b0a.css.map +1 -0
  41. package/samples/react/build/static/js/787.b83ed06f.chunk.js +2 -0
  42. package/samples/react/build/static/js/787.b83ed06f.chunk.js.map +1 -0
  43. package/samples/react/build/static/js/main.0a848807.js +3 -0
  44. package/samples/react/build/static/js/main.0a848807.js.LICENSE.txt +39 -0
  45. package/samples/react/build/static/js/main.0a848807.js.map +1 -0
  46. package/samples/react/build/static/media/sdk-icons.b491623214b2af4cccdb.png +0 -0
  47. package/samples/react/package-lock.json +28973 -0
  48. package/samples/react/package.json +44 -0
  49. package/samples/react/public/favicon.ico +0 -0
  50. package/samples/react/public/index.html +43 -0
  51. package/samples/react/public/logo192.png +0 -0
  52. package/samples/react/public/logo512.png +0 -0
  53. package/samples/react/public/manifest.json +25 -0
  54. package/samples/react/public/robots.txt +3 -0
  55. package/samples/react/src/App.css +38 -0
  56. package/samples/react/src/App.js +22 -0
  57. package/samples/react/src/App.test.js +8 -0
  58. package/samples/react/src/assets/img/sdk-icons.png +0 -0
  59. package/samples/react/src/components/Cart.js +29 -0
  60. package/samples/react/src/components/ProductCard.js +27 -0
  61. package/samples/react/src/context/CartContext.js +116 -0
  62. package/samples/react/src/index.css +13 -0
  63. package/samples/react/src/index.js +17 -0
  64. package/samples/react/src/logo.svg +1 -0
  65. package/samples/react/src/reportWebVitals.js +13 -0
  66. package/samples/react/src/screens/Checkout.js +82 -0
  67. package/samples/react/src/screens/Store.js +21 -0
  68. package/samples/react/src/setupTests.js +5 -0
  69. package/samples/react/src/storeProducts.js +30 -0
  70. package/src/classes/3dsHandler.js +203 -0
  71. package/src/classes/checkout.js +125 -0
  72. package/src/classes/inlineCheckout.js +349 -0
  73. package/src/data/api.js +109 -0
  74. package/src/helpers/skyflow.js +139 -0
  75. package/src/helpers/styles.js +61 -0
  76. package/src/helpers/template.js +112 -0
  77. package/src/helpers/utils.js +68 -0
  78. package/src/index-dev.js +129 -0
  79. package/src/index.html +58 -0
  80. package/src/index.js +7 -0
  81. package/success.html +22 -0
  82. package/v1/bundle.min.js +18 -0
  83. package/webpack.config.js +66 -0
@@ -0,0 +1,203 @@
1
+ export class ThreeDSHandler {
2
+ constructor({
3
+ payload = null,
4
+ apiKey,
5
+ baseUrl,
6
+ successUrl
7
+ }) {
8
+ this.baseUrl = baseUrl,
9
+ this.apiKey = apiKey,
10
+ this.payload = payload,
11
+ this.successUrl = successUrl
12
+ }
13
+
14
+ saveVerifyTransactionUrl() {
15
+ const url = this.payload?.next_action?.redirect_to_url?.verify_transaction_status_url
16
+ if (url) {
17
+ this.saveUrlWithExpiration(url)
18
+ } else {
19
+ const url = this.payload?.next_action?.iframe_resources?.verify_transaction_status_url
20
+ if (url) {
21
+ this.saveUrlWithExpiration(url)
22
+ } else {
23
+ console.log('No verify_transaction_status_url found');
24
+ }
25
+ }
26
+ }
27
+
28
+ saveUrlWithExpiration(url) {
29
+ try {
30
+ const now = new Date()
31
+ const item = {
32
+ url: url,
33
+ // Expires after 20 minutes
34
+ expires: now.getTime() + 20 * 60 * 1000
35
+ }
36
+ localStorage.setItem('verify_transaction_status', JSON.stringify(item))
37
+ } catch (error) {
38
+ console.log('error: ', error)
39
+ }
40
+ }
41
+
42
+ getUrlWithExpiration() {
43
+ const item = JSON.parse(localStorage.getItem("verify_transaction_status"))
44
+ if (!item) return
45
+
46
+ const now = new Date()
47
+ if (now.getTime() > item.expires) {
48
+ this.removeVerifyTransactionUrl()
49
+ return null
50
+ } else {
51
+ return item.url
52
+ }
53
+ }
54
+
55
+ removeVerifyTransactionUrl() {
56
+ localStorage.removeItem("verify_transaction_status")
57
+ }
58
+
59
+ getVerifyTransactionUrl() {
60
+ return localStorage.getItem("verify_transaction_status")
61
+ }
62
+
63
+ loadIframe() {
64
+ const iframe = this.payload?.next_action?.iframe_resources?.iframe
65
+
66
+ if (iframe) {
67
+ return new Promise((resolve, reject) => {
68
+ const iframe = this.payload?.next_action?.iframe_resources?.iframe
69
+
70
+ if (iframe) {
71
+ this.saveVerifyTransactionUrl()
72
+ const container = document.createElement('div')
73
+ container.innerHTML = iframe
74
+ document.body.appendChild(container)
75
+
76
+ // Create and append the script tag manually
77
+ const script = document.createElement('script')
78
+ script.textContent = 'document.getElementById("tdsMmethodForm").submit();'
79
+ container.appendChild(script)
80
+
81
+ // Resolve the promise when the iframe is loaded
82
+ const iframeElement = document.getElementById('tdsMmethodTgtFrame')
83
+ iframeElement.onload = () => resolve(true)
84
+ } else {
85
+ console.log('No redirection found');
86
+ reject(false)
87
+ }
88
+ })
89
+ }
90
+ }
91
+
92
+ getRedirectUrl() {
93
+ return this.payload?.next_action?.redirect_to_url?.url
94
+ }
95
+
96
+ redirectToChallenge() {
97
+ const url = this.getRedirectUrl()
98
+ if (url) {
99
+ this.saveVerifyTransactionUrl()
100
+ window.location = url;
101
+ } else {
102
+ console.log('No redirection found');
103
+ }
104
+ }
105
+
106
+ // Returns an object
107
+ // https://example.com/?name=John&age=30&city=NewYork
108
+ // { name: "John", age: "30", city: "NewYork" }
109
+ getURLParameters() {
110
+ const parameters = {};
111
+ const urlParams = new URLSearchParams(window.location.search);
112
+
113
+ for (const [key, value] of urlParams) {
114
+ parameters[key] = value;
115
+ }
116
+
117
+ return parameters;
118
+ }
119
+
120
+ handleSuccessTransaction(response) {
121
+ this.removeVerifyTransactionUrl();
122
+ window.location = this.successUrl
123
+ console.log('Transacción autorizada exitosamente.');
124
+ return response;
125
+ }
126
+
127
+ handleDeclinedTransaction(response) {
128
+ this.removeVerifyTransactionUrl();
129
+ console.log('Transacción rechazada.');
130
+ throw new Error("Transacción rechazada.");
131
+ }
132
+
133
+ // TODO: the method below needs to be tested with a real 3DS challenge
134
+ // since we couldn't get a test card that works with this feature
135
+ async handle3dsChallenge(response_json) {
136
+ // Create the form element:
137
+ const form = document.createElement('form');
138
+ form.name = 'frm';
139
+ form.method = 'POST';
140
+ form.action = response_json.redirect_post_url;
141
+
142
+ // Add hidden fields:
143
+ const creqInput = document.createElement('input');
144
+ creqInput.type = 'hidden';
145
+ creqInput.name = response_json.creq;
146
+ creqInput.value = response_json.creq;
147
+ form.appendChild(creqInput);
148
+
149
+ const termUrlInput = document.createElement('input');
150
+ termUrlInput.type = 'hidden';
151
+ termUrlInput.name = response_json.term_url;
152
+ termUrlInput.value = response_json.TermUrl;
153
+ form.appendChild(termUrlInput);
154
+
155
+ // Append the form to the body:
156
+ document.body.appendChild(form);
157
+ form.submit();
158
+
159
+ await this.verifyTransactionStatus();
160
+ }
161
+
162
+ async handleTransactionResponse(response) {
163
+ const response_json = await response.json();
164
+
165
+ if (response_json.status === "Pending") {
166
+ return await this.handle3dsChallenge(response_json);
167
+ } else if (["Success", "Authorized"].includes(response_json.status)) {
168
+ return this.handleSuccessTransaction(response);
169
+ } else {
170
+ return this.handleDeclinedTransaction(response);
171
+ }
172
+ }
173
+
174
+ async verifyTransactionStatus() {
175
+ const verifyUrl = this.getUrlWithExpiration();
176
+
177
+ if (verifyUrl) {
178
+ const url = `${this.baseUrl}${verifyUrl}`;
179
+ try {
180
+ const response = await fetch(url, {
181
+ method: "GET",
182
+ headers: {
183
+ "Content-Type": "application/json",
184
+ Authorization: `Token ${this.apiKey}`,
185
+ },
186
+ // body: JSON.stringify(data),
187
+ });
188
+
189
+ if (response.status !== 200) {
190
+ console.error('La verificación de la transacción falló.');
191
+ return
192
+ }
193
+
194
+ return await this.handleTransactionResponse(response);
195
+ } catch (error) {
196
+ console.error('Error al verificar la transacción:', error);
197
+ return error;
198
+ }
199
+ } else {
200
+ console.log('No verify_transaction_status_url found');
201
+ }
202
+ }
203
+ }
@@ -0,0 +1,125 @@
1
+ import { AES } from "crypto-js";
2
+
3
+ export class Checkout {
4
+ constructor({
5
+ apiKey,
6
+ type = "payment",
7
+ backgroundColor="#141414",
8
+ color="#EBEBEB",
9
+ cb=()=>{},
10
+ url="http://checkout.tonder.io/#/"
11
+ }) {
12
+ this.url = url
13
+ this.apiKey = apiKey
14
+ this.type = type
15
+ this.backgroundColor = backgroundColor
16
+ this.color = color
17
+ this.params = ""
18
+ this.order = {}
19
+ this.buttonText = "Proceder al pago"
20
+ this.cb = cb
21
+
22
+ window.addEventListener("message", this.receiveMessage.bind(this), false);
23
+ }
24
+ generateButton = (buttonText) => {
25
+ this.buttonText = buttonText ? buttonText : this.buttonText
26
+ this.tonderButton = document.createElement('button');
27
+ this.tonderButton.innerHTML = this.buttonText;
28
+ this.stylishButton(this.tonderButton)
29
+ this.tonderButton.onclick = this.openCheckout
30
+ }
31
+ getButton = ({buttonText}) => {
32
+ this.generateButton(buttonText)
33
+ return this.tonderButton
34
+ }
35
+ mountButton = ({buttonText}) => {
36
+ this.generateButton(buttonText)
37
+ const entryPoint = document.getElementById("tonder-checkout")
38
+ try {
39
+ entryPoint.innerHTML = ""
40
+ entryPoint.append(this.tonderButton)
41
+ } catch(error) {
42
+ console.error(error)
43
+ }
44
+ }
45
+ stylishButton = (element) => {
46
+ element.style.backgroundColor = this.backgroundColor
47
+ element.style.color = this.color
48
+ element.style.display = 'flex'
49
+ element.style.justifyContent = 'center'
50
+ element.style.border = 'none'
51
+ element.style.padding = '1rem'
52
+ element.style.borderRadius = '10px'
53
+ element.style.fontSize = '1rem'
54
+ element.style.width = '100%'
55
+ element.style.boxShadow = '0 3px 6px 0 rgba(0,0,0,0.16)'
56
+ }
57
+ setOrder = ({products, email, shippingCost }) => {
58
+ let _order = {}
59
+ if (products) _order.products = products
60
+ if (email) _order.email = email
61
+ if (shippingCost) _order.shippingCost = shippingCost
62
+ this.order = {...this.order, ..._order}
63
+ return this.order
64
+ }
65
+ openTabListener = (tab, button) => {
66
+ const tabInterval = setInterval(() => {
67
+ if (tab.closed) {
68
+ clearInterval(tabInterval);
69
+ button.disabled = false
70
+ button.innerHTML = this.buttonText
71
+ }
72
+ }, 500)
73
+ }
74
+ openCheckout = () => {
75
+ const queryString = this.getUrlParams()
76
+ const encrypted = AES.encrypt(queryString, 'url-params-encrypt').toString()
77
+ const encodedURL = encodeURIComponent(encrypted);
78
+ this.params = "?" + encodedURL;
79
+ const newWindow = window.open(this.url + this.params, '_blank', `width=1200,height=$800,left=0,top=0`);
80
+ this.tonderButton.disabled = true
81
+ this.tonderButton.innerHTML = `
82
+ <div class="loader"></div>
83
+ <style>
84
+ .loader {
85
+ border: 4px solid ${this.color};
86
+ border-radius: 50%;
87
+ border-top: 4px solid ${this.backgroundColor};
88
+ width: 0.625rem;
89
+ height: 0.625rem;
90
+ -webkit-animation: spin 2s linear infinite; /* Safari */
91
+ animation: spin 2s linear infinite;
92
+ }
93
+
94
+ /* Safari */
95
+ @-webkit-keyframes spin {
96
+ 0% { -webkit-transform: rotate(0deg); }
97
+ 100% { -webkit-transform: rotate(360deg); }
98
+ }
99
+
100
+ @keyframes spin {
101
+ 0% { transform: rotate(0deg); }
102
+ 100% { transform: rotate(360deg); }
103
+ }
104
+ </style>
105
+ `
106
+ this.openTabListener(newWindow, this.tonderButton)
107
+ }
108
+ getUrlParams = () => {
109
+ const params = { apiKey: this.apiKey, ...this.order, type: this.type}
110
+ if (params.products) {
111
+ params.products = JSON.stringify(params.products)
112
+ }
113
+ const queryString = new URLSearchParams(params).toString();
114
+ return queryString
115
+ }
116
+ receiveMessage(event) {
117
+ // Parse data if it is possible, in case of error it will return the raw data.
118
+ try {
119
+ const data = JSON.parse(event.data)
120
+ this.cb(data)
121
+ } catch(error) {
122
+ this.cb(event.data)
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,349 @@
1
+ import { cardTemplate } from '../helpers/template.js'
2
+ import {
3
+ getBusiness,
4
+ customerRegister,
5
+ createOrder,
6
+ createPayment,
7
+ startCheckoutRouter,
8
+ getOpenpayDeviceSessionID
9
+ } from '../data/api';
10
+ import {
11
+ showError,
12
+ getBrowserInfo,
13
+ } from '../helpers/utils';
14
+ import { initSkyflow } from '../helpers/skyflow'
15
+ import { ThreeDSHandler } from './3dsHandler.js';
16
+
17
+
18
+ export class InlineCheckout {
19
+ static injected = false;
20
+ customer = {}
21
+ items = []
22
+ baseUrl = process.env.BASE_URL || "http://localhost:8000";
23
+ collectContainer = null
24
+ merchantData = {}
25
+ cartTotal = null
26
+ metadata = {}
27
+ card = {}
28
+
29
+ constructor({
30
+ apiKey,
31
+ returnUrl,
32
+ successUrl,
33
+ renderPaymentButton = false,
34
+ callBack = () => { },
35
+ styles,
36
+ }) {
37
+ this.apiKeyTonder = apiKey;
38
+ this.returnUrl = returnUrl;
39
+ this.successUrl = successUrl;
40
+ this.renderPaymentButton = renderPaymentButton;
41
+ this.callBack = callBack;
42
+ this.customStyles = styles
43
+
44
+ this.abortController = new AbortController()
45
+ this.process3ds = new ThreeDSHandler(
46
+ { apiKey: apiKey, baseUrl: this.baseUrl, successUrl: successUrl }
47
+ )
48
+ }
49
+
50
+ #mountPayButton() {
51
+ if (!this.renderPaymentButton) return;
52
+
53
+ const payButton = document.querySelector("#tonderPayButton");
54
+ if (!payButton) {
55
+ console.error("Pay button not found");
56
+ return;
57
+ }
58
+
59
+ payButton.style.display = "block";
60
+ payButton.textContent = `Pagar $${this.cartTotal}`;
61
+ payButton.onclick = async (event) => {
62
+ event.preventDefault();
63
+ await this.#handlePaymentClick(payButton);
64
+ };
65
+ }
66
+
67
+ async #handlePaymentClick(payButton) {
68
+ const prevButtonContent = payButton.innerHTML;
69
+ payButton.innerHTML = `<div class="lds-dual-ring"></div>`;
70
+ try {
71
+ const response = await this.payment(this.customer);
72
+ this.callBack(response);
73
+ } catch (error) {
74
+ console.error("Payment error:", error);
75
+ } finally {
76
+ payButton.innerHTML = prevButtonContent;
77
+ }
78
+ }
79
+
80
+ payment(data) {
81
+ return new Promise(async (resolve, reject) => {
82
+ try {
83
+ this.#handleCustomer(data.customer)
84
+ this.setCartTotal(data.cart?.total)
85
+ this.setCartItems(data.cart?.items)
86
+ this.#handleMetadata(data)
87
+ this.#handleCurrency(data)
88
+ this.#handleCard(data)
89
+ const response = await this.#checkout()
90
+ if (response) {
91
+ const process3ds = new ThreeDSHandler({
92
+ baseUrl: this.baseUrl,
93
+ apiKey: this.apiKeyTonder,
94
+ payload: response,
95
+ });
96
+ this.callBack(response);
97
+
98
+ const iframe = response?.next_action?.iframe_resources?.iframe
99
+
100
+ if (iframe) {
101
+ process3ds.loadIframe().then(() => {
102
+ //TODO: Check if this will be necessary on the frontend side
103
+ // after some the tests in production, since the 3DS process
104
+ // doesn't works properly on the sandbox environment
105
+ // setTimeout(() => {
106
+ // process3ds.verifyTransactionStatus();
107
+ // }, 10000);
108
+ process3ds.verifyTransactionStatus();
109
+ }).catch((error) => {
110
+ console.log('Error loading iframe:', error)
111
+ })
112
+ } else {
113
+ const redirectUrl = process3ds.getRedirectUrl()
114
+ if (redirectUrl) {
115
+ process3ds.redirectToChallenge()
116
+ } else {
117
+ resolve(response);
118
+ }
119
+ }
120
+ }
121
+ } catch (error) {
122
+ reject(error);
123
+ }
124
+ });
125
+ }
126
+
127
+ #handleCustomer(customer) {
128
+ console.log('customer: ', customer)
129
+ if (!customer) return
130
+
131
+ this.firstName = customer?.firstName
132
+ this.lastName = customer?.lastName
133
+ this.country = customer?.country
134
+ this.address = customer?.street
135
+ this.city = customer?.city
136
+ this.state = customer?.state
137
+ this.postCode = customer?.postCode
138
+ this.email = customer?.email
139
+ this.phone = customer?.phone
140
+ this.customer = customer
141
+ }
142
+
143
+ #handleMetadata(data) {
144
+ this.metadata = data?.metadata
145
+ }
146
+
147
+ #handleCurrency(data) {
148
+ this.currency = data?.currency
149
+ }
150
+
151
+ #handleCard(data) {
152
+ this.card = data?.card
153
+ }
154
+
155
+ setCartItems(items) {
156
+ console.log('items: ', items)
157
+ this.cartItems = items
158
+ }
159
+
160
+ setCartTotal(total) {
161
+ console.log('total: ', total)
162
+ this.cartTotal = total
163
+ this.#updatePayButton()
164
+ }
165
+
166
+ #updatePayButton() {
167
+ const payButton = document.querySelector("#tonderPayButton");
168
+ if (!payButton) return
169
+ payButton.textContent = `Pagar $${this.cartTotal}`;
170
+ }
171
+
172
+ setCallback(cb) {
173
+ this.cb = cb
174
+ }
175
+
176
+ injectCheckout() {
177
+ if (InlineCheckout.injected) return
178
+ this.process3ds.verifyTransactionStatus()
179
+ const injectInterval = setInterval(() => {
180
+ if (document.querySelector("#tonder-checkout")) {
181
+ document.querySelector("#tonder-checkout").innerHTML = cardTemplate;
182
+ this.#mountTonder();
183
+ clearInterval(injectInterval);
184
+ InlineCheckout.injected = true
185
+ }
186
+ }, 500);
187
+ }
188
+
189
+ async #fetchMerchantData() {
190
+ this.merchantData = await getBusiness(
191
+ this.baseUrl,
192
+ this.apiKeyTonder,
193
+ this.abortController.signal
194
+ );
195
+ return this.merchantData
196
+ }
197
+
198
+ async getCustomer(customer, signal) {
199
+ return await customerRegister(this.baseUrl, this.apiKeyTonder, customer, signal);
200
+ }
201
+
202
+ async #mountTonder() {
203
+ this.#mountPayButton()
204
+
205
+ const {
206
+ vault_id,
207
+ vault_url,
208
+ } = await this.#fetchMerchantData();
209
+
210
+ this.collectContainer = await initSkyflow(
211
+ vault_id,
212
+ vault_url,
213
+ this.baseUrl,
214
+ this.apiKeyTonder,
215
+ this.abortController.signal,
216
+ this.customStyles,
217
+ );
218
+ }
219
+
220
+ removeCheckout() {
221
+ InlineCheckout.injected = false
222
+ // Cancel all requests
223
+ this.abortController.abort();
224
+ this.abortController = new AbortController();
225
+
226
+ clearInterval(this.injectInterval);
227
+ console.log("InlineCheckout removed from DOM and cleaned up.");
228
+ }
229
+
230
+ async #getCardTokens() {
231
+ if (this.card?.skyflow_id) return this.card
232
+ try {
233
+ const collectResponse = await this.collectContainer.collect();
234
+ const cardTokens = await collectResponse["records"][0]["fields"];
235
+ return cardTokens;
236
+ } catch (error) {
237
+ showError("Por favor, verifica todos los campos de tu tarjeta")
238
+ throw error;
239
+ }
240
+ }
241
+
242
+ async #checkout() {
243
+ try {
244
+ document.querySelector("#tonderPayButton").disabled = true;
245
+ } catch (error) {
246
+ }
247
+
248
+ const { openpay_keys, reference, business } = this.merchantData
249
+ const total = Number(this.cartTotal)
250
+
251
+ const cardTokens = await this.#getCardTokens();
252
+
253
+ try {
254
+ let deviceSessionIdTonder;
255
+ if (openpay_keys.merchant_id && openpay_keys.public_key) {
256
+ deviceSessionIdTonder = await getOpenpayDeviceSessionID(
257
+ openpay_keys.merchant_id,
258
+ openpay_keys.public_key,
259
+ this.abortController.signal
260
+ );
261
+ }
262
+
263
+ const { id, auth_token } = await this.getCustomer(
264
+ this.customer,
265
+ this.abortController.signal
266
+ )
267
+
268
+ var orderItems = {
269
+ business: this.apiKeyTonder,
270
+ client: auth_token,
271
+ billing_address_id: null,
272
+ shipping_address_id: null,
273
+ amount: total,
274
+ status: "A",
275
+ reference: reference,
276
+ is_oneclick: true,
277
+ items: this.cartItems,
278
+ };
279
+ console.log('orderItems: ', orderItems)
280
+ const jsonResponseOrder = await createOrder(
281
+ this.baseUrl,
282
+ this.apiKeyTonder,
283
+ orderItems
284
+ );
285
+
286
+ // Create payment
287
+ const now = new Date();
288
+ const dateString = now.toISOString();
289
+
290
+ var paymentItems = {
291
+ business_pk: business.pk,
292
+ client_id: id,
293
+ amount: total,
294
+ date: dateString,
295
+ order_id: jsonResponseOrder.id,
296
+ };
297
+ const jsonResponsePayment = await createPayment(
298
+ this.baseUrl,
299
+ this.apiKeyTonder,
300
+ paymentItems
301
+ );
302
+
303
+ // Checkout router
304
+ const routerItems = {
305
+ card: cardTokens,
306
+ name: this.firstName || "",
307
+ last_name: this.lastName || "",
308
+ email_client: this.email,
309
+ phone_number: this.phone,
310
+ return_url: this.returnUrl,
311
+ id_product: "no_id",
312
+ quantity_product: 1,
313
+ id_ship: "0",
314
+ instance_id_ship: "0",
315
+ amount: total,
316
+ title_ship: "shipping",
317
+ description: "transaction",
318
+ device_session_id: deviceSessionIdTonder ? deviceSessionIdTonder : null,
319
+ token_id: "",
320
+ order_id: jsonResponseOrder.id,
321
+ business_id: business.pk,
322
+ payment_id: jsonResponsePayment.pk,
323
+ source: 'sdk',
324
+ metadata: this.metadata,
325
+ browser_info: getBrowserInfo(),
326
+ currency: this.currency,
327
+ };
328
+ const jsonResponseRouter = await startCheckoutRouter(
329
+ this.baseUrl,
330
+ this.apiKeyTonder,
331
+ routerItems
332
+ );
333
+
334
+ if (jsonResponseRouter) {
335
+ try {
336
+ document.querySelector("#tonderPayButton").disabled = false;
337
+ } catch { }
338
+ return jsonResponseRouter;
339
+ } else {
340
+ showError("No se ha podido procesar el pago")
341
+ return false;
342
+ }
343
+ } catch (error) {
344
+ console.log(error);
345
+ showError("Ha ocurrido un error")
346
+ throw error;
347
+ }
348
+ };
349
+ }