По любому вопросу мы в одном клике

Задать вопрос

Web SDK Payment

Описание

JavaScript-библиотека для отображения платежной формы на странице мерчанта. Данный способ подходит мерчантам как c высоким, так и с низким уровнем соотвтетсвия требованиям PCI DCC.

Сценарий работы:

  1. Мерчант регистрирует заказ через REST API в платежном шлюзе
  2. Полученный mdOrder (номер заказа) мерчант передает на страницу, где используется данная js-библиотека

Web SDK предоставляет возможность добавлять на свою платежную страницу поля ввода платежных данных через iframe непосредственно от платежного шлюза. Безопасность передачи данных обеспечивается шифрованием протокола HTTPS.

Плюсы:

Минусы:

Библиотека помогает в сборе данных карты, их валидации и проверке, совершении платежа и автоматическом перенаправлении покупателя на финальную страницу через указанный в настройках returnUrl.

Как использовать

Подключить скрипт

Тестовая среда

<script src="https://3dsec.berekebank.kz/payment/modules/multiframe/main.js"></script>

Боевая среда

<script src="https://securepayments.berekebank.kz/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

mdOrder string required
Идентификатор заказа

Описание полей в форме

apiContext string optional
Контекст (часть URL-адреса платежного шлюза после домена) для запросов API.
По умолчанию apiContext автоматически берется из ссылки, используемой для подключения скрипта modules/multiframe/main.js.

language string optional
Язык, используемый для локализации ошибок и плейсхолдеров.
Значение по умолчанию: en.

autofocus boolean optional
Автоматическое переключение фокуса при заполнении полей.
Значение по умолчанию - true

shouldMaskPan boolean optional
Маскировать поле ввода Pan.
По умолчанию false

shouldMaskExpiry boolean optional
Маскировать поле ввода Expiry.
По умолчанию false

shouldMaskCvc boolean optional
Маскировать поле ввода CVC.
По умолчанию true

showPanIcon boolean optional
Показывать иконку платежной системы.
По умолчанию true

panIconStyle CSSStyleDeclaration optional
Кастомные стили для иконки платежной системы

Общие стили для полей ввода

Кастомные стили для каждого из полей ввода (pan, expiry, cvc)

containerClassName string optional
Имя класса выставляемое дополнительно для контейнера.
Значение по умолчанию: field-container

onFormValidate (result: boolean) => void optional
Callback для обработки изменения валидации формы.
Например:
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() обычно выполняет следующие задачи:

  1. Удаляет все слушатели событий, которые были добавлены в экземпляр Web SDK.
  2. Очищает все созданные контейнеры полей ввода.

Пример метода destroy

document.querySelector("#destroy").addEventListener("click", function () {
  webSdkPaymentForm.destroy();
});

Стилизация

Стилизация контейнеров, в которые передаются поля ввода, определяется самостоятельно согласно дизайну вашей страницы. Различные состояния контейнеров поля ввода можно стилизовать с помощью следующих CSS-классов:

Параметр 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.

Стилизация полей ввода

Вы можете настроить внешний вид полей ввода. Для этого

Пример:

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 и т.п., мерчант обязан валидировать самостоятельно на своей стороне.

Примеры регулярных выражений для валидации дополнительных полей:

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, где вы можете, например, скрыть загрузчик или сделать что-либо еще. init() возвращает c.

Например:

webSdkFormWithoutBindings
  .init()
  .then((success) => {
    console.log('success', success)
    // Скрипт успешно инициализирован. Promise возвращает объект, содержащий некоторую полезную инфромацию о зарегистрированном 
    // в платежном шлюзе заказе. После этого можно убрать лоадер или выполнить другие действия:
    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.

Метод принимает следующие параметры:

phone String optional
Номер телефона клиента

email String optional
Email клиента

cardholderName String optional
Имя держателя карты.

jsonParams Object optional
Дополнительные поля. Например, вы можете отправить дополнительную информацию о заказе или любую другую полезную для вас информацию. Например, jsonParams: { "t-shirt-color": "черный", "size": "M" }

Обновление мандата Visa Secure Data Field

Обратите внимание на требования IPS Visa относительно дополнительных полей данных, необходимых для запросов на аутентификацию EMV 3DS. Мерчанты должны предоставлять полные и точные данные о транзакциях в своих запросах на аутентификацию. Мерчанты также должны гарантировать, что 3DS Method URL-адрес совершает сбор данных устройства для поддержки успешной аутентификации в случае, если 3DS Method URL-адрес предоставлен эмитентом.

Таким образом, сбор дополнительных полей для VISA является ответственнотью мерчанта. Вы можете ознакомиться c полным текстом требований в 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");
  });

Демонстрация без сохраненной карты

Для работы Web SDK Payment требует Order ID (идентификатор заказа зарегистрированный в платежном шлюзе).
Для рабочих целей - регистрируйте заказ через 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">(Должен поступать из бэкенда. Данный ввод предназначен только для демонстрации)</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.

Метод принимает следующие параметры:

phone String optional
Номер телефона клиента

saveCard boolean optional
Опция сохранения карты. Например, document.querySelector('#save-card').checked

email String optional
Email клиента

cardholderName String optional
Имя держателя карты.

jsonParams Object optional
Дополнительные поля. Например, вы можете отправить дополнительную информацию о заказе или любую другую полезную для вас информацию. Например, 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");
  });

Демонстрация с сохраненной картой

Для работы Web SDK Payment требует Order ID (идентификатор заказа зарегистрированный в платежном шлюзе).
Для рабочих целей - регистрируйте заказ через 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>ххххх-ххххх-ххххх-ххххх</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 = "";
            }
          });

        // Когда форма готова, можем скрыть загрузчик
        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 string optional
Номер заказа, переданный при инициализации

Объект с информацией о зарегистрированном заказе

Возможно наличие дополнительных полей, либо отсутствие некоторых полей из списка выше.

Пример

{
  "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 string optional
Адрес редиректа после совершения оплаты

Объект с информацией о проведенном платеже

Возможно наличие дополнительных полей, либо отсутствие некоторых полей из списка выше.

Пример

{
    "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://3dsec.berekebank.kz/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">Pay by new card</option>
            </select>
          </div>
          <div className="col-12 input-form">
            <label htmlFor="pan" className="form-label">
              Card number
            </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">
              Expiry
            </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"
            />
            Save card
          </label>
        </div>
        <div className="pay-control">
          <button
            className="btn btn-primary btn-lg"
            type="submit"
            id="pay"
            ref={payButtonRef}
            onClick={handlePayment}
          >
            Pay
          </button>
        </div>
        <div
          className="error my-2 text-center text-danger visually-hidden"
          id="error"
        ></div>
      </div>
    </div>
  );
}

export default App;
Категории:
eCommerce SDK
Категории
Результаты поиска