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.
- package/.env-example +1 -0
- package/.htaccess +1 -0
- package/README.md +204 -0
- package/cypress/e2e/1-getting-started/todo.cy.js +143 -0
- package/cypress/e2e/2-advanced-examples/actions.cy.js +299 -0
- package/cypress/e2e/2-advanced-examples/aliasing.cy.js +39 -0
- package/cypress/e2e/2-advanced-examples/assertions.cy.js +176 -0
- package/cypress/e2e/2-advanced-examples/connectors.cy.js +98 -0
- package/cypress/e2e/2-advanced-examples/cookies.cy.js +118 -0
- package/cypress/e2e/2-advanced-examples/cypress_api.cy.js +185 -0
- package/cypress/e2e/2-advanced-examples/files.cy.js +85 -0
- package/cypress/e2e/2-advanced-examples/location.cy.js +32 -0
- package/cypress/e2e/2-advanced-examples/misc.cy.js +104 -0
- package/cypress/e2e/2-advanced-examples/navigation.cy.js +56 -0
- package/cypress/e2e/2-advanced-examples/network_requests.cy.js +163 -0
- package/cypress/e2e/2-advanced-examples/querying.cy.js +114 -0
- package/cypress/e2e/2-advanced-examples/spies_stubs_clocks.cy.js +201 -0
- package/cypress/e2e/2-advanced-examples/storage.cy.js +110 -0
- package/cypress/e2e/2-advanced-examples/traversal.cy.js +121 -0
- package/cypress/e2e/2-advanced-examples/utilities.cy.js +108 -0
- package/cypress/e2e/2-advanced-examples/viewport.cy.js +58 -0
- package/cypress/e2e/2-advanced-examples/waiting.cy.js +30 -0
- package/cypress/e2e/2-advanced-examples/window.cy.js +22 -0
- package/cypress/fixtures/example.json +5 -0
- package/cypress/support/commands.js +25 -0
- package/cypress/support/e2e.js +20 -0
- package/cypress.config.js +9 -0
- package/index.html +178 -0
- package/index.js.example +50 -0
- package/package.json +29 -0
- package/samples/react/README.md +70 -0
- package/samples/react/build/asset-manifest.json +16 -0
- package/samples/react/build/favicon.ico +0 -0
- package/samples/react/build/index.html +1 -0
- package/samples/react/build/logo192.png +0 -0
- package/samples/react/build/logo512.png +0 -0
- package/samples/react/build/manifest.json +25 -0
- package/samples/react/build/robots.txt +3 -0
- package/samples/react/build/static/css/main.073c9b0a.css +2 -0
- package/samples/react/build/static/css/main.073c9b0a.css.map +1 -0
- package/samples/react/build/static/js/787.b83ed06f.chunk.js +2 -0
- package/samples/react/build/static/js/787.b83ed06f.chunk.js.map +1 -0
- package/samples/react/build/static/js/main.0a848807.js +3 -0
- package/samples/react/build/static/js/main.0a848807.js.LICENSE.txt +39 -0
- package/samples/react/build/static/js/main.0a848807.js.map +1 -0
- package/samples/react/build/static/media/sdk-icons.b491623214b2af4cccdb.png +0 -0
- package/samples/react/package-lock.json +28973 -0
- package/samples/react/package.json +44 -0
- package/samples/react/public/favicon.ico +0 -0
- package/samples/react/public/index.html +43 -0
- package/samples/react/public/logo192.png +0 -0
- package/samples/react/public/logo512.png +0 -0
- package/samples/react/public/manifest.json +25 -0
- package/samples/react/public/robots.txt +3 -0
- package/samples/react/src/App.css +38 -0
- package/samples/react/src/App.js +22 -0
- package/samples/react/src/App.test.js +8 -0
- package/samples/react/src/assets/img/sdk-icons.png +0 -0
- package/samples/react/src/components/Cart.js +29 -0
- package/samples/react/src/components/ProductCard.js +27 -0
- package/samples/react/src/context/CartContext.js +116 -0
- package/samples/react/src/index.css +13 -0
- package/samples/react/src/index.js +17 -0
- package/samples/react/src/logo.svg +1 -0
- package/samples/react/src/reportWebVitals.js +13 -0
- package/samples/react/src/screens/Checkout.js +82 -0
- package/samples/react/src/screens/Store.js +21 -0
- package/samples/react/src/setupTests.js +5 -0
- package/samples/react/src/storeProducts.js +30 -0
- package/src/classes/3dsHandler.js +203 -0
- package/src/classes/checkout.js +125 -0
- package/src/classes/inlineCheckout.js +349 -0
- package/src/data/api.js +109 -0
- package/src/helpers/skyflow.js +139 -0
- package/src/helpers/styles.js +61 -0
- package/src/helpers/template.js +112 -0
- package/src/helpers/utils.js +68 -0
- package/src/index-dev.js +129 -0
- package/src/index.html +58 -0
- package/src/index.js +7 -0
- package/success.html +22 -0
- package/v1/bundle.min.js +18 -0
- package/webpack.config.js +66 -0
package/src/data/api.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export async function getOpenpayDeviceSessionID(merchant_id, public_key, signal) {
|
|
2
|
+
let openpay = await window.OpenPay;
|
|
3
|
+
openpay.setId(merchant_id);
|
|
4
|
+
openpay.setApiKey(public_key);
|
|
5
|
+
openpay.setSandboxMode(true);
|
|
6
|
+
var response = await openpay.deviceData.setup({signal});
|
|
7
|
+
return response;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function getBusiness(baseUrlTonder, apiKeyTonder, signal) {
|
|
11
|
+
const getBusiness = await fetch(
|
|
12
|
+
`${baseUrlTonder}/api/v1/payments/business/${apiKeyTonder}`,
|
|
13
|
+
{
|
|
14
|
+
headers: {
|
|
15
|
+
Authorization: `Token ${apiKeyTonder}`,
|
|
16
|
+
},
|
|
17
|
+
signal: signal,
|
|
18
|
+
}
|
|
19
|
+
);
|
|
20
|
+
const response = await getBusiness.json();
|
|
21
|
+
return response
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function customerRegister(baseUrlTonder, apiKeyTonder, customer, signal) {
|
|
25
|
+
const url = `${baseUrlTonder}/api/v1/customer/`;
|
|
26
|
+
const data = {
|
|
27
|
+
email: customer.email,
|
|
28
|
+
first_name: customer?.firstName,
|
|
29
|
+
last_name: customer?.lastName,
|
|
30
|
+
phone: customer?.phone,
|
|
31
|
+
}
|
|
32
|
+
const response = await fetch(url, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
Authorization: `Token ${apiKeyTonder}`,
|
|
37
|
+
},
|
|
38
|
+
signal: signal,
|
|
39
|
+
body: JSON.stringify(data),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (response.status === 201) {
|
|
43
|
+
const jsonResponse = await response.json();
|
|
44
|
+
return jsonResponse;
|
|
45
|
+
} else {
|
|
46
|
+
throw new Error(`Error: ${response.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function createOrder(baseUrlTonder, apiKeyTonder, orderItems) {
|
|
51
|
+
const url = `${baseUrlTonder}/api/v1/orders/`;
|
|
52
|
+
const data = orderItems;
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: {
|
|
56
|
+
"Content-Type": "application/json",
|
|
57
|
+
Authorization: `Token ${apiKeyTonder}`,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(data),
|
|
60
|
+
});
|
|
61
|
+
if (response.status === 201) {
|
|
62
|
+
const jsonResponse = await response.json();
|
|
63
|
+
return jsonResponse;
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(`Error: ${response.statusText}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function createPayment(baseUrlTonder, apiKeyTonder, paymentItems) {
|
|
70
|
+
const url = `${baseUrlTonder}/api/v1/business/${paymentItems.business_pk}/payments/`;
|
|
71
|
+
const data = paymentItems;
|
|
72
|
+
const response = await fetch(url, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
Authorization: `Token ${apiKeyTonder}`,
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify(data),
|
|
79
|
+
});
|
|
80
|
+
if (response.status >= 200 && response.status <=299) {
|
|
81
|
+
const jsonResponse = await response.json();
|
|
82
|
+
return jsonResponse;
|
|
83
|
+
} else {
|
|
84
|
+
throw new Error(`Error: ${response.statusText}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export async function startCheckoutRouter(baseUrlTonder, apiKeyTonder, routerItems) {
|
|
89
|
+
try {
|
|
90
|
+
const url = `${baseUrlTonder}/api/v1/checkout-router/`;
|
|
91
|
+
const data = routerItems;
|
|
92
|
+
const response = await fetch(url, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
Authorization: `Token ${apiKeyTonder}`,
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(data),
|
|
99
|
+
});
|
|
100
|
+
if (response.status >= 200 && response.status <= 299) {
|
|
101
|
+
const jsonResponse = await response.json();
|
|
102
|
+
return jsonResponse;
|
|
103
|
+
} else {
|
|
104
|
+
throw new Error("Failed to start checkout router")
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { defaultStyles } from "./styles";
|
|
2
|
+
|
|
3
|
+
export async function initSkyflow(
|
|
4
|
+
vaultId,
|
|
5
|
+
vaultUrl,
|
|
6
|
+
baseUrl,
|
|
7
|
+
apiKey,
|
|
8
|
+
signal,
|
|
9
|
+
customStyles = {}
|
|
10
|
+
) {
|
|
11
|
+
const skyflow = await Skyflow.init({
|
|
12
|
+
vaultID: vaultId,
|
|
13
|
+
vaultURL: vaultUrl,
|
|
14
|
+
getBearerToken: async () => {
|
|
15
|
+
// Pass the signal to the fetch call
|
|
16
|
+
const response = await fetch(`${baseUrl}/api/v1/vault-token/`, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'Authorization': `Token ${apiKey}`,
|
|
20
|
+
},
|
|
21
|
+
signal: signal,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (response.ok) {
|
|
25
|
+
const responseBody = await response.json();
|
|
26
|
+
return responseBody.token;
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error('Failed to retrieve bearer token');
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
options: {
|
|
32
|
+
logLevel: Skyflow.LogLevel.ERROR,
|
|
33
|
+
env: Skyflow.Env.DEV,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Create collect Container.
|
|
38
|
+
const collectContainer = await skyflow.container(
|
|
39
|
+
Skyflow.ContainerType.COLLECT
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Custom styles for collect elements.
|
|
43
|
+
var collectStylesOptions = Object.keys(customStyles).length === 0 ? defaultStyles : customStyles
|
|
44
|
+
|
|
45
|
+
const stylesForCardNumber = { ...collectStylesOptions.inputStyles.base };
|
|
46
|
+
stylesForCardNumber.textIndent = '44px';
|
|
47
|
+
|
|
48
|
+
const lengthMatchRule = {
|
|
49
|
+
type: Skyflow.ValidationRuleType.LENGTH_MATCH_RULE,
|
|
50
|
+
params: {
|
|
51
|
+
max: 70,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const cardHolderNameElement = await collectContainer.create({
|
|
56
|
+
table: "cards",
|
|
57
|
+
column: "cardholder_name",
|
|
58
|
+
...collectStylesOptions,
|
|
59
|
+
label: collectStylesOptions.labels?.nameLabel,
|
|
60
|
+
placeholder: collectStylesOptions.placeholders?.namePlaceholder,
|
|
61
|
+
type: Skyflow.ElementType.CARDHOLDER_NAME,
|
|
62
|
+
validations: [lengthMatchRule],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
cardHolderNameElement.setError('Inválido')
|
|
66
|
+
|
|
67
|
+
// Create collect elements.
|
|
68
|
+
const cardNumberElement = await collectContainer.create({
|
|
69
|
+
table: "cards",
|
|
70
|
+
column: "card_number",
|
|
71
|
+
...collectStylesOptions,
|
|
72
|
+
inputStyles: {
|
|
73
|
+
...collectStylesOptions.inputStyles,
|
|
74
|
+
base: stylesForCardNumber
|
|
75
|
+
},
|
|
76
|
+
label: collectStylesOptions.labels?.cardLabel,
|
|
77
|
+
placeholder: collectStylesOptions.placeholders?.cardPlaceholder,
|
|
78
|
+
type: Skyflow.ElementType.CARD_NUMBER,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
cardNumberElement.setError('Inválido')
|
|
82
|
+
|
|
83
|
+
const cvvElement = await collectContainer.create({
|
|
84
|
+
table: "cards",
|
|
85
|
+
column: "cvv",
|
|
86
|
+
...collectStylesOptions,
|
|
87
|
+
label: collectStylesOptions.labels?.cvvLabel,
|
|
88
|
+
placeholder: collectStylesOptions.placeholders?.cvvPlaceholder,
|
|
89
|
+
type: Skyflow.ElementType.CVV,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
cvvElement.setError('Inválido')
|
|
93
|
+
|
|
94
|
+
const expiryMonthElement = await collectContainer.create({
|
|
95
|
+
table: "cards",
|
|
96
|
+
column: "expiration_month",
|
|
97
|
+
...collectStylesOptions,
|
|
98
|
+
label: collectStylesOptions.labels?.expiryDateLabel,
|
|
99
|
+
placeholder: collectStylesOptions.placeholders?.expiryMonthPlaceholder,
|
|
100
|
+
type: Skyflow.ElementType.EXPIRATION_MONTH,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expiryMonthElement.setError('Inválido')
|
|
104
|
+
|
|
105
|
+
const expiryYearElement = await collectContainer.create({
|
|
106
|
+
table: "cards",
|
|
107
|
+
column: "expiration_year",
|
|
108
|
+
...collectStylesOptions,
|
|
109
|
+
label: "",
|
|
110
|
+
placeholder: collectStylesOptions.placeholders?.expiryYearPlaceholder,
|
|
111
|
+
type: Skyflow.ElementType.EXPIRATION_YEAR,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expiryYearElement.setError('Inválido')
|
|
115
|
+
|
|
116
|
+
await mountElements(
|
|
117
|
+
cardNumberElement,
|
|
118
|
+
cvvElement,
|
|
119
|
+
expiryMonthElement,
|
|
120
|
+
expiryYearElement,
|
|
121
|
+
cardHolderNameElement,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return collectContainer
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function mountElements(
|
|
128
|
+
cardNumberElement,
|
|
129
|
+
cvvElement,
|
|
130
|
+
expiryMonthElement,
|
|
131
|
+
expiryYearElement,
|
|
132
|
+
cardHolderNameElement,
|
|
133
|
+
) {
|
|
134
|
+
cardNumberElement.mount("#collectCardNumber");
|
|
135
|
+
cvvElement.mount("#collectCvv");
|
|
136
|
+
expiryMonthElement.mount("#collectExpirationMonth");
|
|
137
|
+
expiryYearElement.mount("#collectExpirationYear");
|
|
138
|
+
cardHolderNameElement.mount("#collectCardholderName");
|
|
139
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const defaultStyles = {
|
|
2
|
+
inputStyles: {
|
|
3
|
+
base: {
|
|
4
|
+
border: "1px solid #e0e0e0",
|
|
5
|
+
padding: "10px 7px",
|
|
6
|
+
borderRadius: "5px",
|
|
7
|
+
color: "#1d1d1d",
|
|
8
|
+
marginTop: "2px",
|
|
9
|
+
backgroundColor: "white",
|
|
10
|
+
fontFamily: '"Inter", sans-serif',
|
|
11
|
+
fontSize: '16px',
|
|
12
|
+
'&::placeholder': {
|
|
13
|
+
color: "#ccc",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
cardIcon: {
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
left: '6px',
|
|
19
|
+
bottom: 'calc(50% - 12px)',
|
|
20
|
+
},
|
|
21
|
+
complete: {
|
|
22
|
+
color: "#4caf50",
|
|
23
|
+
},
|
|
24
|
+
empty: {},
|
|
25
|
+
focus: {},
|
|
26
|
+
invalid: {
|
|
27
|
+
border: "1px solid #f44336",
|
|
28
|
+
},
|
|
29
|
+
global: {
|
|
30
|
+
'@import': 'url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap")',
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
labelStyles: {
|
|
34
|
+
base: {
|
|
35
|
+
fontSize: '12px',
|
|
36
|
+
fontWeight: '500',
|
|
37
|
+
fontFamily: '"Inter", sans-serif'
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
errorTextStyles: {
|
|
41
|
+
base: {
|
|
42
|
+
fontSize: '12px',
|
|
43
|
+
fontWeight: '500',
|
|
44
|
+
color: "#f44336",
|
|
45
|
+
fontFamily: '"Inter", sans-serif'
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
labels: {
|
|
49
|
+
nameLabel: 'Titular de la tarjeta',
|
|
50
|
+
cardLabel: 'Número de tarjeta',
|
|
51
|
+
cvvLabel: 'CVC/CVV',
|
|
52
|
+
expiryDateLabel: 'Fecha de expiración',
|
|
53
|
+
},
|
|
54
|
+
placeholders: {
|
|
55
|
+
namePlaceholder: 'Nombre como aparece en la tarjeta',
|
|
56
|
+
cardPlaceholder: '1234 1234 1234 1234',
|
|
57
|
+
cvvPlaceholder: '3-4 dígitos',
|
|
58
|
+
expiryMonthPlaceholder: 'MM',
|
|
59
|
+
expiryYearPlaceholder: 'AA'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export const cardTemplate = `
|
|
2
|
+
<div class="container-tonder">
|
|
3
|
+
<div id="collectCardholderName" class="empty-div"></div>
|
|
4
|
+
<div id="collectCardNumber" class="empty-div"></div>
|
|
5
|
+
<div class="collect-row">
|
|
6
|
+
<div id="collectExpirationMonth" class="empty-div"></div>
|
|
7
|
+
<div id="collectExpirationYear" class="expiration-year"></div>
|
|
8
|
+
<div id="collectCvv" class="empty-div"></div>
|
|
9
|
+
</div>
|
|
10
|
+
<div id="msgError"></div>
|
|
11
|
+
<button id="tonderPayButton" class="pay-button">Pagar</button>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<style>
|
|
15
|
+
.container-tonder {
|
|
16
|
+
background-color: #F9F9F9;
|
|
17
|
+
margin: 0 auto !important;
|
|
18
|
+
padding: 30px 10px 30px 10px;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
transition: max-height 0.5s ease-out;
|
|
21
|
+
max-width: 600px;
|
|
22
|
+
border: solid 1px #e3e3e3;
|
|
23
|
+
max-height: 100vh;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.collect-row {
|
|
27
|
+
display: flex !important;
|
|
28
|
+
justify-content: space-between !important;
|
|
29
|
+
width: 100% !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.collect-row > :first-child {
|
|
33
|
+
min-width: 120px; !important
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.expiration-year {
|
|
37
|
+
padding-top: 25px !important;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.empty-div {
|
|
41
|
+
height: 80px !important;
|
|
42
|
+
margin-top: 2px;
|
|
43
|
+
margin-bottom: 4px;
|
|
44
|
+
margin-left: 10px !important;
|
|
45
|
+
margin-right: 10px !important;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.error-container{
|
|
49
|
+
color: red !important;
|
|
50
|
+
background-color: #FFDBDB !important;
|
|
51
|
+
margin-bottom: 13px !important;
|
|
52
|
+
font-size: 80% !important;
|
|
53
|
+
padding: 8px 10px !important;
|
|
54
|
+
border-radius: 10px !important;
|
|
55
|
+
text-align: left !important;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.pay-button {
|
|
59
|
+
font-size: 16px;
|
|
60
|
+
font-weight: bold;
|
|
61
|
+
min-height: 2.3rem;
|
|
62
|
+
border-radius: 0.5rem;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
width: 100%;
|
|
65
|
+
padding: 1rem;
|
|
66
|
+
text-align: center;
|
|
67
|
+
border: none;
|
|
68
|
+
background-color: #000;
|
|
69
|
+
color: #fff;
|
|
70
|
+
margin-bottom: 10px;
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.lds-dual-ring {
|
|
75
|
+
display: inline-block;
|
|
76
|
+
width: 14px;
|
|
77
|
+
height: 14px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.lds-dual-ring:after {
|
|
81
|
+
content: " ";
|
|
82
|
+
display: block;
|
|
83
|
+
width: 14px;
|
|
84
|
+
height: 14px;
|
|
85
|
+
border-radius: 50%;
|
|
86
|
+
border: 6px solid #fff;
|
|
87
|
+
border-color: #fff transparent #fff transparent;
|
|
88
|
+
animation: lds-dual-ring 1.2s linear infinite;
|
|
89
|
+
}
|
|
90
|
+
@keyframes lds-dual-ring {
|
|
91
|
+
0% {
|
|
92
|
+
transform: rotate(0deg);
|
|
93
|
+
}
|
|
94
|
+
100% {
|
|
95
|
+
transform: rotate(360deg);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@media screen and (max-width: 600px) {
|
|
101
|
+
.payment_method_zplit {
|
|
102
|
+
font-size: 16px !important;
|
|
103
|
+
width: 100% !important;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.payment_method_zplit label img {
|
|
107
|
+
display: none !important;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
</style>
|
|
112
|
+
`
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export async function addScripts() {
|
|
2
|
+
try {
|
|
3
|
+
const skyflowScript = document.createElement("script");
|
|
4
|
+
skyflowScript.src = "https://js.skyflow.com/v1/index.js";
|
|
5
|
+
await new Promise((resolve, reject) => {
|
|
6
|
+
skyflowScript.onload = resolve;
|
|
7
|
+
skyflowScript.onerror = reject;
|
|
8
|
+
document.head.appendChild(skyflowScript);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const openPay1Script = document.createElement("script");
|
|
12
|
+
openPay1Script.src = "https://openpay.s3.amazonaws.com/openpay.v1.min.js";
|
|
13
|
+
await new Promise((resolve, reject) => {
|
|
14
|
+
openPay1Script.onload = resolve;
|
|
15
|
+
openPay1Script.onerror = reject;
|
|
16
|
+
document.head.appendChild(openPay1Script);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const openPay2Script = document.createElement("script");
|
|
20
|
+
openPay2Script.src = "https://openpay.s3.amazonaws.com/openpay-data.v1.min.js";
|
|
21
|
+
await new Promise((resolve, reject) => {
|
|
22
|
+
openPay2Script.onload = resolve;
|
|
23
|
+
openPay2Script.onerror = reject;
|
|
24
|
+
document.head.appendChild(openPay2Script);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error("Error loading scripts", error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function toCurrency(value) {
|
|
33
|
+
if (isNaN(parseFloat(value))) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
var formatter = new Intl.NumberFormat("es-MX", {
|
|
37
|
+
style: "currency",
|
|
38
|
+
currency: "MXN",
|
|
39
|
+
minimumFractionDigits: 2
|
|
40
|
+
});
|
|
41
|
+
return formatter.format(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function showError(message) {
|
|
45
|
+
var msgErrorDiv = document.getElementById("msgError");
|
|
46
|
+
msgErrorDiv.classList.add("error-container");
|
|
47
|
+
msgErrorDiv.innerHTML = message;
|
|
48
|
+
setTimeout(function () {
|
|
49
|
+
try {
|
|
50
|
+
document.querySelector("#tonderPayButton").disabled = false;
|
|
51
|
+
} catch (error) {}
|
|
52
|
+
msgErrorDiv.classList.remove("error-container");
|
|
53
|
+
msgErrorDiv.innerHTML = "";
|
|
54
|
+
}, 3000);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function getBrowserInfo() {
|
|
58
|
+
const browserInfo = {
|
|
59
|
+
javascript_enabled: true, // Assumed since JavaScript is running
|
|
60
|
+
time_zone: new Date().getTimezoneOffset(),
|
|
61
|
+
language: navigator.language || 'en-US', // Fallback to 'en-US'
|
|
62
|
+
color_depth: window.screen ? window.screen.colorDepth : null,
|
|
63
|
+
screen_width: window.screen ? window.screen.width * window.devicePixelRatio || window.screen.width : null,
|
|
64
|
+
screen_height: window.screen ? window.screen.height * window.devicePixelRatio || window.screen.height : null,
|
|
65
|
+
user_agent: navigator.userAgent,
|
|
66
|
+
};
|
|
67
|
+
return browserInfo;
|
|
68
|
+
}
|
package/src/index-dev.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { InlineCheckout } from './classes/inlineCheckout'
|
|
2
|
+
|
|
3
|
+
const customStyles = {
|
|
4
|
+
inputStyles: {
|
|
5
|
+
base: {
|
|
6
|
+
border: "2px dashed #4a90e2",
|
|
7
|
+
padding: "12px 8px",
|
|
8
|
+
borderRadius: "8px",
|
|
9
|
+
color: "#333333",
|
|
10
|
+
backgroundColor: "#f0f0f0",
|
|
11
|
+
fontFamily: '"Arial", sans-serif',
|
|
12
|
+
fontSize: '14px',
|
|
13
|
+
'&::placeholder': {
|
|
14
|
+
color: "#888888",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
cardIcon: {
|
|
18
|
+
position: 'absolute',
|
|
19
|
+
left: '6px',
|
|
20
|
+
bottom: 'calc(50% - 12px)',
|
|
21
|
+
},
|
|
22
|
+
complete: {
|
|
23
|
+
color: "#4caf50",
|
|
24
|
+
},
|
|
25
|
+
empty: {},
|
|
26
|
+
focus: {},
|
|
27
|
+
invalid: {
|
|
28
|
+
border: "1px solid #f44336",
|
|
29
|
+
},
|
|
30
|
+
global: {
|
|
31
|
+
'@import': 'url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap")',
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
labelStyles: {
|
|
35
|
+
base: {
|
|
36
|
+
fontSize: '14px',
|
|
37
|
+
fontWeight: 'bold',
|
|
38
|
+
fontFamily: '"Inter", sans-serif',
|
|
39
|
+
color: "#4a90e2",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
errorTextStyles: {
|
|
43
|
+
base: {
|
|
44
|
+
fontSize: '12px',
|
|
45
|
+
fontWeight: '500',
|
|
46
|
+
color: "#e74c3c",
|
|
47
|
+
fontFamily: '"Inter", sans-serif',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
labels: {
|
|
51
|
+
nameLabel: 'Nombre de la de Tarjeta',
|
|
52
|
+
cardLabel: 'Número de Tarjeta',
|
|
53
|
+
cvvLabel: 'Código de Seguridad',
|
|
54
|
+
expiryDateLabel: 'Fecha de Expiración',
|
|
55
|
+
},
|
|
56
|
+
placeholders: {
|
|
57
|
+
namePlaceholder: 'Nombre como aparece en la tarjeta',
|
|
58
|
+
cardPlaceholder: '0000 0000 0000 0000',
|
|
59
|
+
cvvPlaceholder: '123',
|
|
60
|
+
expiryMonthPlaceholder: 'Mes',
|
|
61
|
+
expiryYearPlaceholder: 'Año'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const checkoutData = {
|
|
66
|
+
customer: {
|
|
67
|
+
firstName: "Adrian",
|
|
68
|
+
lastName: "Martinez",
|
|
69
|
+
country: "Mexico",
|
|
70
|
+
address: "Pinos 507, Col El Tecuan",
|
|
71
|
+
city: "Durango",
|
|
72
|
+
state: "Durango",
|
|
73
|
+
postCode: "34105",
|
|
74
|
+
email: "adrian@email.com",
|
|
75
|
+
phone: "8161234567",
|
|
76
|
+
},
|
|
77
|
+
currency: 'mxn',
|
|
78
|
+
cart: {
|
|
79
|
+
total: 399,
|
|
80
|
+
items: [
|
|
81
|
+
{
|
|
82
|
+
description: "Black T-Shirt",
|
|
83
|
+
quantity: 1,
|
|
84
|
+
price_unit: 1,
|
|
85
|
+
discount: 0,
|
|
86
|
+
taxes: 0,
|
|
87
|
+
product_reference: 1,
|
|
88
|
+
name: "T-Shirt",
|
|
89
|
+
amount_total: 399,
|
|
90
|
+
},
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
card: { "skyflow_id": "53ca875c-16fd-4395-8ac9-c756613dbaf9" },
|
|
94
|
+
metadata: {
|
|
95
|
+
order_id: 123456
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// localhost
|
|
100
|
+
const apiKey = "4c87c36e697e65ddfe288be0afbe7967ea0ab865";
|
|
101
|
+
const returnUrl = "http://127.0.0.1:8080/"
|
|
102
|
+
const successUrl = "http://127.0.0.1:8080/success"
|
|
103
|
+
// stage
|
|
104
|
+
// const apiKey = "8365683bdc33dd6d50fe2397188d79f1a6765852";
|
|
105
|
+
|
|
106
|
+
const inlineCheckout = new InlineCheckout({
|
|
107
|
+
apiKey,
|
|
108
|
+
returnUrl,
|
|
109
|
+
successUrl,
|
|
110
|
+
styles: customStyles
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
inlineCheckout.injectCheckout();
|
|
114
|
+
|
|
115
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
116
|
+
const payButton = document.getElementById('pay-button');
|
|
117
|
+
payButton.addEventListener('click', async function() {
|
|
118
|
+
try {
|
|
119
|
+
payButton.textContent = 'Procesando...';
|
|
120
|
+
const response = await inlineCheckout.payment(checkoutData);
|
|
121
|
+
console.log(response)
|
|
122
|
+
alert('Pago realizado con éxito');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
alert('Error al realizar el pago')
|
|
125
|
+
} finally {
|
|
126
|
+
payButton.textContent = 'Pagar';
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
package/src/index.html
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<style>
|
|
8
|
+
#checkout-container {
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
}
|
|
12
|
+
#pay-button {
|
|
13
|
+
padding: 10px 20px;
|
|
14
|
+
margin-top: 30px;
|
|
15
|
+
background-color: #4a90e2;
|
|
16
|
+
color: white;
|
|
17
|
+
border: none;
|
|
18
|
+
border-radius: 5px;
|
|
19
|
+
font-size: 16px;
|
|
20
|
+
cursor: pointer;
|
|
21
|
+
transition: background-color 0.3s;
|
|
22
|
+
}
|
|
23
|
+
#payButton:hover {
|
|
24
|
+
background-color: #357abd;
|
|
25
|
+
}
|
|
26
|
+
.cart-details {
|
|
27
|
+
font-size: 3rem;
|
|
28
|
+
font-weight: bold;
|
|
29
|
+
padding: 2rem;
|
|
30
|
+
display: flex;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
}
|
|
33
|
+
.button-container {
|
|
34
|
+
width: 100%;
|
|
35
|
+
display: flex;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
|
|
41
|
+
<body>
|
|
42
|
+
<div class="checkout-container">
|
|
43
|
+
<div class="cart-details">
|
|
44
|
+
<span>Total </span>
|
|
45
|
+
<div id="cart-total">$399</div>
|
|
46
|
+
</div>
|
|
47
|
+
<form id="payment-form">
|
|
48
|
+
<div id="tonder-checkout"></div>
|
|
49
|
+
</form>
|
|
50
|
+
<div class="button-container">
|
|
51
|
+
<button id="pay-button">Pagar</button>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</body>
|
|
55
|
+
<script src="https://js.skyflow.com/v1/index.js"></script>
|
|
56
|
+
<script src="https://openpay.s3.amazonaws.com/openpay.v1.min.js"></script>
|
|
57
|
+
<script src="https://openpay.s3.amazonaws.com/openpay-data.v1.min.js"></script>
|
|
58
|
+
</html>
|
package/src/index.js
ADDED
package/success.html
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Pago Completado</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
font-family: Arial, sans-serif;
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
align-items: center;
|
|
13
|
+
height: 100vh;
|
|
14
|
+
margin: 0;
|
|
15
|
+
color: #333;
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
</head>
|
|
19
|
+
<body>
|
|
20
|
+
<h1>Pago completado con éxito</h1>
|
|
21
|
+
</body>
|
|
22
|
+
</html>
|