Web SDK Payment
Описание
JavaScript-библиотека за показване на платежна форма на страницата на търговеца. Този начин е подходящ за търговци както с високо, така и с ниско ниво на съответствие с изискванията PCI DCC.
Сценарий на работа:
- Търговецът регистрира поръчка чрез REST API в платежен шлюз
- Получения
mdOrder(номер на поръчката) търговецът предава на страницата, където се използва тази js-библиотека
Web SDK предоставя възможност за добавяне на своята платежна страница полета за въвеждане на платежни данни чрез iframe непосредствено от платежен шлюз. Сигурността на предаването на данни се осигурява от шифроването на протокола HTTPS.
Плюсове:
- Сигурно: Платежната страница на търговеца и нейните скриптове нямат достъп до платежните полета, предадени чрез iframe. Данните за картите не могат да бъдат събирани от външни скриптове.
- Просто: Скриптът лесно се интегрира на страницата. За да съответства на общия стил на сайта, се изисква минимална персонализация.
- Надеждно: От страната на търговеца не се изисква съответствие с високите нива PCI DCC.
- Удобно: На сървъра могат да бъдат предадени допълнителни полета, като
email,language,phoneиjsonParams.
Минуси:
- В момента Web SDK не е съвместим с функцията за множествени опити за плащане.
Библиотеката помага в събирането на данни за картата, тяхната валидация и проверка, извършването на плащането и автоматичното пренасочване на купувача към финалната страница чрез посочения в настройките returnUrl.
Как да използвате
Свързване на скрипт
Тестова среда
<script src="https://uat.dskbank.bg/payment/modules/multiframe/main.js"></script>Бойна среда
<script src="https://epg.dskbank.bg/payment/modules/multiframe/main.js"></script>Подготовка
Първо, необходимо е да създадете HTML-форма за приемане на плащания. Формата трябва да съдържа блокове #pan, #expiry, #cvc и бутон #pay. Не е задължително да използвате именно тези имена на полета. Ще можете да настроите нужните ви имена по време на инициализация.
Пример за HTML-форма, която не поддържа връзки:
<div class="card-body">
<div class="col-12">
<label for="pan" class="form-label">Card number</label>
<!-- Container for card number field -->
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Expiry</label>
<!-- Container for expiry card field -->
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<!-- Container for CVC/CVV field -->
<div id="cvc" class="form-control"></div>
</div>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<!-- Payment loader -->
<span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
<span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>Ако използвате връзки, трябва да включите допълнителен HTML блок: #select-binding.
Ето пример за HTML-форма, която поддържа връзки:
<div class="card-body">
<div class="col-12" id="select-binding-container" style="display: none">
<!-- Select for bindings -->
<select class="form-select" id="select-binding" aria-label="Default select example">
<option selected value="new_card">Pay with a new card</option>
</select>
</div>
<div class="col-12">
<label for="pan" class="form-label">Card number</label>
<!-- Container for card number field -->
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Expiry</label>
<!-- Container for expiry card field -->
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<!-- Container for cvc/cvv field -->
<div id="cvc" class="form-control"></div>
</div>
<label class="col-12" id="save-card-container">
<!-- Save card checkbox -->
<input class="form-check-input" type="checkbox" value="" id="save-card" />
Save card
</label>
</div>
<!-- Pay button -->
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<!-- Payment loader -->
<span class="spinner-border spinner-border-sm visually-hidden" id="pay-spinner"></span>
<span>Pay</span>
</button>
<!-- Container for errors -->
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>Можете да добавите към формата всякакви допълнителни полета, като Cardholder name (име на притежателя на картата), Email (електронна поща), Phone (телефонен номер) и т.н. Обаче не забравяйте впоследствие да ги предадете в метода doPayment().
Инициализация на Web SDK
Описание на платежната форма
Трябва да изпълните функцията конструктор new window.PaymentForm().
window.PaymentForm() може да приема следните свойства:
Свойства за инициализация на PaymentForm
По подразбиране apiContext автоматично се взема от връзката, използвана за свързване на скрипта
modules/multiframe/main.js.
Стойност по подразбиране:
en.
Стойност по подразбиране -
true
По подразбиране
false
По подразбиране
false
По подразбиране
true
По подразбиране
true
Стойност по подразбиране:
field-container
Например:
onFormValidate: (isValid) => {
alert(isValid ? 'Congratulations!' : 'Oops! We regret.');
}Пример на инициализация на Web SDK
const webSdkPaymentForm = new window.PaymentForm({
// Номер на поръчката (регистрацията на поръчката се извършва преди инициализацията на формата)
mdOrder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
// Име на класа, който ще бъде зададен за контейнерите с полетата за въвеждане
containerClassName: "field-container",
onFormValidate: (isValid) => {
// Обработване на валидацията на формата
},
// Контекст за API заявките
apiContext: "/payment",
// Език - използва се за локализация на грешките и наименованията на плейсхолдерите.
// Езикът трябва да се поддържа в настройките на Търговеца
language: "en",
// Автоматично превключване на фокуса при попълване на полетата
autoFocus: true,
// Показване на иконата на платежната система
showPanIcon: true,
// Потребителски стилове за иконата на платежната система
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: document.querySelector("#pan"),
onFocus: (containerElement) => {
// Действие при фокусиране на полето
// (containerElement съдържа референция към контейнер елемента на полето)
},
onBlur: (containerElement) => {
// Действие при загуба на фокус от полето
// (containerElement съдържа референция към контейнер елемента на полето)
},
onValidate: (isValid, containerElement) => {
// Действие при валидация на полето
// (isValid е равно на true ако полето е валидно, иначе false)
// (containerElement съдържа референция към контейнер елемента на полето)
},
},
expiry: {
container: document.querySelector("#expiry"),
// ...
},
cvc: {
container: document.querySelector("#cvc"),
// ...
},
},
// Стилове за полетата за въвеждане
styles: {
// Базово състояние
base: {
color: "black",
padding: '0px 16px',
fontSize: '18px',
fontFamily: 'monospace',
},
// Състояние с фокус
focus: {
color: "blue",
},
// Изключено състояние
disabled: {
color: "gray",
},
// С валидна стойност
valid: {
color: "green",
},
// С невалидна стойност
invalid: {
color: "red",
},
// Стил за плейсхолдера
placeholder: {
// Базов стил
base: {
color: "gray",
},
// Стил при фокус
focus: {
color: "transparent",
},
},
},
});Метод destroy
Методът destroy() в Web SDK се използва за премахване на всички ресурси и слушатели на събития, свързани с конкретен екземпляр на Web SDK. Когато извикате метода destroy(), той изчиства всички слушатели на събития и контейнери на полета за въвеждане, които са били създадени от Web SDK през неговия жизнен цикъл. Това е полезно, когато вече не се нуждаете от екземпляра на Web SDK.
Методът destroy() обикновено изпълнява следните задачи:
- Премахва всички слушатели на събития, които са били добавени в екземпляра на Web SDK.
- Изчиства всички създадени контейнери на полета за въвеждане.
Пример на метод destroy
document.querySelector("#destroy").addEventListener("click", function () {
webSdkPaymentForm.destroy();
});Стилизиране
Стилизирането на контейнерите, в които се предават полетата за въвеждане, се определя самостоятелно съгласно дизайна на вашата страница. Различните състояния на контейнерите на полетата за въвеждане могат да бъдат стилизирани с помощта на следните CSS класове:
-
{className}--focus- поле във фокус -
{className}--valid- поле с валидна стойност -
{className}--invalid- поле с невалидна стойност
Параметърът className се задава при инициализация чрез параметъра containerClassName в свойствата на window.PaymentForm().
Пример:
<style>
.field-container {
width: 100%;
height: 50px;
padding: 0;
}
.field-container--focus {
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
}
.field-container--valid {
border-color: #198754;
}
.field-container--valid.field-container--focus {
box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25);
}
.field-container--invalid {
border-color: #dc3545;
}
.field-container--invalid.field-container--focus {
box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
}
</style>
<script>
//...
const paymentForm = new window.PaymentForm({
//...
containerClassName: 'field-container',
//...
</script>Шрифтове
В Web SDK можно използувати системни/прединсталирани на устройствата шрифтове. Шрифтът се задава при инициализация на Web SDK в свойството styles или customStyles. Например в styles.base.fontFamily.
Стилизиране на полетата за въвеждане
Можете да настроите външния вид на полетата за въвеждане. За това
- Използвайте обекта
stylesза базови стилове на всички полета - Използвайте
customStylesза предефиниране на стиловете на отделни полета (например, pan, expiry, cvc)
Пример:
const webSdkPaymentForm = new window.PaymentForm({
// ...
// стилове по подразбиране за всички полета за въвеждане
styles: {
base: {
color: 'black',
padding: '0px 16px',
fontSize: '18px',
fontFamily: 'Arial, sans-serif',
},
// ...
invalid: {
color: 'red',
},
// ...
},
// персонализирани стилове за полето за въвеждане на номера на картата
customStyles: {
pan: {
base: {
color: 'blue',
padding: '0px 24px',
fontSize: '22px',
},
invalid: {
color: 'orange',
},
},
},
});Допълнителни настройки
Настройте допълнителни параметри при инициализация, като език на placeholder, икони на платежни системи, маскиране на полета за въвеждане и др. Пълен списък на параметрите е достъпен в Свойства за инициализация на PaymentForm.
Валидация
WebSDK осигурява проверка, валидация и защита само на основните полета за въвеждане, необходими за извършване на плащане: Pan, Expiry, CVC.
Всички останали допълнителни полета, като Cardholder name, Phone, Email, полета осигуряващи изпълнението на изискванията на мандата Visa Secure Data и т.п., търговецът е длъжен да валидира самостоятелно от своя страна.
Примери на регулярни израzi за валидация на допълнителни полета:
Cardholder name:
// regex: ^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$
export function validateCardholderName({
errorMessage = 'Invalid cardholder'
} = {}) {
return (value) => {
const regex = /^[A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]* [A-zÁÉÍÑÓÚÜáéíñóúü][A-zÁÉÍÑÓÚÜáéíñóúü'\.\s]*$/;
return regex.test(String(value).trim()) ? null : errorMessage;
};
}Email:
// regex: ^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$
export function validateEmail({
errorMessage = 'Invalid email'
} = {}) {
return (value) => {
const regex = /^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
return regex.test(String(value).toLowerCase()) ? null : errorMessage;
};
}Phone:
// regex: ^\+\d{7,15}$
export function validatePhone({
errorMessage = 'Invalid phone number'
} = {}) {
return (value) => {
const regex = /^\+\d{7,15}$/;
return regex.test(String(value).trim()) ? null : errorMessage;
};
}Плащане без запазена карта (връзка)
Стъпка 1. Изпълнете метода за инициализация
След определяне на параметрите на Web SDK е необходимо да извикате init().
Тази функция връща callback, където можете например да скриете loader-а или да направите нещо друго.
init() връща c.
Например:
webSdkFormWithoutBindings
.init()
.then((success) => {
console.log('success', success)
// Скриптът е успешно инициализиран. Promise връща обект, съдържащ полезна информация за регистрираната
// в платежната система поръчка. След това можете да премахнете loader-а или да изпълните други действия:
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
})
.catch((error) => {
// При инициализацията на скрипта възникнаха грешки. По-нататъшното изпълнение е невъзможно.
// Promise връща съобщение за грешка, което можем да покажем на страницата:
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
})
.finally(() => {
// Действия, изпълнявани след инициализацията, независимо от успешното или неуспешното й изпълнение.
});Стъпка 2. Обработка на натискането на бутона за плащане. Изпълнете метода за плащане
За да изпълните плащането, извикайте функцията doPayment().
Не е нужно да изпращате данни за картата, това ще направи Web SDK. doPayment() връща Promise.
Методът приема следните параметри:
jsonParams: { "t-shirt-color": "черен", "size": "M" }
Актуализация на мандата Visa Secure Data Field
Обърнете внимание на изискванията на IPS Visa относно допълнителните полета с данни, необходими за заявките за удостоверяване EMV 3DS. Търговците трябва да предоставят пълни и точни данни за транзакциите в своите заявки за удостоверяване. Търговците също трябва да гарантират, че 3DS Method URL-адресът извършва събиране на данни за устройството за поддръжка на успешното удостоверяване в случай, че 3DS Method URL-адресът е предоставен от издателя.
По този начин събирането на допълнителни полета за VISA е отговорност на търговеца. Можете да се запознаете с пълния текст на изискванията в Visa Secure Data Field Mandate.
Допълнителните полета се предават като свойства на обекта, който е аргумент на функцията doPayment().
Пример за извикване:
webSdkFormWithoutBindings
.doPayment({
// Допълнителни параметри
email: "foo@bar.com",
phone: "4420123456789",
cardholderName: "JOHN DOE",
jsonParams: { foo: "bar" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Обработка на грешки. За пример ще покажем блок с грешка
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Изпълнява се във всеки случай. Например да направим бутона "Плати" отново активен.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});Демонстрация без запазена карта
За работни цели - регистрирайте поръчка чрез API.
Тази форма се използва само за демонстрационни цели - използвайте стойност Order ID =
xxxxx-xxxxx-xxxxx-xxxxxЦелия демонстрационен код
<div class="container_demo">
<div class="about">
<form name="formRunTest">
<label for="mdOrder"> Order ID (mdOrder) <br>
<span class="label__desc">(Трябва да постъпи от backend-а. Този вход е предназначен само за демонстрация)</span>
</label>
<div class="run-test">
<input id="mdOrder" type="text" placeholder="Paste the mdOrder registered for sandbox"/>
<button class="btn-mini" id="load" type="submit">Load</button>
</div>
</form>
</div>
<div class="payment-form">
<div class="payment-form-loader payment-form-loader--active">
Web SDK Payment изисква наличие на mdOrder (предварително регистрирана в шлюза поръчка).<br>
Регистриране на поръчка може чрез Merchant Portal или чрез API. <br><br>
Или опитайте да използвате <code>xxxxx-xxxxx-xxxxx-xxxxx</code> ако искате да получите само платежната форма.
</div>
<div class="card-body">
<div class="col-12">
<label for="pan" class="form-label">Номер на карта</label>
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Срок на валидност</label>
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<div id="cvc" class="form-control"></div>
</div>
<!-- Допълнителни полета за Visa Mandatory -->
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Cardholder</label>
<div id="cardholder" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="Surname"value="JOHN DOE">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Mobile phone</label>
<div id="mobile" class="additional-field-container">
<input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Email address</label>
<div id="email" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
</div>
</div>
</div>
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<span
class="spinner-border spinner-border-sm me-2 visually-hidden"
role="status"
aria-hidden="true"
id="pay-spinner">
</span>
<span>Плати</span>
</button>
<!-- Тези бутони са необходими само за демонстрация работата на метода destroy -->
<button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
<span>Унищожи</span>
</button>
<button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
<span>Инициализирай</span>
</button>
<div class="error my-2 text-center text-danger visually-hidden" id="error"></div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Функция за инициализация на платежната форма с възможност за повторна употреба след унищожаване
function initPaymentForm() {
const mrOrderInput = document.getElementById("mdOrder");
mrOrderInput.classList.remove("invalid");
if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
mrOrderInput.classList.add("invalid");
return;
}
// Инициализация на Web SDK. Необходим е идентификатор на поръчка mdOrder.
initPayment(mrOrderInput.value);
}
document.formRunTest.addEventListener("submit", function (e) {
e.preventDefault();
// Инициализация на платежната форма
initPaymentForm();
});
let webSdkFormWithoutBindings;
// Масив от обекти за допълнителни полета, които съдържат id на полето, неговия шаблон за валидация и допустими символи за въвеждане
const mandatoryFieldsWithoutBinding = [
{
id: '#cardholder',
template: /^[a-zA-Z '`.\-]{4,24}$/,
replace: /[^a-zA-Z ' \-`.]/g,
},
{
id: '#mobile',
template: /^\+?[1-9][0-9]{7,14}$/,
replace: /[^0-9\+]/g,
},
{
id: '#email',
template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
replace: /[^a-zA-Z0-9@._-]/g,
}
]
function initPayment(mdOrder) {
webSdkFormWithoutBindings = new window.PaymentForm({
mdOrder: mdOrder,
onFormValidate: () => {},
language: "en", // Език (английски)
containerClassName: "field-container",
autoFocus: true,
showPanIcon: true,
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: document.querySelector("#pan"),
},
expiry: {
container: document.querySelector("#expiry"),
},
cvc: {
container: document.querySelector("#cvc"),
},
},
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
// Действие след инициализация
webSdkFormWithoutBindings.init().then(() => {
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
})
.catch((e) => {
// Показване на грешка при инициализация на webSDK
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
}
.finally(() => {
// Валидация и автозамяна на недопустими символи
mandatoryFieldsWithBinding.forEach(item => {
const field = document.querySelector(item.id)
field.closest(".additional-field").style.display = '';
field.addEventListener('input', () => {
let inputValue = field.querySelector('input')
inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
if (item.id.includes("#cardholder")) {
inputValue.value = inputValue.value.toUpperCase()
}
if (item.template) {
// CSS клас ".additional-field-invalid" за показване на невалидни полета
if (item.template.test(inputValue.value)) {
field.classList.remove("additional-field-invalid")
} else {
field.classList.add("additional-field-invalid")
}
}
})
})
})
}
// Обработчик "Плати"
document.querySelector("#pay").addEventListener("click", () => {
const payButton = document.querySelector("#pay");
// Правим бутона "Плати" неактивен, за да избегнем двойни плащания
payButton.disabled = true;
// Показваме зареждачката на потребителя
const spinnerEl = document.querySelector("#pay-spinner");
spinnerEl.classList.remove("visually-hidden");
// Скриваме контейнера с грешката
const errorEl = document.querySelector("#error");
errorEl.classList.add("visually-hidden");
// Валидация на допълнителни полета Visa Mandatory
if (document.querySelectorAll('.additional-field-invalid').length) {
errorEl.innerHTML = "Form is not valid";
errorEl.classList.remove('visually-hidden');
spinnerEl.classList.add('visually-hidden');
return
}
// Започваме процеса на плащане
webSdkFormWithoutBindings
.doPayment({
// Допълнителни параметри
email: document.querySelector('#email input').value,
phone: document.querySelector('#mobile input').value,
cardholderName: document.querySelector('#cardholder input').value,
jsonParams: { size: "L" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Изпълнява се при грешка
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Изпълнява се във всеки случай, например, правим бутона "Плащане" отново активен.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});
});
// Обработчик "Унищожи"
document
.querySelector("#destroyFormWithoutCredentials")
.addEventListener("click", function () {
// Премахваме бутона "Унищожи" и показваме бутона "Инициализирай" за демонстрация
this.style.display = "none";
document.querySelector("#reinitFormWithoutCredentials").style.display =
"";
// Унищожаваме платежната форма Web SDK
webSdkFormWithoutBindings.destroy();
});
// Обработчик "Инициализирай форма"
document
.querySelector("#reinitFormWithoutCredentials")
.addEventListener("click", function () {
// Премахваме бутона "Инициализирай" и показваме бутона "Унищожи" за демонстрация
this.style.display = "none";
document.querySelector("#destroyFormWithoutCredentials").style.display =
"";
// Инициализация на платежната форма
initPaymentForm();
});
});
</script>Плащане със запазена карта (връзка)
Стъпка 1. Изпълнение на метод за инициализация
След определяне на параметрите на Web SDK е необходимо да извикате init().
Тази функция връща callback, където можете например да скриете зареждача или да направите нещо друго.
init() връща Promise.
Например:
// Инициализация
webSdkFormWithBindings
.init()
.then(({ orderSession }) => {
// Обектът `orderSession` съдържа цялата информация за поръчката, включително информация за запазените идентификационни данни (за връзката).
console.info("orderSession", orderSession);
// Показване на елемента за избор на запазена карта
document.querySelector("#select-binding-container").style.display =
orderSession.bindings.length ? "" : "none";
// Попълване на избора със запазените идентификационни данни
orderSession.bindings.forEach((binding) => {
document
.querySelector("#select-binding")
.options.add(new Option(binding.pan, binding.id));
});
// Обработка на избора на запазени идентификационни данни или нова карта
document
.querySelector("#select-binding")
.addEventListener("change", function () {
const bindingId = this.value;
if (bindingId !== "new_card") {
webSdkFormWithBindings.selectBinding(bindingId);
// Скриване на полето "Запази карта"
document.querySelector("#save-card-container").style.display = "none";
} else {
// Избор на връзка с null означава преминаване към нова карта
webSdkFormWithBindings.selectBinding(null);
// Показване на полето "Запази карта"
document.querySelector("#save-card-container").style.display = "";
}
});
// Когато формата е готова, можем да скрием зареждача
document.querySelector("#pay-form-loader").classList.add("visually-hidden");
})
.catch((error) => {
// Възникнаха грешки при инициализацията на скрипта. По-нататъшното изпълнение е невъзможно.
// Promise връща съобщение за грешка, което можем да покажем на страницата:
const errorEl = document.querySelector('#error_1');
errorEl.innerHTML = e.message;
errorEl.classList.remove('visually-hidden');
});Стъпка 2. Обработка на натискането на бутона за плащане. Изпълнение на метод за плащане
За да изпълните плащане, извикайте функцията doPayment().
Не е нужно да изпращате данни за картата, това ще направи Web SDK. doPayment() връща Promise.
Методът приема следните параметри:
document.querySelector('#save-card').checked
jsonParams: { "t-shirt-color": "черен", "size": "M" }
Прилагане на запазени карти
За да платите с помощта на запазени идентификационни данни, трябва да предадете избрания bindingId във формата преди извикването на doPayment:
webSdkFormWithBindings.selectBinding('bindingId');
Ако се предумате и искате да платите с нова карта, не забравяйте да премахнете bindingId от формата:
webSdkFormWithBindings.selectBinding(null);
Пример за извикване:
webSdkFormWithBindings
.doPayment({
// Допълнителни параметри
email: "foo@bar.com",
phone: "4420123456789",
saveCard: document.querySelector("#save-card").checked,
cardholderName: "JOHN DOE",
jsonParams: { foo: "bar" },
})
.then((result) => {
console.log("result", result);
})
.catch((e) => {
// Обработка на грешки. За пример ще покажем блок с грешка
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Изпълни във всеки случай. Например, направи бутона "Плати" отново активен.
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});Демонстрация със запазена карта
За работни цели - регистрирайте поръчка чрез API.
Тази форма се използва само за демонстрационни цели - използвайте стойността Order ID =
xxxxx-xxxxx-xxxxx-xxxxxЦелият демонстрационен код
<div class="container_demo">
<div class="about">
<form name="formRunTest">
<label for="mdOrder"> Идентификатор на поръчка (mdOrder) <br/>
<span class="label__desc">(Приведено само за демонстрационни цели. Идентификаторът на поръчката трябва да идва от бекенда.)</span>
</label>
<div class="run-test">
<input id="mdOrder" type="text" placeholder="Поставете mdOrder, регистриран в Sandbox"/>
<button class="btn-mini" id="load" type="submit">Зареди</button>
</div>
</form>
</div>
<div class="payment-form">
<div class="payment-form-loader payment-form-loader--active">
За Web SDK Payment се изисква mdOrder (предварително регистрирана поръчка в шлюза).<br/>
Можете да регистрирате поръчка чрез личния кабинет или чрез API. <br/><br/>
Или опитайте да използвате <code>xxxxx-xxxxx-xxxxx-xxxxx</code> ако искате да проверите само формата за плащане.
</div>
<div id="pay-form-loader" class="spinner-container visually-hidden">
<div class="spinner-border" role="status"></div>
</div>
<div class="card-body">
<div class="col-12" id="select-binding-container" style="display: none">
<select class="form-select" id="select-binding" aria-label="Default select example">
<option selected value="new_card">Плащане с нова карта</option>
</select>
</div>
<div class="col-12">
<label for="pan" class="form-label">Номер на карта</label>
<div id="pan" class="form-control"></div>
</div>
<div class="col-6 col-expiry">
<label for="expiry" class="form-label">Срок на валидност</label>
<div id="expiry" class="form-control"></div>
</div>
<div class="col-6 col-cvc">
<label for="cvc" class="form-label">CVC / CVV</label>
<div id="cvc" class="form-control"></div>
</div>
<label class="col-12" id="save-card-container">
<input class="form-check-input" type="checkbox" value="" id="save-card" />
Запази карта
</label>
<!--Допълнителни полета Visa Mandatory -->
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Притежател на карта</label>
<div id="cardholder" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="NAME SURNAME"value="JOHN DOE">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Телефон</label>
<div id="mobile" class="additional-field-container">
<input type="tel" class="additional-field-input" placeholder="+4915112345" value="+4915112345678">
</div>
</div>
<div class="col-12 additional-field" style="display:none;">
<label for="" class="form-label">Електронна поща</label>
<div id="email" class="additional-field-container">
<input type="text" class="additional-field-input" placeholder="address@mail" value="address@mail.com">
</div>
</div>
</div>
<button class="btn btn-primary btn-lg" type="submit" id="pay">
<span
class="spinner-border spinner-border-sm me-2 visually-hidden"
role="status"
aria-hidden="true"
id="pay-spinner">
</span>
<span>Плати</span>
</button>
<!-- Тези бутони са нужни само за демонстрация на работата на метода destroy -->
<button class="btn btn-secondary" type="button" id="destroyFormWithoutCredentials">
<span>Унищожи</span>
</button>
<button class="btn btn-secondary" type="button" id="reinitFormWithoutCredentials" style="display: none;">
<span>Инициализиране</span>
</button>
<div class="error my-2 visually-hidden" id="error"></div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", () => {
// Функция за инициализация на платежна форма за нейното повторно използване след унищожение
function initPaymentForm() {
const mrOrderInput = document.getElementById("mdOrder");
mrOrderInput.classList.remove("invalid");
if (!/(\w+-){3,10}\w+/g.test(mrOrderInput.value)) {
mrOrderInput.classList.add("invalid");
return;
}
// Премахване на плейсхолдера
document
.querySelector(".payment-form-loader")
.classList.remove("payment-form-loader--active");
// Добавяме зареждач на платежни форми
document
.querySelector("#pay-form-loader")
.classList.remove("visually-hidden");
// Инициализация на Web SDK. Изисква се задължително mdOrder (идентификатор на поръчка).
initPayment(mrOrderInput.value);
}
// Инициализация на обработчик за тестови данни
function handleSubmit(e) {
e.preventDefault();
// Инициализиране на платежна форма
initPaymentForm();
}
// Регистрация на събитие за пример на въвеждане
document.formRunTest.addEventListener("submit", handleSubmit);
let webSdkFormWithBindings;
// Масив от обекти за допълнителни полета, които съдържат id на полето, неговия шаблон за валидация и допустими символи за въвеждане
const mandatoryFieldsWithoutBinding = [
{
id: '#cardholder',
template: /^[a-zA-Z '`.\-]{4,24}$/,
replace: /[^a-zA-Z ' \-`.]/g,
},
{
id: '#mobile',
template: /^\+?[1-9][0-9]{7,14}$/,
replace: /[^0-9\+]/g,
},
{
id: '#email',
template: /^[a-zA-Z0-9._-]{1,64}@([a-zA-Z0-9.-]{2,255})\.[a-zA-Z]{2,255}$/,
replace: /[^a-zA-Z0-9@._-]/g,
}
]
function initPayment(mdOrder) {
webSdkFormWithBindings = new window.PaymentForm({
// Номер на поръчка (регистрацията на поръчката се извършва преди инициализацията на формата)
mdOrder: mdOrder,
// Обработка на валидацията на формата
onFormValidate: (isValid) => {
// Например, можете да деактивирате бутоните „Плати" и „Получи токен", ако формата не е валидна, например:
// const payButton = document.querySelector('#pay');
// payButton.disabled = !isValid;
},
// Контекст за API извиквания
apiContext: "/payment",
// Езикът се използва за локализация на грешки и наименования за плейсхолдери.
// Езикът трябва да се поддържа в настройките на търговеца
language: "en",
// Име на клас за контейнерните елементи, съдържащи iframe
containerClassName: "field-container",
// Автоматично превключване на фокуса при попълване на полета
autoFocus: true,
// Показване на икона на платежната система
showPanIcon: true,
// Допълнителни стилове за иконата на платежната система
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
// Настройки на полето
fields: {
// Елемент-контейнер, в който ще бъде поставен iframe с полето
pan: {
container: document.querySelector("#pan"),
},
// Срок на валидност на картата
expiry: {
container: document.querySelector("#expiry"),
},
// CVC/CVV-код
cvc: {
container: document.querySelector("#cvc"),
},
},
// Допълнителни стилове за настройка на външния вид на полетата за въвеждане в iframes
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
disabled: {
backgroundColor: "#e9ecef",
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
// Действие след инициализация
webSdkFormWithBindings
.init()
.then(({ orderSession }) => {
// Обектът `orderSession` съдържа цялата информация за поръчката, включително информация за запазените идентификационни данни (за връзката).
console.info("orderSession", orderSession);
// Покажи избраната връзка
document.querySelector("#select-binding-container").style.display =
orderSession.bindings.length ? "" : "none";
// Запълни избора със запазени идентификационни данни
orderSession.bindings.forEach((binding) => {
document
.querySelector("#select-binding")
.options.add(new Option(binding.pan, binding.id));
});
// Обработка на избора на запазени идентификационни данни или нова карта
document
.querySelector("#select-binding")
.addEventListener("change", function () {
const bindingId = this.value;
if (bindingId !== "new_card") {
// Задай идентификатор на връзката
webSdkFormWithBindings.selectBinding(bindingId);
// Hide the 'Save card' checkbox
document.querySelector("#save-card-container").style.display =
"none";
} else {
// Избор на връзка с null означава преминаване към нова карта
webSdkFormWithBindings.selectBinding(null);
// Покажи отметката "Запази карта"
document.querySelector("#save-card-container").style.display = "";
}
});
// Когато формата е готова, можем да скрием зареждачa
document
.querySelector("#pay-form-loader")
.classList.add("visually-hidden");
// Премахни събитие за пример за въвеждане
document.formRunTest.removeEventListener("submit", handleSubmit);
})
.catch((error) => {
// Изпълни при грешка
const errorEl = document.querySelector("#error");
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Валидация и автоматична замяна на недопустими символи
mandatoryFieldsWithBinding.forEach(item => {
const field = document.querySelector(item.id)
field.closest(".additional-field").style.display = '';
field.addEventListener('input', () => {
let inputValue = field.querySelector('input')
inputValue.value = item.replace ? inputValue.value.replace(item.replace,'') : inputValue.value;
if (item.id.includes("#cardholder")) {
inputValue.value = inputValue.value.toUpperCase()
}
if (item.template) {
// CSS клас ".additional-field-invalid" за показване на невалидни полета
if (item.template.test(inputValue.value)) {
field.classList.remove("additional-field-invalid")
} else {
field.classList.add("additional-field-invalid")
}
}
})
})
});
}
// Обработчик за плащане
document.querySelector("#pay").addEventListener("click", () => {
// Правим бутона "Плащане" неактивен, за да избегнем двойни плащания
const payButton = document.querySelector("#pay");
payButton.disabled = true;
// Покажи зареждача за потребителя
const spinnerEl = document.querySelector("#pay-spinner");
spinnerEl.classList.remove("visually-hidden");
// Скрий контейнера за грешки
const errorEl = document.querySelector("#error");
errorEl.classList.add("visually-hidden");
// Валидиране на допълнителни полета Visa Mandatory
if (document.querySelectorAll('.additional-field-invalid').length) {
errorEl.innerHTML = "Form is not valid";
errorEl.classList.remove('visually-hidden');
spinnerEl.classList.add('visually-hidden');
return
}
// Започни плащане
webSdkFormWithBindings
.doPayment({
// Допълнителни параметри
email: document.querySelector('#email input').value,
phone: document.querySelector('#mobile input').value,
cardholderName: document.querySelector('#cardholder input').value,
saveCard: document.querySelector("#save-card").checked,
jsonParams: { foo: "bar" },
})
.then((result) => {
// Тук може да направите нещо с резултата от плащането
console.log("result", result);
})
.catch((e) => {
// Изпълни при грешка
errorEl.innerHTML = e.message;
errorEl.classList.remove("visually-hidden");
})
.finally(() => {
// Изпълни във всеки случай. Например, отново направи активен бутона „Плащане".
payButton.disabled = false;
spinnerEl.classList.add("visually-hidden");
});
});
// обработчик Destroy
document
.querySelector("#destroyFormWithCredentials")
.addEventListener("click", function () {
// Премахни бутона Destroy и покажи бутона за повторна инициализация за демонстрация
this.style.display = "none";
document.querySelector("#reinitFormWithCredentials").style.display = "";
// Изпълни метода destroy във веб-формата SDK
webSdkFormWithBindings.destroy();
});
// Инициирай обработчик на формата за плащане
document
.querySelector("#reinitFormWithCredentials")
.addEventListener("click", function () {
// Премахни бутона Reinit и покажи бутона Destroy за демонстрация
this.style.display = "none";
document.querySelector("#destroyFormWithCredentials").style.display = "";
// Инициализация на формата за плащане
initPaymentForm();
});
});
</script>Обработка на данни, върнати от методите init( ) и doPayment( )
Метод init( )
Методът връща Promise, който при успешно изпълнение връща обект с информация за регистрираната поръчка. По съображения за сигурност, този обект не съдържа картови данни и друга поверителна информация.
Методът връща следните параметри:
Възможно е наличие на допълнителни полета, или отсъствие на някои полета от списъка по-горе.
Пример
{
"mdOrder": "5541f44c-d7ec-7a6c-997d-1d4d0007bc7d",
"orderSession": {
"amount": "100000",
"currencyAlphaCode": "BYN",
"currencyNumericCode": "933",
"sessionTimeOverAt": 1740385187287,
"orderNumber": "27000",
"description": "",
"cvcNotRequired": false,
"bindingEnabled": false,
"bindingDeactivationEnabled": false,
"merchantOptions": [
"MASTERCARD_TDS",
"MASTERCARD",
"VISA",
"VISA_TDS",
"CARD"
],
"customerDetails": {},
"merchantInfo": {
"merchantUrl": "http://google.com",
"merchantFullName": "Coffee to Go",
"merchantLogin": "CoffeToGo",
"captchaMode": "NONE",
"loadedResources": {
"logo": true,
"footer": false
},
"custom": false
},
"bindings": [
{
"cardholderName": "CARDHOLDER NAME",
"createdAt": 1712321609666,
"id": "83ffea5d-061f-7eca-912a-02ff0007bc7d",
"pan": "4111 11** **** 1111",
"expiry": "12/24",
"cardInfo": {
"name": "TEST BANK-A",
"nameEn": "TEST BANK-A",
"backgroundColor": "#fbf0ff",
"backgroundGradient": [
"#fafafa",
"#f3f0ff"
],
"supportedInvertTheme": false,
"backgroundLightness": true,
"country": "hu",
"defaultLanguage": "en",
"textColor": "#040e5d",
"url": null,
"logo": "logo/main/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"logoInvert": "logo/invert/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"logoMini": "logo/mini/293c39ad-0bcb-4cbb-803e-65c435877b5a/1.svg",
"design": null,
"paymentSystem": "visa",
"cobrand": null,
"productCategory": null,
"productCode": null,
"mnemonic": "TEST BANK-A",
"params": null
}
}
]
}
}Неуспешно изпълнение
При неуспешно изпълнение Promise връща съобщение за грешка, което можем да покажем на страницата. Пример за съобщение: Error: Формата е невалидна.
Примери за код за обработка на данните, върнати от метода за инициализация init() са приведени в раздела Стъпка 1. Изпълнение на метода за инициализация за плащане без запазена карта и със запазена карта.
Метод doPayment( )
Методът връща Promise, който при успешно изпълнение връща обект с информация за извършеното плащане.
Методът връща следните параметри:
Възможно е наличието на допълнителни полета, или липсата на някои полета от списъка по-горе.
Пример
{
"redirectUrl": "https://bankhost.com/payment/merchants/ecom/finish.html?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"finishedPaymentInfo": {
"paymentSystem": "MASTERCARD",
"merchantShortName": "CoffeToGo",
"merchantLogin": "CoffeToGo",
"merchantFullName": "Coffee to Go",
"approvalCode": "123456",
"orderNumber": "4003",
"formattedTotalAmount": "15.00",
"backUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"failUrl": "https://www.coffeetogo.com/someproblem?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"terminalId": "12345678",
"orderDescription": "Order 123",
"displayErrorMessage": "",
"loadedResources": {
"footer": false,
"logo": false
},
"currencyAlphaCode": "EUR",
"orderFeatures": [
"ACS_IN_IFRAME",
"BINDING_NOT_NEEDED"
],
"isWebView": false,
"actionCodeDetailedDescription": "Request processed successfully",
"transDate": "29.11.2024 15:19:30",
"currency": "978",
"actionCode": 0,
"expiry": "12/2024",
"formattedAmount": "15.00",
"actionCodeDescription": "",
"formattedFeeAmount": "0.00",
"email": "address@mail.com",
"amount": "1500",
"merchantCode": "12345678",
"ip": "x.x.x.x",
"panMasked": "555555**5599",
"successUrl": "https://www.coffeetogo.com/congratulation?orderId=568b2db6-2acc-79e7-9ed8-746a00cd6608&lang=en",
"paymentWay": "CARD",
"processingErrorType": {
"value": "NO_ERROR",
"messageCode": "payment.errors.no_error",
"apiErrorCodeMessage": "payment.errors.no_error.code"
},
"panMasked4digits": "**** **** **** 5599",
"amountsInfo": {
"currencyDto": {
"alphabeticCode": "EUR",
"numericCode": "978",
"minorUnit": 2
},
"depositedAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"totalAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"refundedAmount": {
"value": 0,
"formattedValue": "0.00"
},
"approvedAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"feeAmount": {
"value": 0,
"formattedValue": "0.00"
},
"paymentAmount": {
"value": 1500,
"formattedValue": "15.00"
},
"amount": {
"value": 1500,
"formattedValue": "15.00"
},
"depositedTotalAmount": {
"value": 1500,
"formattedValue": "15.00"
}
},
"errorTypeName": "SUCCESS",
"feeAmount": "0",
"totalAmount": "1500",
"orderParams": {
"phone": "+4915112345678",
"foo": "bar",
"paymentMethod": "multiframe-sdk"
},
"orderExpired": false,
"refNum": "111111111111",
"finishPageLogin": "ecom",
"sessionExpired": false,
"cardholderName": "JOHN DOE",
"paymentDate": "29.11.2024 15:19:49",
"merchantUrl": "https://www.coffeetogo.com/",
"status": "DEPOSITED"
}
}Неуспешно изпълнение
При неуспешно изпълнение Promise връща съобщение за грешка, което можем да покажем на страницата. Пример за съобщение: Error: Operation declined. Please check the data and available balance of the account.
Примери за код за обработка на данните, връщани от метода doPayment(), са дадени в раздел Стъпка 2. Обработка на натискането на бутона за плащане. Изпълни метода за плащане за плащане без запазена карта и със запазена карта
Автоматично пренасочване след извършване на плащането
След плащането се извършва автоматично пренасочване от страницата с Web SDK. За да се обработи връщаният от метода doPayment() обект непосредствено на страницата с Web SDK, се изисква изключване на автоматичното пренасочване след извършване на плащането. За това се изисква специално разрешение в системата ‒ в платежната шлюз за данния търговец трябва да бъде включена пермисия Поддръжка Acs IFrame включена. За получаване на разрешение се обърнете към службата за техническа поддръжка на банката. Също така при инициализацията на Web SDK в предаваният обект трябва да съдържа свойство shouldHandleResultManually: true.
Например:
webSdkForm = new window.PaymentForm({
...
shouldHandleResultManually: true,
...
});Web SDK в React SPA
При използване на Web SDK в single page application (SPA) на React е необходимо да се инициализира Web SDK, като се изпълни методът webSdkPaymentForm.init() при всяко първоначално рендериране на страницата с формата Web SDK в SPA.
При събитие за нулиране на страницата с формата Web SDK (т.е. при преминаване към друга страница), е необходимо да се изпълни методът webSdkPaymentForm.destroy(). Това е важно, тъй като на страницата трябва да остане само един обработчик на формата webSDK (multiframe-commutator).
При връщане на страницата с формата Web SDK е необходимо отново да се проведе инициализация с помощта на методът webSdkPaymentForm.init().
Обърнете внимание, че за използването на тази библиотека се изисква съответствие със стандарта PCI DSS, тъй като тя обработва данни на карти. Повече за PCI DSS тук.
Пример React компонент
import { useEffect, useRef } from "react";
function addScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement("script");
script.setAttribute("src", src);
script.addEventListener("load", resolve);
script.addEventListener("error", reject);
document.body.appendChild(script);
});
}
function App() {
const panRef = useRef(null);
const expiryRef = useRef(null);
const cvcRef = useRef(null);
const selectBindingRef = useRef(null);
const saveCardContainerRef = useRef(null);
const payButtonRef = useRef(null);
let webSdkPaymentForm = null;
useEffect(() => {
const initPaymentForm = async () => {
await addScript(
"https://uat.dskbank.bg/payment/modules/multiframe/main.js",
);
webSdkPaymentForm = new window.PaymentForm({
mdOrder: mdOrder,
containerClassName: "field-container",
onFormValidate: (isValid) => {
// Обработка на валидацията на формата
},
apiContext: "/payment",
language: "en",
autoFocus: true,
showPanIcon: true,
panIconStyle: {
height: "16px",
top: "calc(50% - 8px)",
right: "8px",
},
fields: {
pan: {
container: panRef.current,
onFocus: (containerElement) => {
// Обработка на фокуса
},
onBlur: (containerElement) => {
// Обработка на загубата на фокус
},
onValidate: (isValid, containerElement) => {
// Обработка на валидацията
},
},
expiry: {
container: expiryRef.current,
// Настройка на полето за срок на валидност
},
cvc: {
container: cvcRef.current,
// Настройка на CVC/CVV полето
},
},
styles: {
base: {
padding: "0px 16px",
color: "black",
fontSize: "18px",
fontFamily: 'monospace',
},
focus: {
color: "blue",
},
disabled: {
color: "gray",
},
valid: {
color: "green",
},
invalid: {
color: "red",
},
placeholder: {
base: {
color: "gray",
},
focus: {
color: "transparent",
},
},
},
});
webSdkPaymentForm
.init()
.then(({ orderSession }) => {
console.info("orderSession", orderSession);
if (orderSession.bindings.length) {
selectBindingRef.current.style.display = "";
} else {
selectBindingRef.current.style.display = "none";
}
if (orderSession.bindingEnabled) {
saveCardContainerRef.current.style.display = "";
} else {
saveCardContainerRef.current.style.display = "none";
}
orderSession.bindings.forEach((binding) => {
const option = new Option(binding.pan, binding.id);
selectBindingRef.current.options.add(option);
});
})
.catch(() => {
// Обработка на грешката при инициализация
});
};
initPaymentForm();
return () => {
if (webSdkPaymentForm) {
webSdkPaymentForm.destroy();
}
};
}, []);
const handlePayment = () => {
payButtonRef.current.disabled = true;
webSdkPaymentForm
.doPayment({})
.then((result) => {
// Обработка на успешното плащане
})
.catch((e) => {
alert("Error");
})
.finally(() => {
payButtonRef.current.disabled = false;
});
};
const handleSelectBinding = () => {
const bindingId = selectBindingRef.current.value;
if (bindingId !== "new_card") {
webSdkPaymentForm.selectBinding(bindingId);
saveCardContainerRef.current.style.display = "none";
} else {
webSdkPaymentForm.selectBinding(null);
saveCardContainerRef.current.style.display = "";
}
};
return (
<div className="container">
<div className="websdk-form">
<div className="card-body">
<div
className="col-12"
id="select-binding-container"
onChange={handleSelectBinding}
>
<select
className="form-select"
id="select-binding"
ref={selectBindingRef}
aria-label="Default select example"
>
<option value="new_card">Плащане с нова карта</option>
</select>
</div>
<div className="col-12 input-form">
<label htmlFor="pan" className="form-label">
Номер на карта
</label>
<div id="pan" className="form-control" ref={panRef}></div>
</div>
<div className="col-6 col-expiry input-form">
<label htmlFor="expiry" className="form-label">
Валидност
</label>
<div id="expiry" className="form-control" ref={expiryRef}></div>
</div>
<div className="col-6 col-cvc">
<label htmlFor="cvc" className="form-label">
CVC / CVV
</label>
<div id="cvc" className="form-control" ref={cvcRef}></div>
</div>
<label className="col-12" id="save-card-container">
<input
className="form-check-input me-1"
ref={saveCardContainerRef}
type="checkbox"
value=""
id="save-card"
/>
Запазване на карта
</label>
</div>
<div className="pay-control">
<button
className="btn btn-primary btn-lg"
type="submit"
id="pay"
ref={payButtonRef}
onClick={handlePayment}
>
Плащане
</button>
</div>
<div
className="error my-2 text-center text-danger visually-hidden"
id="error"
></div>
</div>
</div>
);
}
export default App;