Налаштування інтеграції LMS з платіжними системами (продаж курсів)
Монетизація LMS — це не лише кнопка "Купити". Повна інтеграція з платіжною системою включає разові покупки, підписки, купони, розстрочку, повернення та коректний облік транзакцій.
Вибір платіжної системи
| Система | Регіони | Особливості |
|---|---|---|
| Stripe | Весь світ | Найкраще API, вбудовані підписки, Stripe Checkout |
| PayPal | Весь світ | Широка довіра користувачів, PayPal Express |
| ЮКасса | Росія/СНД | Російські карти, РНКО, QR-код |
| LiqPay | Україна | Приват24, широко відомий |
| Paddle | SaaS-продукти | Merchant of Record, берує на себе ПДВ |
Для міжнародних LMS — Stripe як основа + регіональні шлюзи для конкретних ринків.
Stripe: базова інтеграція
// Бекенд: створення Checkout Session
async function createCheckoutSession(userId, courseId, priceId, couponId = null) {
const user = await db.users.findByPk(userId);
const course = await db.courses.findByPk(courseId);
const session = await stripe.checkout.sessions.create({
customer_email: user.email,
client_reference_id: `${userId}:${courseId}`, // Пов'яжемо у webhook
line_items: [{
price: priceId, // Stripe Price ID
quantity: 1,
}],
discounts: couponId ? [{ coupon: couponId }] : [],
mode: 'payment', // Або 'subscription' для підписок
success_url: `${process.env.APP_URL}/courses/${courseId}?payment=success&session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.APP_URL}/courses/${courseId}?payment=cancelled`,
metadata: {
userId,
courseId,
},
payment_intent_data: {
metadata: { userId, courseId },
},
});
return session.url;
}
Обробник Webhook
Не покладайтесь на success_url для підтвердження платежу — користувачі можуть його підробити. Завжди використовуйте webhooks:
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook Error: ${err.message}`);
}
switch (event.type) {
case 'checkout.session.completed': {
const session = event.data.object;
if (session.payment_status === 'paid') {
const { userId, courseId } = session.metadata;
await enrollStudentAfterPayment(userId, courseId, session.id);
}
break;
}
case 'charge.refunded': {
await handleRefund(event.data.object);
break;
}
case 'customer.subscription.deleted': {
await revokeSubscriptionAccess(event.data.object.customer);
break;
}
}
res.json({ received: true });
});
Моделі монетизації
Разова покупка — стандартний Stripe Payment. Студент платить один раз, отримує доступ назавжди (або на N місяців).
Підписка — Stripe Subscriptions. Доступ до всіх курсів поки підписка активна. При скасуванні — скасуємо доступ через webhook customer.subscription.deleted.
Розстрочка — кілька платежів через Stripe payment_intent з installment_plan або вручну через scheduled invoices.
Корпоративні ліцензії — покупка N місць для команди. У БД: таблиця licenses → license_seats → прив'язка до користувачів.
Промокоди
// Створення промокода
const coupon = await stripe.coupons.create({
name: 'SUMMER2026',
percent_off: 30, // Або amount_off у копійках
duration: 'once',
redeem_by: Math.floor(new Date('2026-09-01').getTime() / 1000),
max_redemptions: 500,
});
const promotionCode = await stripe.promotionCodes.create({
coupon: coupon.id,
code: 'SUMMER2026',
restrictions: {
first_time_transaction: false,
},
});
На фронтенді — поле введення промокода з валідацією через Stripe API перед оформленням.
ЮКасса для російських користувачів
use YooKassa\Client;
$client = new Client();
$client->setAuth(SHOP_ID, SECRET_KEY);
$payment = $client->createPayment([
'amount' => ['value' => '3900.00', 'currency' => 'RUB'],
'capture' => true,
'confirmation' => [
'type' => 'redirect',
'return_url' => env('APP_URL') . '/payment/return',
],
'description' => "Курс: {$course->title}",
'metadata' => ['user_id' => $userId, 'course_id' => $courseId],
], uniqid('', true));
return $payment->getConfirmation()->getConfirmationUrl();
Облік транзакцій
CREATE TABLE transactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id),
course_id UUID REFERENCES courses(id),
provider VARCHAR(50), -- 'stripe', 'yookassa', 'paypal'
provider_payment_id VARCHAR(200) UNIQUE,
amount NUMERIC(10,2),
currency VARCHAR(3),
status VARCHAR(50), -- 'pending', 'succeeded', 'failed', 'refunded'
coupon_code VARCHAR(100),
discount_amount NUMERIC(10,2) DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
Терміни
Інтеграція Stripe з разовою покупкою, webhook, зарахуванням на курс та промокодами — 4–5 днів. Модель підписки — додатково 2–3 дні. ЮКасса — 2–3 дні. Корпоративні ліцензії — 3–4 дні.







