Skip to main content

ioka Elements

ioka Elements - это JavaScript SDK для встраивания карточных полей ioka в вашу checkout-страницу. Вы управляете внешним интерфейсом формы: версткой, labels, кнопкой оплаты, checkbox сохранения карты, ошибками и статусами. Карточные данные вводятся внутри защищенных iframe-полей ioka и не попадают в DOM вашего сайта.

Как работает оплата

  1. Ваш backend создает заказ в ioka.
  2. Backend возвращает frontend-у orderId и, если требуется для сценария с сохраненными картами, customerId и customerAccessToken.
  3. Frontend подключает ioka.js.
  4. Frontend создает checkout через IokaCheckout.create(...).
  5. Frontend монтирует поля pan, holder, expiry, cvc в ваши HTML-контейнеры.
  6. Покупатель вводит данные карты.
  7. Frontend вызывает checkout.pay(...).
  8. SDK валидирует поля, создает платеж и открывает 3DS, если он нужен.
  9. SDK вызывает onSuccess или onFail.
  10. Backend подтверждает финальный статус заказа через webhook или API.
Важно

Frontend не должен создавать заказ с секретным API key. Создавайте заказ только на backend-е и возвращайте в браузер только параметры, необходимые для оплаты.

1. Создайте заказ на backend-е

Пример ответа вашего backend-а:

{
"orderId": "ord_123",
"amountLabel": "12 500 ₸"
}

amountLabel нужен только для отображения суммы в интерфейсе. Источником истины должна быть сумма заказа, созданного в ioka.

Если на форме есть checkbox Сохранить карту, backend должен создать или найти Customer и создать Order с customer_id. На frontend по-прежнему достаточно вернуть orderId: SDK отправит save: true, а ioka сохранит карту к Customer, который привязан к Order.

Разные сценарии используют разные параметры:

СценарийЧто делает backendЧто передать в IokaCheckout.create(...)
Разовая оплата новой картойСоздать OrderorderId
Оплата новой картой с checkbox Сохранить картуСоздать или найти Customer, создать Order с customer_idorderId
Отдельное сохранение карты без оплатыСоздать или найти Customermode: "save", customerId, customerAccessToken
Оплата сохраненной картойСоздать Order с customer_id и/или card_idorderId, cardId; customerAccessToken нужен только если frontend сам получает список карт или события Customer

customerAccessToken не нужен для разовой оплаты и для checkbox saveCard. Он нужен только для отдельных customer-операций на frontend-е: например, standalone-сохранения карты, получения списка сохраненных карт или проверки событий карты.

2. Подключите SDK

Для production:

<script src="https://elements.ioka.kz/ioka.js"></script>

Для тестового магазина:

<script src="https://dev-elements.ioka.kz/ioka.js"></script>
ОкружениеScriptapiBase
Productionhttps://elements.ioka.kz/ioka.jshttps://api.ioka.kz
Тестовый магазинhttps://dev-elements.ioka.kz/ioka.jshttps://stage-api.ioka.kz

Если SDK загружен с нужного домена, elementsOrigin передавать не нужно. SDK определит origin из script src.

3. Добавьте контейнеры для полей

<form id="checkout-form">
<label>
Номер карты
<div id="card-pan" class="ioka-field"></div>
<span id="error-pan" class="field-error"></span>
</label>

<label>
Владелец карты
<div id="card-holder" class="ioka-field"></div>
<span id="error-holder" class="field-error"></span>
</label>

<label>
Срок действия
<div id="card-expiry" class="ioka-field"></div>
<span id="error-expiry" class="field-error"></span>
</label>

<label>
CVC
<div id="card-cvc" class="ioka-field"></div>
<span id="error-cvc" class="field-error"></span>
</label>

<label>
<input id="save-card" type="checkbox" />
Сохранить карту на этом сайте
</label>

<button id="pay" type="button">Оплатить</button>
<div id="status" role="status" aria-live="polite"></div>
</form>

4. Создайте checkout и смонтируйте поля

