Вступ
Adyen — це популярне платіжне рішення, яке надає можливості для безпечної обробки платежів. Хоча Adyen пропонує готові компоненти, іноді нам потрібен більший контроль над інтерфейсом користувача (UI), при цьому зберігаючи стандарти безпеки. У цій статті ми дізнаємося, як реалізувати кастомізований інтерфейс для введення кредитної картки, використовуючи захищені поля Adyen з React-хуками та TypeScript.
Необхідні вимоги
Перед тим як почати, переконайтесь, що у вас є:
- Налаштований проект на React + TypeScript (ми будемо використовувати Vite)
- Облікові дані клієнта Adyen
- Node.js версії 18.17.0 або новішої
- Базові знання React-хуків та TypeScript
Налаштування проекту
Спочатку створіть новий проект Vite з React та TypeScript:
npm create vite@latest adyen-integration -- --template react-ts
cd adyen-integration
npm install
Потім встановіть необхідні пакети:
npm install @adyen/adyen-web
Реалізація
1. Ініціалізація конфігурації Adyen
Спочатку створимо кастомний хук для ініціалізації Adyen. Цей хук буде обробляти базову конфігурацію та створювати екземпляр checkout.
// src/hooks/useInitializeAdyen.ts
import { useState, useEffect } from "react";
import { AdyenCheckout, AdyenConfiguration } from "@adyen/adyen-web";
import "@adyen/adyen-web/styles/adyen.css";
interface AdyenConfiguration {
locale: string;
countryCode: string;
environment: "test" | "live" | "live-us" | "live-au" | "live-apse" | "live-in";
clientKey: string;
analytics: {
enabled: boolean;
};
}
export default function useInitializeAdyen() {
const [checkout, setCheckout] = useState(null);
useEffect(() => {
const initializeCheckout = async () => {
const configuration: AdyenConfiguration = {
locale: "en-US",
countryCode: "US",
environment: "test",
clientKey: "YOUR_CLIENT_KEY",
analytics: {
enabled: false,
},
};
try {
const checkoutInstance = await AdyenCheckout(configuration);
setCheckout(checkoutInstance);
} catch (error) {
console.error("Error initializing Adyen:", error);
}
};
initializeCheckout();
}, []);
return { checkout };
}
## Створення хука для Adyen Checkout
Далі ми створимо хук, щоб обробляти компонент кредитної картки з обробкою помилок:
// src/hooks/useAdyenCheckout.ts
import { useEffect, useReducer, useRef } from "react";
import useInitializeAdyen from "./useInitializeAdyen";
import { CustomCard, CustomCardConfiguration } from "@adyen/adyen-web";
```
const initialState = {
cardNumberError: "",
dateError: "",
cvcError: "",
};
function reducer(
state: typeof initialState,
action: { type: string; payload: string }
) {
switch (action.type) {
case "SET_CARD_NUMBER_ERROR":
return { ...state, cardNumberError: action.payload };
case "SET_DATE_ERROR":
return { ...state, dateError: action.payload };
case "SET_CVC_ERROR":
return { ...state, cvcError: action.payload };
default:
return state;
}
}
export default function useAdyenCheckout() {
const { checkout } = useInitializeAdyen();
const cardRef = useRef(null);
const [errorState, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
if (checkout) {
const configuration: CustomCardConfiguration = {
type: "card",
brands: ["visa", "mc"],
onError: (error) => {
console.error("Error:", error);
},
onChange: (state) => {
// Обробка помилок номера картки
if (state.errors?.encryptedCardNumber) {
dispatch({
type: "SET_CARD_NUMBER_ERROR",
payload: state.errors.encryptedCardNumber.errorMessage,
});
} else {
dispatch({ type: "SET_CARD_NUMBER_ERROR", payload: "" });
}
// Обробка помилок дати
if (state.errors?.encryptedExpiryDate) {
dispatch({
type: "SET_DATE_ERROR",
payload: "Будь ласка, введіть правильну дату",
});
} else {
dispatch({ type: "SET_DATE_ERROR", payload: "" });
}
// Обробка помилок CVC
if (state.errors?.encryptedSecurityCode) {
dispatch({
type: "SET_CVC_ERROR",
payload: "Будь ласка, введіть правильний CVC",
});
} else {
dispatch({ type: "SET_CVC_ERROR", payload: "" });
}
if (state.isValid) {
console.log("Картка є валідною:", state.data);
}
},
onBrand: (brand: { brand: string }) => {
console.log('Бренд картки:', brand);
},
styles: {
base: {
color: "#000",
fontSize: "14px",
fontFamily: "Arial, sans-serif",
},
},
placeholders: {
encryptedCardNumber: 'Номер картки',
encryptedExpiryMonth: 'MM',
encryptedExpiryYear: 'YY',
encryptedSecurityCode: 'CVC'
}
};
const card = new CustomCard(checkout, configuration).mount(
"#card-container"
);
cardRef.current = card;
}
return () => {
if (cardRef.current) {
cardRef.current.unmount();
}
};
}, [checkout]);
return { card: cardRef.current, errors: errorState };
}
3. Створення компонента для картки
Тепер створимо React-компонент, який буде рендерити нашу кастомізовану форму для кредитної картки:
// src/components/CreditCardForm.tsx
import React from 'react';
import useAdyenCheckout from '../hooks/useAdyenCheckout';
import './CreditCardForm.css';
const CreditCardForm: React.FC = () => {
const { errors } = useAdyenCheckout();
return (
<div>
Номер картки
{errors.cardNumberError && (
<div className="error">{errors.cardNumberError}</div>
)}
Дата закінчення терміну
{errors.dateError && (
<div className="error">{errors.dateError}</div>
)}
CVC / CVV
</div>
);
};
export default CreditCardForm;
## Стилізація
Додайте CSS для форми:
/* src/components/CreditCardForm.css */
.card-form {
max-width: 400px;
padding: 20px;
margin: 0 auto;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
```
.form-group {
margin-bottom: 16px;
}
label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
}
.form-row {
display: flex;
gap: 16px;
}
.card-field,
.date-fields,
.cvv-field {
height: 40px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
background: #fff;
}
.date-fields {
display: flex;
gap: 8px;
}
.card-field:focus-within,
.date-fields:focus-within,
.cvv-field:focus-within {
border-color: #0066cc;
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.2);
}
.error-message {
color: #d10244;
font-size: 12px;
margin-top: 4px;
}
/* Стилі для iframe Adyen */
iframe {
border: none;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
5. Оновлення компонента App
Нарешті, оновіть ваш компонент App, щоб використовувати форму кредитної картки:
// src/App.tsx
import CreditCardForm from './components/CreditCardForm'
import './App.css'
function App() {
return (
<div>
Інтеграція платежів Adyen
</div>
);
}
export default App;
Додайте базову стилізацію для додатку:
/* src/App.css */
.app {
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 40px;
}
Ключові особливості
1. Типова безпека:
- Повна підтримка TypeScript з правильними інтерфейсами
- Сильна перевірка типів для конфігурацій і стану
- Кращий досвід розробки з підказками типів
2. Обробка помилок:
- Деталізовані повідомлення про помилки для кожного поля
- Реальний час перевірки валідності
- Правильне управління станом помилок з використанням useReducer
3. Безпека:
- Дані картки безпосередньо шифруються Adyen
- Жодні чутливі дані не потрапляють на ваші сервери
- Відповідність стандартам PCI забезпечується Adyen
4. Кастомізація:
- Повний контроль над розташуванням інтерфейсу
- Кастомні стилі
- Гнучка обробка помилок і їх відображення
5. Валідність:
- Перевірка валідності номера картки в реальному часі
- Визначення типу картки
- Перевірка терміну дії та CVC
Кращі практики
- Завжди обробляйте помилки коректно та показуйте чіткі повідомлення
- Тестуйте ретельно як у тестовому, так і в реальному середовищі
- Ніколи не зберігайте та не реєструйте дані карток
- Завжди використовуйте HTTPS
- Оновлюйте Node.js та залежності до останніх версій
- Використовуйте TypeScript для кращої типізації
- Реалізуйте правильні стани завантаження
- Правильно обробляйте демонтаж компонентів
Важливі зауваження
- Замініть
YOUR_CLIENT_KEY
на ваш фактичний клієнтський ключ Adyen - Переконайтеся, що використовуєте Node.js версії 18.17.0 або вищої
- В середовищі продукції потрібно використовувати параметр “live”
- Рекомендується реалізувати стани завантаження та межі помилок
- Додайте правильну обробку помилок для продуктивного використання
- Тестуйте з різними типами карток і сценаріями помилок
Ресурси
Перекладено з: Creating Custom Credit Card UI with Adyen and React + TypeScript