Інтеграція з Visa/Mastercard для крипто-карти
Пряма інтеграція з Visa або Mastercard як principal member — процес тривалістю 12-24 місяці з капітальними вимогами $2М+. Реалістичний шлях для стартапу — робота через program manager або directly-connected issuer, яких вже є членство в мережах. Ця сторінка описує технічну сторону такої інтеграції.
Учасники екосистеми
Щоб зрозуміти куди інтегруватися, потрібно розуміти структуру:
Visa/Mastercard Network
↓
Principal Member Bank (BIN owner)
↓
Card Program Manager (Marqeta, Stripe Issuing, Galileo)
↓
Your Crypto Platform
↓
End User
Principal Member — банк з прямим членством у Visa/MC. Видає BIN-діапазони. Program Manager — технологічний посередник, надає API для видачі карт, обробки авторизацій. Бере на себе технічні та частково нормативні зобов'язання. Your Platform — обробляє крипто-сторону: зберігання активів, конвертація, користувацький інтерфейс.
Marqeta — основний шлях
Marqeta — лідуючий card issuing API. Використовується Cash App, DoorDash, Coinbase Card. Працює як program manager: ви видаєте карти через їхній API, вони обробляють авторизації та через Just-In-Time (JIT) funding запитують у вас кошти в момент кожної транзакції.
JIT Funding — ключова концепція
JIT (Just-In-Time) Funding — механізм, при якому Marqeta не тримає баланс користувача у себе. Замість цього: в момент кожної карткової авторизації Marqeta робить webhook запрос до вашого API, ви відповідаєте approve/decline з сумою. Це дозволяє застосувати вашу бізнес-логіку (перевірити крипто-баланс, конвертувати) в реальному часі.
// Ваш JIT Funding endpoint
app.post("/marqeta/jit-funding", async (req, res) => {
const { token, type, amount, currency_code, card_token } = req.body;
// Це webhook від Marqeta — потрібно відповісти за 2-3 секунди макс
const userId = await getUserByCardToken(card_token);
const user = await getUser(userId);
// Конвертуємо USD суму в крипто
const cryptoAmount = await convertUSDToCrypto(amount, user.preferredAsset);
// Перевіряємо та резервуємо баланс
const hasBalance = await reserveBalance(userId, cryptoAmount);
if (!hasBalance) {
return res.json({
jit_funding: {
token: token,
method: "pgfs.authorization",
user_token: userId,
amount: 0,
},
// decline
});
}
return res.json({
jit_funding: {
token: token,
method: "pgfs.authorization",
user_token: userId,
amount: amount,
currency_code: "USD",
},
});
});
Marqeta API — видача карт
import Marqeta from "@marqeta/core-api";
const marqeta = new Marqeta({
applicationToken: process.env.MARQETA_APP_TOKEN,
accessToken: process.env.MARQETA_ACCESS_TOKEN,
baseUrl: "https://sandbox.marqeta.com/v3",
});
// Створення користувача в Marqeta
async function createMarqetaUser(userId: string, userData: UserData) {
const marqetaUser = await marqeta.users.create({
token: userId,
first_name: userData.firstName,
last_name: userData.lastName,
email: userData.email,
birth_date: userData.birthDate,
address1: userData.address,
city: userData.city,
state: userData.state,
country: userData.country,
postal_code: userData.postalCode,
});
return marqetaUser;
}
// Видача віртуальної карти
async function issueVirtualCard(userId: string) {
const card = await marqeta.cards.create({
user_token: userId,
card_product_token: process.env.CARD_PRODUCT_TOKEN,
});
// Отримання PAN (номера карти) через безпечний канал
const cardDetails = await marqeta.cards.getShowPAN(card.token);
return {
cardToken: card.token,
last4: card.last_four,
expiration: card.expiration,
pan: cardDetails.pan, // тільки для відображення в app
cvv2: cardDetails.cvv_number, // тільки для відображення в app
};
}
3DS (3D Secure) для онлайн-транзакцій
Для онлайн-покупок (без фізичного POS-терміналу) потрібна 3DS верифікація. Marqeta підтримує 3DS 2.x через окремий endpoint:
// 3DS Challenge handler
app.post("/marqeta/3ds-challenge", async (req, res) => {
const { transaction_token, card_token, merchant } = req.body;
// Відправляємо користувачу push/SMS для підтвердження
await sendOTPToUser(card_token, transaction_token);
// Відповідь: запросити OTP (challenge) або затвердити без challenge (frictionless)
res.json({
three_ds_decision: "CHALLENGE",
otp_required: true,
});
});
Stripe Issuing — альтернатива
Stripe Issuing простіший у інтеграції, доступний у 30+ країнах. Не підтримує JIT funding повністю — баланс повинен бути pre-funded на рахунку Stripe. Це означає, що потрібно утримувати фіатний резерв у Stripe, що ускладнює крипто-інтеграцію.
Підходить для: B2B expense management карти, корпоративні карти з крипто-балансом. Не оптимальний для consumer крипто дебетових карт.
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
// Видача карти
const card = await stripe.issuing.cards.create({
cardholder: cardholderId,
currency: "usd",
type: "virtual",
status: "active",
spending_controls: {
spending_limits: [{ amount: 50000, interval: "daily" }], // $500/день
},
});
Авторизації та Settlement
Важливо розуміти різницю:
Авторизація — перевірка та блокування коштів. Відбувається миттєво. Ваш JIT endpoint повинен відповісти за 2-3 секунди.
Clearing/Settlement — фактичне списання. Відбувається за 1-3 робочих дні. До того моменту сума "заморожена".
Refund/Reversal — відмена транзакції. Може прийти дні після авторизації. Система повинна обробляти revert hold.
// Обробка фінального settlement
app.post("/marqeta/webhook", async (req, res) => {
const event = req.body;
switch (event.type) {
case "authorization":
// Hold: резервуємо крипто
await holdCrypto(event.card_token, event.amount);
break;
case "authorization.clearing":
// Settlement: конвертуємо та остаточно списуємо
await settleTransaction(event.transaction.token, event.amount);
break;
case "authorization.reversal":
// Відмена: звільняємо заблоковану крипто
await releaseHold(event.transaction.token);
break;
case "refund":
// Повернення: зараховуємо крипто назад
await refundCrypto(event.card_token, event.amount);
break;
}
res.status(200).send("OK");
});
Управління курсовим ризиком
При холді USD сум у крипто-еквіваленті — є ризик: курс зміниться між авторизацією (T+0) та settlement (T+1..3). Три підходи:
Стейблкоін за умовчанням (USDC/USDT). Жодного курсового ризику. Користувачі тримають стейблкоїни, оплата прямолінійна. Мінус: жодного крипто-апсайду.
Over-reservation. При авторизації резервуємо суму з буфером (+2-3%). При settlement надлишок повертається. Просте рішення для малих обсягів.
FX hedge. При авторизації фіксуємо курс через похідні або швидкий CEX trade. Складніше, дорожче, але точніше. Потрібно для великих обсягів або волатильних активів.
PCI DSS compliance
Робота з номерами карт (PAN) вимагає PCI DSS сертифікації. Рівні:
- SAQ A — якщо не обробляєте PAN безпосередньо (Marqeta зберігає, ви використовуєте тільки токени): мінімальні вимоги
- SAQ D — якщо зберігаєте/обробляєте PAN: повний audit, дорого
Рекомендація: використовуйте card tokenization (Marqeta Token Vault). Ваша система працює тільки з card_token, PAN ніколи не проходить через ваші серверерs.
Нормативний шлях
| Юрисдикція | Вимога | Термін | Вартість |
|---|---|---|---|
| EU | EMI ліцензія (Lithuania) | 6-12 мів | $50-200k |
| UK | FCA EMI | 9-18 мів | $100-300k |
| USA | MTL в кожному штаті | 12-24 мів | $500k+ |
| Singapore | MAS Major Payment Institution | 12-18 мів | $100-200k |
Мінімальний шлях для EU: реєстрація в Литві + паспортизація. Для решти світу — партнерство з вже ліцензованим емітентом.
Графік розроблення
- Marqeta інтеграція (JIT + card issuance + webhooks): 4-6 тижнів
- 3DS інтеграція: +2-3 тижні
- Crypto custody + конвертація: паралельно, 4-8 тижнів
- KYC/AML: 2-4 тижні
- Mobile app: 8-12 тижнів
- Compliance + тестування: 4-6 тижнів
- Всього (технічно): 5-7 місяців