<script>
const payButton = document.getElementById("pay");
const statusNode = document.getElementById("status");
const saveCardInput = document.getElementById("save-card");

function setStatus(message) {
statusNode.textContent = message || "";
}

function setLoading(loading) {
payButton.disabled = loading;
payButton.textContent = loading ? "Обработка..." : "Оплатить";
}

function renderFieldState(state) {
if (!state || !state.field) return;

const field = document.getElementById(`card-${state.field}`);
const error = document.getElementById(`error-${state.field}`);

field?.classList.toggle("is-focused", Boolean(state.focused));
field?.classList.toggle("is-invalid", Boolean(state.error));
if (error) error.textContent = state.error?.message || "";
}

function renderValidation(result) {
Object.values(result.fields || {}).forEach(renderFieldState);
}

async function createCheckoutOnMerchantBackend() {
const response = await fetch("/api/create-ioka-checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ cartId: "cart_123" }),
});

if (!response.ok) {
throw new Error("Не удалось подготовить оплату");
}

return response.json();
}

async function initPaymentForm() {
setLoading(true);
setStatus("Подготовка оплаты...");

const params = await createCheckoutOnMerchantBackend();

const checkout = IokaCheckout.create({
apiBase: "https://api.ioka.kz",
orderId: params.orderId,
locale: "ru",
onFieldChange: renderFieldState,
onValidation: renderValidation,
onProcessing() {
setLoading(true);
setStatus("Обработка платежа...");
},
onThreeDSOpen() {
setStatus("Подтвердите оплату в окне 3DS.");
},
onThreeDSClose() {
setLoading(false);
setStatus("Подтверждение 3DS было закрыто.");
},
onPending() {
setStatus("Проверяем статус платежа...");
},
onSuccess() {
setLoading(false);
setStatus("Оплата прошла успешно.");
},
onFail(error) {
setLoading(false);
setStatus(error?.message || "Не удалось оплатить.");
},
});

const elements = checkout.elements({ height: "24px" });
elements.create("pan").mount("#card-pan");
elements.create("holder").mount("#card-holder");
elements.create("expiry").mount("#card-expiry");
elements.create("cvc").mount("#card-cvc");

payButton.addEventListener("click", async () => {
setLoading(true);
setStatus("");

try {
await checkout.pay({ saveCard: saveCardInput.checked });
} catch (error) {
setLoading(false);
if (error?.validation) renderValidation(error.validation);
setStatus(error?.message || "Проверьте данные карты.");
}
});

window.addEventListener("beforeunload", () => checkout.destroy());

setLoading(false);
setStatus("");
}

initPaymentForm().catch((error) => {
setLoading(false);
setStatus(error?.message || "Не удалось открыть оплату.");
});
</script>

Кастомизация полей

Внешний вид формы контролируется вашим CSS. ioka Elements монтирует iframe внутрь контейнера, а контейнер остается частью вашей страницы.

.ioka-field {
height: 46px;
padding: 11px 12px;
border: 1px solid #cfd6df;
border-radius: 10px;
background: #ffffff;
}

.ioka-field.is-focused {
border-color: #1f6feb;
}

.ioka-field.is-invalid {
border-color: #d92d20;
background: #fff8f7;
}

.field-error {
min-height: 18px;
margin-top: 5px;
color: #d92d20;
font-size: 12px;
}

Текст внутри iframe-полей настраивается через appearance.field:

const checkout = IokaCheckout.create({
apiBase: "https://api.ioka.kz",
orderId: params.orderId,
appearance: {
field: {
fontSize: 18,
fontFamily: "Inter, system-ui, sans-serif",
textColor: "#172033",
placeholderColor: "#7b8794",
bgColor: "transparent",
border: "none",
paddingX: 0,
paddingY: 0,
},
},
});

Доступные параметры appearance.field:

