Коротко: В 2015 году в КР работало три реалистичных варианта онлайн-оплаты: Элсом (QIWI-кошелёк, API через HTTP), банковский VISA/Mastercard эквайринг (РСК или МБанк, 3DS), СБЕР KG переводы. PayPal не работал. Stripe не работал. Наличными при доставке оставалось 60% оборота даже у лидеров рынка.
Что реально работало в 2015
| Метод | Охват | Комиссия | Сложность интеграции |
|---|---|---|---|
| Элсом (E-wallet) | 30% аудитории | 1-2% | Средняя |
| Банковская карта (РСК/МБанк) | 20% аудитории | 2-3% | Высокая (договор, 3DS) |
| Мобильный банкинг перевод | 15% | 0-0.5% | Высокая (ручная сверка) |
| Наличные при доставке | 60% | 0% | Нулевая |
Сумма больше 100% - клиенты выбирали несколько способов.
Интеграция Элсом (2015 API)
<?php
// ElsomPayment.php - интеграция с Элсом API 2015
class ElsomPayment {
private string $merchantId;
private string $secretKey;
private string $apiUrl = 'https://api.elsom.kg/v1/';
public function __construct(string $merchantId, string $secretKey) {
$this->merchantId = $merchantId;
$this->secretKey = $secretKey;
}
/**
* Создать платёжный запрос
* Пользователь будет перенаправлен на страницу Элсом для подтверждения
*/
public function createPayment(int $orderId, float $amount, string $description): array {
$timestamp = time();
$params = [
'merchant_id' => $this->merchantId,
'order_id' => (string)$orderId,
'amount' => number_format($amount, 2, '.', ''),
'currency' => 'KGS',
'description' => $description,
'callback_url' => 'https://myshop.kg/payment/callback/elsom',
'success_url' => 'https://myshop.kg/order/' . $orderId . '/success',
'fail_url' => 'https://myshop.kg/order/' . $orderId . '/fail',
'timestamp' => $timestamp,
];
// Подпись: HMAC-SHA256 от отсортированных параметров
ksort($params);
$signString = implode('|', $params);
$params['signature'] = hash_hmac('sha256', $signString, $this->secretKey);
$response = $this->request('POST', 'payments/create', $params);
return [
'payment_id' => $response['payment_id'],
'redirect_url' => $response['payment_url'],
];
}
/**
* Обработка callback от Элсом
* Вызывается когда пользователь подтверждает оплату
*/
public function handleCallback(array $postData): bool {
// Проверяем подпись
$signature = $postData['signature'] ?? '';
$data = $postData;
unset($data['signature']);
ksort($data);
$expectedSig = hash_hmac('sha256', implode('|', $data), $this->secretKey);
if (!hash_equals($expectedSig, $signature)) {
error_log('Elsom callback: invalid signature');
return false;
}
return $postData['status'] === 'SUCCESS';
}
private function request(string $method, string $endpoint, array $data): array {
$ch = curl_init($this->apiUrl . $endpoint);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
}
Контроллер оплаты
<?php
// PaymentController.php
class PaymentController {
private ElsomPayment $elsom;
private OrderRepository $orders;
public function initiate(int $orderId): void {
$order = $this->orders->findOrFail($orderId);
// Проверить что заказ принадлежит текущему пользователю
if ($order['user_id'] !== auth()->userId()) {
http_response_code(403);
die('Forbidden');
}
$paymentMethod = $_POST['method'] ?? 'elsom';
switch ($paymentMethod) {
case 'elsom':
$result = $this->elsom->createPayment(
$orderId,
$order['total'],
'Заказ #' . $orderId . ' на myshop.kg'
);
// Сохранить payment_id для последующей сверки
$this->orders->setPaymentId($orderId, $result['payment_id'], 'elsom');
header('Location: ' . $result['redirect_url']);
exit;
case 'card':
// Редирект на страницу банка
$bankUrl = $this->initiateBankPayment($order);
header('Location: ' . $bankUrl);
exit;
case 'cod':
// Наличные при доставке
$this->orders->updateStatus($orderId, 'confirmed');
header('Location: /order/' . $orderId . '/confirmed');
exit;
}
}
public function elsomCallback(): void {
$postData = json_decode(file_get_contents('php://input'), true);
if (!$this->elsom->handleCallback($postData)) {
http_response_code(400);
echo json_encode(['status' => 'error']);
return;
}
$orderId = (int)$postData['order_id'];
$order = $this->orders->find($orderId);
if ($order && $order['status'] === 'pending_payment') {
$this->orders->updateStatus($orderId, 'paid');
$this->orders->recordPayment($orderId, [
'method' => 'elsom',
'transaction_id' => $postData['transaction_id'],
'amount' => $postData['amount'],
'paid_at' => date('Y-m-d H:i:s'),
]);
// Отправить уведомление клиенту и менеджеру
$this->notifyPaymentReceived($order);
}
echo json_encode(['status' => 'ok']);
}
}
Банковский эквайринг: 3D Secure поток
<?php
// BankPayment.php - VISA/Mastercard через РСК банк
class BankPayment {
private string $terminalId;
private string $merchantPassword;
private string $bankGateway = 'https://pay.rsk.kg/gateway/';
public function initiate(int $orderId, float $amount, string $returnUrl): string {
$params = [
'TerminalID' => $this->terminalId,
'Amount' => (int)($amount * 100), // В тыйынах (копейках сома)
'OrderID' => $orderId,
'Currency' => '417', // KGS по ISO 4217
'Merchant_data' => base64_encode(json_encode([
'order_id' => $orderId,
'amount' => $amount,
])),
'returnUrl' => $returnUrl,
];
$params['Signature'] = $this->sign($params);
// Форма POST на страницу банка
$html = '<form method="POST" action="' . $this->bankGateway . 'pay" id="bankForm">';
foreach ($params as $key => $value) {
$html .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($value) . '">';
}
$html .= '</form><script>document.getElementById("bankForm").submit()</script>';
return $html;
}
public function processReturn(array $data): bool {
if (!$this->verifySignature($data)) {
return false;
}
return $data['Result'] === '000'; // '000' = успешная оплата
}
private function sign(array $params): string {
$str = $params['TerminalID'] . $params['Amount'] . $params['OrderID'] . $this->merchantPassword;
return md5($str);
}
}
База данных платежей
CREATE TABLE payments (
id INT AUTO_INCREMENT PRIMARY KEY,
order_id INT NOT NULL,
method ENUM('elsom','card','cod','mobile_transfer') NOT NULL,
status ENUM('pending','completed','failed','refunded') DEFAULT 'pending',
amount DECIMAL(10,2) NOT NULL,
currency CHAR(3) DEFAULT 'KGS',
transaction_id VARCHAR(100), -- ID транзакции в платёжной системе
raw_response TEXT, -- Сохраняем сырой ответ для разбора споров
paid_at DATETIME,
created_at DATETIME DEFAULT NOW(),
INDEX idx_order (order_id),
INDEX idx_transaction (transaction_id)
) ENGINE=InnoDB;
Главная проблема 2015 года: доверие к онлайн-оплате
Технически мы сделали всё правильно. Но 60% клиентов выбирали «наличными курьеру».
Решение, которое сработало у нас: скидка 3% при онлайн-оплате. За три месяца доля онлайн-оплат выросла с 18% до 35%. Люди были готовы платить онлайн - им нужен был весомый повод начать.
К 2018 году это соотношение перевернулось само по себе: молодая аудитория перешла на карты без всяких скидок.