@gudhub/ssg-web-components-library 1.0.116 → 1.0.118
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/package.json +1 -1
- package/src/components/get-in-touch-form/get-in-touch-form.html +0 -3
- package/src/components/get-in-touch-form/get-in-touch-form.js +16 -72
- package/src/components/get-in-touch-form/get-in-touch-form.scss +0 -12
- package/src/components/liqpay-component/config.js +6 -0
- package/src/components/liqpay-component/liqpay-component.html +91 -0
- package/src/components/liqpay-component/liqpay-component.js +197 -0
- package/src/components/liqpay-component/liqpay-component.json +14 -0
- package/src/components/liqpay-component/liqpay-component.md +0 -0
- package/src/components/liqpay-component/liqpay-component.scss +337 -0
- package/src/components/liqpay-payment-button/config.js +6 -0
- package/src/components/liqpay-payment-button/liqpay-payment-button.html +5 -0
- package/src/components/liqpay-payment-button/liqpay-payment-button.js +23 -0
- package/src/components/liqpay-payment-button/liqpay-payment-button.json +4 -0
- package/src/components/liqpay-payment-button/liqpay-payment-button.md +0 -0
- package/src/components/liqpay-payment-button/liqpay-payment-button.scss +7 -0
- package/src/components/masonry-gallery/masonry-gallery.js +68 -24
- package/src/components/video-slider/animations/cascade-strips.js +89 -0
- package/src/components/video-slider/animations/fade.js +31 -0
- package/src/components/video-slider/animations/multi-strip.js +66 -0
- package/src/components/video-slider/animations/sectors.js +104 -0
- package/src/components/video-slider/animations/wipe-left.js +43 -0
- package/src/components/video-slider/animations/zoom.js +51 -0
- package/src/components/video-slider/config.js +6 -0
- package/src/components/video-slider/utils/draw-image-cover.js +27 -0
- package/src/components/video-slider/utils/easing.js +5 -0
- package/src/components/video-slider/video-slider.html +5 -0
- package/src/components/video-slider/video-slider.js +177 -0
- package/src/components/video-slider/video-slider.readme.md +218 -0
- package/src/components/video-slider/video-slider.scss +16 -0
- package/src/config.js +1 -0
package/package.json
CHANGED
|
@@ -10,9 +10,6 @@
|
|
|
10
10
|
<button type="submit" class="btn">${buttonText ? buttonText : 'Get in touch'}</button>
|
|
11
11
|
<div class="loader"></div>
|
|
12
12
|
</div>
|
|
13
|
-
<div class="recaptcha-notice">
|
|
14
|
-
This site is protected by reCAPTCHA.
|
|
15
|
-
</div>
|
|
16
13
|
</form>
|
|
17
14
|
<div class="overflow success">
|
|
18
15
|
<div class="icon_wrapper">
|
|
@@ -5,7 +5,6 @@ import defaultConfigs from './get-in-touch-form-data.json';
|
|
|
5
5
|
import { checkInputsValidations } from './inputsValidation.js';
|
|
6
6
|
|
|
7
7
|
let recaptchaPromise = null;
|
|
8
|
-
let recaptchaBadgeObserver = null;
|
|
9
8
|
|
|
10
9
|
function loadRecaptcha(siteKey) {
|
|
11
10
|
if (!siteKey) {
|
|
@@ -36,41 +35,16 @@ function loadRecaptcha(siteKey) {
|
|
|
36
35
|
return recaptchaPromise;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
|
-
function hideRecaptchaBadge() {
|
|
40
|
-
const badge = document.querySelector('.grecaptcha-badge');
|
|
41
|
-
|
|
42
|
-
if (!badge) return;
|
|
43
|
-
|
|
44
|
-
badge.style.setProperty('visibility', 'hidden', 'important');
|
|
45
|
-
badge.style.setProperty('opacity', '0', 'important');
|
|
46
|
-
badge.style.setProperty('pointer-events', 'none', 'important');
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function observeRecaptchaBadge() {
|
|
50
|
-
hideRecaptchaBadge();
|
|
51
|
-
|
|
52
|
-
if (recaptchaBadgeObserver) return;
|
|
53
|
-
|
|
54
|
-
recaptchaBadgeObserver = new MutationObserver(() => {
|
|
55
|
-
hideRecaptchaBadge();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
recaptchaBadgeObserver.observe(document.body, {
|
|
59
|
-
childList: true,
|
|
60
|
-
subtree: true
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
38
|
class GetInTouchForm extends GHComponent {
|
|
65
39
|
constructor() {
|
|
66
40
|
super();
|
|
67
41
|
|
|
68
|
-
this.formId = this.getAttribute(
|
|
42
|
+
this.formId = this.getAttribute("data-form-id");
|
|
69
43
|
this.defaultConfigs = defaultConfigs;
|
|
70
44
|
|
|
71
45
|
this.generateInput = this.generateInput;
|
|
72
46
|
this.isFormSubmitted = false;
|
|
73
|
-
|
|
47
|
+
|
|
74
48
|
this.placement = 'main';
|
|
75
49
|
this.config = window.getConfig()?.componentsConfigs?.formConfig || window.getConfig()?.formConfig;
|
|
76
50
|
|
|
@@ -92,11 +66,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
92
66
|
this.attachEventListeners();
|
|
93
67
|
|
|
94
68
|
if (this.recaptcha_site_key) {
|
|
95
|
-
loadRecaptcha(this.recaptcha_site_key)
|
|
96
|
-
.then(() => {
|
|
97
|
-
observeRecaptchaBadge();
|
|
98
|
-
})
|
|
99
|
-
.catch(console.error);
|
|
69
|
+
loadRecaptcha(this.recaptcha_site_key).catch(console.error);
|
|
100
70
|
}
|
|
101
71
|
}
|
|
102
72
|
}
|
|
@@ -106,26 +76,15 @@ class GetInTouchForm extends GHComponent {
|
|
|
106
76
|
super.render(html);
|
|
107
77
|
this.attachEventListeners();
|
|
108
78
|
|
|
109
|
-
|
|
110
|
-
loadRecaptcha(this.recaptcha_site_key)
|
|
111
|
-
.then(() => {
|
|
112
|
-
observeRecaptchaBadge();
|
|
113
|
-
})
|
|
114
|
-
.catch(console.error);
|
|
115
|
-
}
|
|
79
|
+
loadRecaptcha(this.recaptcha_site_key).catch(console.error);
|
|
116
80
|
}
|
|
117
81
|
|
|
118
82
|
attachEventListeners() {
|
|
119
|
-
|
|
120
|
-
|
|
83
|
+
this.getElementsByTagName('form')[0]
|
|
84
|
+
.addEventListener('submit', (e) => this.handleSubmit(e));
|
|
121
85
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (restartButton) {
|
|
127
|
-
restartButton.addEventListener('click', () => this.hideFail());
|
|
128
|
-
}
|
|
86
|
+
this.getElementsByClassName('restart_button')[0]
|
|
87
|
+
.addEventListener('click', () => this.hideFail());
|
|
129
88
|
}
|
|
130
89
|
|
|
131
90
|
onParentPopupClose() {
|
|
@@ -140,7 +99,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
140
99
|
initConfig(formConfigs) {
|
|
141
100
|
try {
|
|
142
101
|
this.config = formConfigs.find(({ id }) => id === this.formId);
|
|
143
|
-
if (!this.config) throw new Error(
|
|
102
|
+
if (!this.config) throw new Error("Config not found");
|
|
144
103
|
} catch {
|
|
145
104
|
const defaultId = this.isInPopup ? 'default popup' : 'default';
|
|
146
105
|
this.config = defaultConfigs.find(({ id }) => id === defaultId);
|
|
@@ -148,7 +107,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
148
107
|
|
|
149
108
|
this.titleName = this.getAttribute('data-form-title') || this.config.title;
|
|
150
109
|
this.subtitleName = this.getAttribute('data-form-subtitle') || this.config.subtitle;
|
|
151
|
-
this.placement = this.getAttribute('data-form-placement') ||
|
|
110
|
+
this.placement = this.getAttribute('data-form-placement') || "main";
|
|
152
111
|
this.buttonText = this.getAttribute('data-form-button-text') || this.config.button_text;
|
|
153
112
|
}
|
|
154
113
|
|
|
@@ -162,8 +121,6 @@ class GetInTouchForm extends GHComponent {
|
|
|
162
121
|
|
|
163
122
|
return new Promise((resolve, reject) => {
|
|
164
123
|
grecaptcha.ready(() => {
|
|
165
|
-
hideRecaptchaBadge();
|
|
166
|
-
|
|
167
124
|
grecaptcha.execute(this.recaptcha_site_key, { action })
|
|
168
125
|
.then(resolve)
|
|
169
126
|
.catch(reject);
|
|
@@ -189,6 +146,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
189
146
|
}
|
|
190
147
|
|
|
191
148
|
this.handleSuccessFormValidation(form, token);
|
|
149
|
+
|
|
192
150
|
} catch (error) {
|
|
193
151
|
console.error(error);
|
|
194
152
|
this.showFail();
|
|
@@ -221,7 +179,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
221
179
|
inputsValidation = async (form) => {
|
|
222
180
|
const inputs = Array.from(form.querySelectorAll('input'));
|
|
223
181
|
return checkInputsValidations(inputs);
|
|
224
|
-
}
|
|
182
|
+
}
|
|
225
183
|
|
|
226
184
|
verifyRecaptcha = async (recaptchaToken) => {
|
|
227
185
|
const response = await fetch('/api/verify-recaptcha', {
|
|
@@ -242,7 +200,7 @@ class GetInTouchForm extends GHComponent {
|
|
|
242
200
|
}
|
|
243
201
|
|
|
244
202
|
return data;
|
|
245
|
-
}
|
|
203
|
+
}
|
|
246
204
|
|
|
247
205
|
createDataObject(form, placement, recaptchaToken) {
|
|
248
206
|
const inputs = {};
|
|
@@ -272,24 +230,13 @@ class GetInTouchForm extends GHComponent {
|
|
|
272
230
|
|
|
273
231
|
addLoader() {
|
|
274
232
|
this.classList.add('loading');
|
|
275
|
-
|
|
276
|
-
const btn = this.querySelector('button[type="submit"]');
|
|
277
|
-
|
|
278
|
-
if (btn) {
|
|
279
|
-
btn.disabled = true;
|
|
280
|
-
}
|
|
233
|
+
this.querySelector('button[type="submit"]').disabled = true;
|
|
281
234
|
}
|
|
282
235
|
|
|
283
236
|
removeLoader() {
|
|
284
237
|
this.classList.remove('loading');
|
|
285
|
-
|
|
286
238
|
const btn = this.querySelector('button[type="submit"]');
|
|
287
|
-
|
|
288
|
-
if (btn) {
|
|
289
|
-
setTimeout(() => {
|
|
290
|
-
btn.disabled = false;
|
|
291
|
-
}, 500);
|
|
292
|
-
}
|
|
239
|
+
setTimeout(() => btn.disabled = false, 500);
|
|
293
240
|
}
|
|
294
241
|
|
|
295
242
|
showSuccess({ email, phone }) {
|
|
@@ -331,9 +278,6 @@ class GetInTouchForm extends GHComponent {
|
|
|
331
278
|
|
|
332
279
|
hideFail() {
|
|
333
280
|
const el = this.querySelector('.overflow.fail');
|
|
334
|
-
|
|
335
|
-
if (!el) return;
|
|
336
|
-
|
|
337
281
|
el.style.opacity = 0;
|
|
338
282
|
|
|
339
283
|
setTimeout(() => {
|
|
@@ -371,4 +315,4 @@ class GetInTouchForm extends GHComponent {
|
|
|
371
315
|
|
|
372
316
|
if (!customElements.get('get-in-touch-form')) {
|
|
373
317
|
customElements.define('get-in-touch-form', GetInTouchForm);
|
|
374
|
-
}
|
|
318
|
+
}
|
|
@@ -271,12 +271,6 @@ get-in-touch-form {
|
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
|
-
.recaptcha-notice {
|
|
275
|
-
margin-top: 10px;
|
|
276
|
-
font-size: 12px;
|
|
277
|
-
line-height: 1.4;
|
|
278
|
-
opacity: 0.7;
|
|
279
|
-
}
|
|
280
274
|
&[data-in-popup] {
|
|
281
275
|
.get-in-touch-form {
|
|
282
276
|
padding-top: 0;
|
|
@@ -374,10 +368,4 @@ popup-container[data-position="bottom-right"] {
|
|
|
374
368
|
}
|
|
375
369
|
}
|
|
376
370
|
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
body .grecaptcha-badge {
|
|
380
|
-
visibility: hidden !important;
|
|
381
|
-
opacity: 0 !important;
|
|
382
|
-
pointer-events: none !important;
|
|
383
371
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<section class="liqpay-component">
|
|
2
|
+
<!-- <div class="liqpay-component__logo">
|
|
3
|
+
<div class="liqpay-component__logo-badge">
|
|
4
|
+
<div class="liqpay-component__logo-badge-text">
|
|
5
|
+
<span class="liqpay-component__logo-gud">GUD</span>
|
|
6
|
+
<span class="liqpay-component__logo-hub">HUB</span>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
<span class="liqpay-component__logo-crm">CRM</span>
|
|
10
|
+
</div> -->
|
|
11
|
+
|
|
12
|
+
<h1 class="liqpay-component__title" data-liqpay-title></h1>
|
|
13
|
+
|
|
14
|
+
<p class="liqpay-component__subtitle" data-liqpay-subtitle></p>
|
|
15
|
+
|
|
16
|
+
<div class="liqpay-component__card">
|
|
17
|
+
<div class="liqpay-component__amount-row">
|
|
18
|
+
<div>
|
|
19
|
+
<p class="liqpay-component__amount-label">Amount</p>
|
|
20
|
+
<p class="liqpay-component__amount" data-liqpay-amount></p>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="liqpay-component__secured-badge">
|
|
23
|
+
<div class="liqpay-component__secured-icon" aria-hidden="true">
|
|
24
|
+
<svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
25
|
+
<polyline points="2,5 4,7.5 8,3" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
26
|
+
</svg>
|
|
27
|
+
</div>
|
|
28
|
+
<span class="liqpay-component__secured-text">Secured</span>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div class="liqpay-component__divider"></div>
|
|
33
|
+
|
|
34
|
+
<div class="liqpay-component__summary">
|
|
35
|
+
<div class="liqpay-component__summary-row">
|
|
36
|
+
<span class="liqpay-component__summary-label">Description</span>
|
|
37
|
+
<strong class="liqpay-component__summary-value" data-liqpay-description></strong>
|
|
38
|
+
</div>
|
|
39
|
+
|
|
40
|
+
<div class="liqpay-component__summary-row" data-liqpay-order-row>
|
|
41
|
+
<span class="liqpay-component__summary-label">Order ID</span>
|
|
42
|
+
<strong class="liqpay-component__summary-value" data-liqpay-order-id></strong>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<p class="liqpay-component__text" data-liqpay-text></p>
|
|
47
|
+
|
|
48
|
+
<form
|
|
49
|
+
class="liqpay-component__form"
|
|
50
|
+
method="POST"
|
|
51
|
+
accept-charset="utf-8"
|
|
52
|
+
data-liqpay-form
|
|
53
|
+
>
|
|
54
|
+
<input type="hidden" name="data" data-liqpay-data>
|
|
55
|
+
<input type="hidden" name="signature" data-liqpay-signature>
|
|
56
|
+
|
|
57
|
+
<button class="liqpay-component__button" type="button" data-liqpay-button>
|
|
58
|
+
<svg class="liqpay-component__button-icon" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
59
|
+
<rect x="2" y="7" width="14" height="10" rx="2" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
60
|
+
<path d="M6 7V5a3 3 0 016 0v2" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
61
|
+
</svg>
|
|
62
|
+
<span data-liqpay-button-text></span>
|
|
63
|
+
</button>
|
|
64
|
+
</form>
|
|
65
|
+
|
|
66
|
+
<p class="liqpay-component__error" data-liqpay-error hidden></p>
|
|
67
|
+
|
|
68
|
+
<p class="liqpay-component__powered">
|
|
69
|
+
Powered by <span class="liqpay-component__powered-brand">LiqPay</span>
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="liqpay-component__security">
|
|
74
|
+
<div class="liqpay-component__security-item">
|
|
75
|
+
<div class="liqpay-component__security-icon" aria-hidden="true">
|
|
76
|
+
<svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
77
|
+
<polyline points="2,5 4,7.5 8,3" stroke="#16a34a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
78
|
+
</svg>
|
|
79
|
+
</div>
|
|
80
|
+
<span>256-bit Encryption</span>
|
|
81
|
+
</div>
|
|
82
|
+
<div class="liqpay-component__security-item">
|
|
83
|
+
<div class="liqpay-component__security-icon" aria-hidden="true">
|
|
84
|
+
<svg viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
85
|
+
<polyline points="2,5 4,7.5 8,3" stroke="#16a34a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
86
|
+
</svg>
|
|
87
|
+
</div>
|
|
88
|
+
<span>PCI DSS Compliant</span>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</section>
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import html from './liqpay-component.html';
|
|
2
|
+
import './liqpay-component.scss';
|
|
3
|
+
import jsonTemplate from './liqpay-component.json';
|
|
4
|
+
|
|
5
|
+
class LiqpayComponent extends window.GHComponent {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
super.setDefaultData(jsonTemplate);
|
|
9
|
+
|
|
10
|
+
this.isLoading = false;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async onServerRender() {
|
|
14
|
+
super.render(html);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
onClientRender() {
|
|
18
|
+
this.initComponent();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
initComponent() {
|
|
22
|
+
this.titleElement = this.querySelector('[data-liqpay-title]');
|
|
23
|
+
this.subtitleElement = this.querySelector('[data-liqpay-subtitle]');
|
|
24
|
+
this.amountElement = this.querySelector('[data-liqpay-amount]');
|
|
25
|
+
this.descriptionElement = this.querySelector('[data-liqpay-description]');
|
|
26
|
+
this.textElement = this.querySelector('[data-liqpay-text]');
|
|
27
|
+
this.orderRowElement = this.querySelector('[data-liqpay-order-row]');
|
|
28
|
+
this.orderIdElement = this.querySelector('[data-liqpay-order-id]');
|
|
29
|
+
this.formElement = this.querySelector('[data-liqpay-form]');
|
|
30
|
+
this.dataInput = this.querySelector('[data-liqpay-data]');
|
|
31
|
+
this.signatureInput = this.querySelector('[data-liqpay-signature]');
|
|
32
|
+
this.buttonElement = this.querySelector('[data-liqpay-button]');
|
|
33
|
+
this.buttonTextElement = this.querySelector('[data-liqpay-button-text]');
|
|
34
|
+
this.errorElement = this.querySelector('[data-liqpay-error]');
|
|
35
|
+
|
|
36
|
+
this.renderPaymentInfo();
|
|
37
|
+
this.bindEvents();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getConfig() {
|
|
41
|
+
return {
|
|
42
|
+
...jsonTemplate,
|
|
43
|
+
...(this.json || {})
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
renderPaymentInfo() {
|
|
48
|
+
const config = this.getConfig();
|
|
49
|
+
|
|
50
|
+
console.log("CONFIG:", config);
|
|
51
|
+
|
|
52
|
+
const amount = Number(config.amount || 0);
|
|
53
|
+
const currency = config.currency || 'UAH';
|
|
54
|
+
|
|
55
|
+
this.titleElement.textContent = config.title || '';
|
|
56
|
+
this.subtitleElement.textContent = config.subtitle || '';
|
|
57
|
+
this.descriptionElement.textContent = config.payment_description || '';
|
|
58
|
+
this.textElement.textContent = config.description || '';
|
|
59
|
+
this.amountElement.textContent = this.formatAmount(amount, currency);
|
|
60
|
+
this.buttonTextElement.textContent = config.button_text || 'Сплатити';
|
|
61
|
+
|
|
62
|
+
if (config.order_id) {
|
|
63
|
+
this.orderIdElement.textContent = config.order_id;
|
|
64
|
+
this.orderRowElement.hidden = false;
|
|
65
|
+
} else {
|
|
66
|
+
this.orderIdElement.textContent = '';
|
|
67
|
+
this.orderRowElement.hidden = true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.formElement.action = config.liqpay_checkout_url;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
bindEvents() {
|
|
74
|
+
this.buttonElement.addEventListener('click', () => this.handlePayment());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async handlePayment() {
|
|
78
|
+
if (this.isLoading) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const config = this.getConfig();
|
|
83
|
+
|
|
84
|
+
this.hideError();
|
|
85
|
+
|
|
86
|
+
if (!this.validatePaymentConfig(config)) {
|
|
87
|
+
this.showError('Некоректні дані для оплати.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
this.setLoading(true);
|
|
93
|
+
|
|
94
|
+
const payment = await this.createPayment(config);
|
|
95
|
+
|
|
96
|
+
this.dataInput.value = payment.data;
|
|
97
|
+
this.signatureInput.value = payment.signature;
|
|
98
|
+
|
|
99
|
+
this.formElement.submit();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error('Failed to create LiqPay payment:', error);
|
|
102
|
+
this.showError(
|
|
103
|
+
config.error_text || 'Не вдалося підготувати оплату. Спробуйте ще раз.'
|
|
104
|
+
);
|
|
105
|
+
} finally {
|
|
106
|
+
this.setLoading(false);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async createPayment(config) {
|
|
111
|
+
const response = await fetch(`https://gudhub-nodejs.ngrok.io:443${config.create_payment_url}`, {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
'Content-Type': 'application/json'
|
|
115
|
+
},
|
|
116
|
+
body: JSON.stringify({
|
|
117
|
+
amount: config.amount,
|
|
118
|
+
currency: config.currency,
|
|
119
|
+
order_id: config.order_id,
|
|
120
|
+
description: config.payment_description
|
|
121
|
+
})
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
const errorText = await response.text();
|
|
126
|
+
|
|
127
|
+
throw new Error(`Payment request failed with status ${response.status}: ${errorText}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const payment = await response.json();
|
|
131
|
+
|
|
132
|
+
if (!payment?.data || !payment?.signature) {
|
|
133
|
+
throw new Error('Invalid payment response');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return payment;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
validatePaymentConfig(config) {
|
|
140
|
+
const amount = Number(config.amount);
|
|
141
|
+
|
|
142
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!config.currency) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!config.order_id) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!config.payment_description) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!config.create_payment_url) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!config.liqpay_checkout_url) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setLoading(isLoading) {
|
|
170
|
+
const config = this.getConfig();
|
|
171
|
+
|
|
172
|
+
this.isLoading = isLoading;
|
|
173
|
+
this.buttonElement.disabled = isLoading;
|
|
174
|
+
this.buttonTextElement.textContent = isLoading
|
|
175
|
+
? config.loading_text || 'Підготовка оплати...'
|
|
176
|
+
: config.button_text || 'Сплатити';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
showError(message) {
|
|
180
|
+
this.errorElement.textContent = message;
|
|
181
|
+
this.errorElement.hidden = false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
hideError() {
|
|
185
|
+
this.errorElement.textContent = '';
|
|
186
|
+
this.errorElement.hidden = true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
formatAmount(amount, currency) {
|
|
190
|
+
return new Intl.NumberFormat('uk-UA', {
|
|
191
|
+
style: 'currency',
|
|
192
|
+
currency
|
|
193
|
+
}).format(amount);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
window.customElements.define('liqpay-component', LiqpayComponent);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "Test Payment",
|
|
3
|
+
"subtitle": "Complete your test payment securely with LiqPay.",
|
|
4
|
+
"description": "After clicking the button you will be redirected to the LiqPay secure payment page.",
|
|
5
|
+
"amount": 1,
|
|
6
|
+
"currency": "UAH",
|
|
7
|
+
"order_id": "test-order-001",
|
|
8
|
+
"payment_description": "Test LiqPay payment",
|
|
9
|
+
"button_text": "Сплатити",
|
|
10
|
+
"loading_text": "Підготовка оплати...",
|
|
11
|
+
"error_text": "Не вдалося підготувати оплату. Спробуйте ще раз.",
|
|
12
|
+
"create_payment_url": "/liqpay/create-payment",
|
|
13
|
+
"liqpay_checkout_url": "https://www.liqpay.ua/api/3/checkout"
|
|
14
|
+
}
|
|
File without changes
|