ПараметрОписание
fontSizeРазмер текста внутри поля
fontFamilyШрифт внутри поля
fontWeightНасыщенность шрифта
textColorЦвет введенного текста
placeholderColorЦвет placeholder
bgColorФон iframe-поля
borderBorder внутри iframe-поля
paddingXГоризонтальный padding внутри iframe-поля
paddingYВертикальный padding внутри iframe-поля

Checkbox сохранения карты - обычный HTML input. Для него не нужен отдельный ioka Element, потому что checkbox не собирает карточные данные.

await checkout.pay({ saveCard: saveCardInput.checked });

API

IokaCheckout.create(options)

Создает checkout instance.

ПараметрОбязателенОписание
apiBaseДаHost ioka API.
orderIdДаID заказа, созданного вашим backend-ом.
customerIdДля отдельных customer-сценариевID плательщика. Не нужен для обычной оплаты и checkbox saveCard, если Order уже создан с customer_id.
customerAccessTokenДля отдельных customer-операцийaccess_token плательщика. Не нужен для обычной оплаты и checkbox saveCard.
localeНетЛокаль сообщений: ru, kz или en. По умолчанию ru.
appearanceНетСтилизация текста внутри iframe-полей.
elementsOriginНетOrigin Elements. Обычно определяется автоматически из script src.

checkout.elements(options?)

Создает контроллер полей.

const elements = checkout.elements({ height: "24px" });

elements.create(type).mount(target)

Монтирует поле в DOM-контейнер.

elements.create("pan").mount("#card-pan");
elements.create("holder").mount("#card-holder");
elements.create("expiry").mount("#card-expiry");
elements.create("cvc").mount("#card-cvc");

Доступные типы:

ТипПоле
panНомер карты
holderВладелец карты
expiryСрок действия
cvcCVC

checkout.pay(options?)

Запускает оплату.

await checkout.pay();

Для оплаты с сохранением новой карты:

await checkout.pay({ saveCard: true });

Перед отправкой платежа SDK сам валидирует поля. Если форма невалидна, promise отклонится с code: "validation_error" и объектом validation.

checkout.validate()

Проверяет смонтированные поля без запуска оплаты.

const result = checkout.validate();

if (!result.valid) {
renderValidation(result);
}

checkout.update(options)

Обновляет параметры checkout, например при создании нового заказа без полной перезагрузки страницы.

checkout.update({
orderId: "ord_new",
customerAccessToken: "customer_access_token_new",
});

checkout.destroy()

Удаляет iframe-поля, 3DS overlay и listeners. Вызывайте destroy() при размонтировании страницы или компонента.

checkout.destroy();

3DS и финальный статус

Если для платежа нужен 3DS, SDK сам откроет fullscreen overlay поверх сайта, покажет банковское подтверждение и продолжит проверять статус заказа. Вам не нужно открывать action_url вручную.

Закрытие 3DS покупателем не является финальным платежным статусом. Финальный результат оплаты подтверждайте на backend-е через webhook или запрос заказа по API.

Безопасность

  • Не собирайте PAN, CVC и срок действия карты своими input-ами.
  • Не логируйте карточные данные.
  • Не передавайте секретный API key в браузер, HTML, JavaScript bundle или localStorage.
  • Используйте HTTPS.
  • Подтверждайте финальный статус заказа на backend-е.
  • Если на сайте настроен CSP, разрешите загрузку SDK и iframe ioka.

Пример CSP для production:

Content-Security-Policy:
script-src 'self' https://elements.ioka.kz;
frame-src 'self' https://elements.ioka.kz https://*.ioka.kz https:;
connect-src 'self' https://api.ioka.kz;

Для 3DS точный frame-src зависит от банка. Если окно 3DS не открывается, проверьте ошибки CSP в browser console.

Тестирование

Для тестового магазина используйте:

<script src="https://dev-elements.ioka.kz/ioka.js"></script>
const checkout = IokaCheckout.create({
apiBase: "https://stage-api.ioka.kz",
orderId: params.orderId,
});

Тестовые карты и пароль для 3DS указаны в разделе Тестирование.