Мок-сервер та локальне тестування
Тестування інтеграцій часто є однією з найскладніших частин побудови сучасних вебдодатків. Зовнішні пісочниці (наприклад, Stripe, Razorpay, Braintree, Paytm) можуть працювати повільно, мати ліміти на кількість запитів або їх важко примусово перевести в специфічні стани помилок (такі як 503 Service Unavailable або висока затримка мережі).
Для вирішення цієї проблеми MirApi Gateway надає виділене високопродуктивне середовище для тестування та мокінгу відповідей за адресою mock.mirapi.io. Це дозволяє безпечно будувати стійкі інтеграції та перевіряти поведінку системи за будь-яких умов.
1. Вступ до MirApi Mock Server
Section titled “1. Вступ до MirApi Mock Server”Мок-сервер MirApi (https://mock.mirapi.io) призначений для імітації відповідей від сторонніх платіжних провайдерів та інших API без використання реальних API-ключів, мерчант-акаунтів або вичерпання лімітів на кількість запитів.
Основні можливості:
Section titled “Основні можливості:”- Імітація успішних та помилкових відповідей: Перевірте, як ваш додаток реагує, коли зовнішній API повертає статуси помилок, такі як HTTP
500 Internal Server Error,502 Bad Gatewayабо503 Service Unavailable. - Штучні затримки (параметр
delayабо заголовки): Додавайте затримки запитів для перевірки таймаутів вашого HTTP-клієнта, роботи петель повторних спроб (retry loops) та автоматичних вимикачів (circuit breakers). - Групи авторизації: Мок-сервер підтримує поділ на тестові ключі. Ви можете використовувати ключі
SuperKey1,SuperKey2,SuperKey3для розділення стану або тестування багатоорендних (multi-tenant) конфігурацій.
2. Специфікація OpenAPI
Section titled “2. Специфікація OpenAPI”Усі ендпоінти мок-сервера задокументовані за стандартом OpenAPI (Swagger). Це дозволяє переглядати доступні ресурси, очікувані параметри та структури відповідей в інтерактивному режимі.
- 📄 Пряме посилання на специфікацію: https://mock.mirapi.io/openapi.yaml
- 🛠️ Інтерактивний Swagger Editor: Відкрийте https://editor.swagger.io та імпортуйте специфікацію за вказаним вище посиланням для швидкого тестування запитів прямо з браузера.
3. Тестування асинхронних вебхуків (Request Bin)
Section titled “3. Тестування асинхронних вебхуків (Request Bin)”Для локального тестування асинхронних вебхуків (X-Webhook-Callback) зазвичай доводиться використовувати Ngrok для прокидання локальних портів або відправляти дані на публічні сервіси на кшталт webhook.site, що може призвести до витоку конфіденційних тестових токенів.
MirApi пропонує приватний безстейтовий утилітарний Request Bin для перехоплення, аналізу та очищення вебхуків під час локальної розробки та автоматизованого тестування (CI/CD).
Робота з ендпоінтами Request Bin:
Section titled “Робота з ендпоінтами Request Bin:”POST /webhooks/incoming?id=your-test-id
Приймає та зберігає будь-які вебхуки від проксі-сервера. Використовуйте унікальний ідентифікаторyour-test-idдля уникнення колізій з іншими розробниками.GET /webhooks/inspect?id=your-test-id
Повертає список усіх отриманих вебхуків для вказаного ідентифікатора у хронологічному порядку із заголовками та тілом запитів.POST /webhooks/clear?id=your-test-id
Очищає збережені вебхуки з пам’яті сервера для вказаного ідентифікатора.
4. Приклад інтеграції редіректу на PHP
Section titled “4. Приклад інтеграції редіректу на PHP”Функція автоматичного редіректу (X-Extract-Redirect) дозволяє проксі-серверу витягти URL-адресу для оплати (Checkout URL) із JSON-відповіді стороннього провайдера за допомогою JSONPath і відразу перенаправити туди браузер клієнта з кодом 302 Found. Це позбавляє необхідності писати код парсингу та редіректу на вашому бекенді.
Нижче наведено основну cURL-логіку інтеграції на PHP (перші 50 рядків). Якщо вам потрібен повний готовий до запуску веб-скрипт із графічним інтерфейсом та обробкою помилок, ви можете розгорнути блок нижче.
<?php/** * MirApi Proxy Gateway - Interactive PHP Redirect Extraction Example (Core Logic) * * Цей фрагмент показує основні cURL-заголовки та обробку 302/301 редіректу від проксі-шлюзу. */
// Перевіряємо чи активовано дію редіректу (GET parameter action=redirect)if (isset($_GET['action']) && $_GET['action'] === 'redirect') { $gatewayUrl = 'https://proxy.mirapi.io/'; // Адреса проксі-шлюзу $apiKey = 'la_************************************************'; // Маскований API Key (замініть на свій X-MirApi-Key) $mockTargetUrl = 'https://mock.mirapi.io/echo/get/json'; // Цільовий мок-ендпоінт
$ch = curl_init($gatewayUrl);
$headers = [ 'X-MirApi-Key: ' . $apiKey, 'X-Target-URL: ' . $mockTargetUrl,
// Вказуємо проксі витягти $.urlResponse з мок-відповіді ("https://google.com") та повернути 302 redirect 'X-Extract-Redirect: $.urlResponse', 'Content-Type: application/json' ];
curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_HEADER => true, // Повертати заголовки для парсингу Location CURLOPT_FOLLOWLOCATION => false, // Не слідувати за редіректом автоматично CURLOPT_TIMEOUT => 10, ]);
$response = curl_exec($ch);
if ($response === false) { die('cURL error: ' . curl_error($ch)); }
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$redirectUrl = '';
// Парсимо заголовок Location з відповіді проксі if ($httpCode === 302 || $httpCode === 301) { $lines = explode("\r\n", $response); foreach ($lines as $line) { if (stripos($line, 'Location:') === 0) { $redirectUrl = trim(substr($line, 9)); break; } } }
// Виконуємо редірект у браузері користувача if (!empty($redirectUrl)) { header("Location: " . $redirectUrl); exit; }}?>Розгорнути повний PHP скрипт (з HTML UI та сторінкою помилки)
<?php/** * MirApi Proxy Gateway - Interactive PHP Redirect Extraction Example * * This script combines a beautiful user interface with PHP cURL logic to test redirect extraction. * You can open this file directly in your browser and click the button to run the test. */
// Check if the redirect action is triggered (GET parameter action=redirect)if (isset($_GET['action']) && $_GET['action'] === 'redirect') { $gatewayUrl = 'https://proxy.mirapi.io/'; // Proxy Gateway address $apiKey = 'la_************************************************'; // Masked API Key (replace with your X-MirApi-Key) $mockTargetUrl = 'https://mock.mirapi.io/echo/get/json'; // Target mock endpoint
$ch = curl_init($gatewayUrl);
$headers = [ 'X-MirApi-Key: ' . $apiKey, 'X-Target-URL: ' . $mockTargetUrl,
// Instruct the proxy to extract $.urlResponse from mock response (which is "https://google.com") and return a 302 redirect 'X-Extract-Redirect: $.urlResponse', 'Content-Type: application/json' ];
curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_HEADER => true, // Return headers in response to parse Location CURLOPT_FOLLOWLOCATION => false, // Do not automatically follow redirects CURLOPT_TIMEOUT => 10, ]);
$response = curl_exec($ch);
if ($response === false) { die('cURL error: ' . curl_error($ch)); }
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
$redirectUrl = '';
// Parse the Location header from proxy response if ($httpCode === 302 || $httpCode === 301) { $lines = explode("\r\n", $response); foreach ($lines as $line) { if (stripos($line, 'Location:') === 0) { $redirectUrl = trim(substr($line, 9)); break; } } }
// Perform final browser redirect if redirect URL was found if (!empty($redirectUrl)) { header("Location: " . $redirectUrl); exit; } else { // Detailed error report page ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Redirect Error</title> <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet"> <style> body { font-family: 'Plus Jakarta Sans', sans-serif; background: #020617; color: #f1f5f9; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; padding: 20px; } .card { background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(16px); padding: 2.5rem; border-radius: 20px; max-width: 600px; border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 10px 30px -10px rgba(0,0,0,0.5); } h1 { color: #f43f5e; font-size: 1.5rem; margin-top: 0; } pre { background: #090d16; padding: 1.2rem; border-radius: 12px; overflow-x: auto; color: #38bdf8; font-family: monospace; font-size: 0.85rem; border: 1px solid rgba(255,255,255,0.05); } .btn { display: inline-block; background: #3b82f6; color: white; padding: 0.8rem 1.8rem; border-radius: 10px; text-decoration: none; font-weight: 600; margin-top: 1rem; transition: background 0.2s; border: none; cursor: pointer; } .btn:hover { background: #2563eb; } </style> </head> <body> <div class="card"> <h1>Redirect Extraction Failed</h1> <p>Proxy returned status <strong><?php echo $httpCode; ?></strong> instead of the expected 302 Found.</p> <p>This may indicate that the proxy server has not been updated to the latest binary version, or the target response could not be parsed successfully.</p> <pre><?php echo htmlspecialchars($response); ?></pre> <a href="php_redirect_example.php" class="btn">Back to Test</a> </div> </body> </html> <?php exit; }}?><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MirApi PHP Redirect Test</title> <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap" rel="stylesheet"> <style> :root { --bg-gradient: linear-gradient(135deg, #0f172a 0%, #020617 100%); --panel-bg: rgba(30, 41, 59, 0.65); --panel-border: rgba(255, 255, 255, 0.08); --text-primary: #f8fafc; --text-secondary: #94a3b8; --primary: #3b82f6; --primary-hover: #2563eb; --shadow: 0 20px 40px -15px rgba(0, 0, 0, 0.6); }
body { font-family: 'Plus Jakarta Sans', sans-serif; background: var(--bg-gradient); color: var(--text-primary); min-height: 100vh; display: flex; justify-content: center; align-items: center; margin: 0; padding: 20px; box-sizing: border-box; }
.container { background: var(--panel-bg); backdrop-filter: blur(20px); border: 1px solid var(--panel-border); padding: 3.5rem 2.5rem; border-radius: 24px; max-width: 480px; width: 100%; text-align: center; box-shadow: var(--shadow); animation: fadeIn 0.5s ease-out; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.icon { font-size: 3.5rem; margin-bottom: 1.5rem; display: inline-block; animation: pulse 2.5s infinite; }
@keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.08); } 100% { transform: scale(1); } }
h1 { font-size: 1.8rem; font-weight: 700; margin: 0 0 1rem 0; background: linear-gradient(to right, #60a5fa, #a78bfa); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
p { color: var(--text-secondary); font-size: 0.95rem; line-height: 1.6; margin: 0 0 2.2rem 0; }
.card-details { background: rgba(15, 23, 42, 0.45); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 14px; padding: 1.2rem; margin-bottom: 2.2rem; text-align: left; font-size: 0.85rem; }
.detail-row { display: flex; justify-content: space-between; margin-bottom: 0.6rem; } .detail-row:last-child { margin-bottom: 0; } .detail-label { color: var(--text-secondary); } .detail-value { font-family: monospace; color: #38bdf8; font-weight: 500; }
.btn-action { display: block; width: 100%; background: var(--primary); color: white; padding: 1.1rem 2rem; border-radius: 12px; text-decoration: none; font-weight: 600; font-size: 1rem; border: none; cursor: pointer; box-shadow: 0 4px 18px 0 rgba(59, 130, 246, 0.35); transition: all 0.25s ease-in-out; box-sizing: border-box; }
.btn-action:hover { background: var(--primary-hover); transform: translateY(-2px); box-shadow: 0 6px 24px 0 rgba(59, 130, 246, 0.45); }
.btn-action:active { transform: translateY(0); }
.footer-note { margin-top: 1.8rem; font-size: 0.75rem; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; } </style></head><body> <div class="container"> <span class="icon">⚡</span> <h1>MirApi Redirect Flow</h1> <p>Click the button below to initiate the test. The PHP backend will send a request to the proxy, which will extract the <strong>google.com</strong> link from the mock response and instantly redirect your browser there.</p>
<div class="card-details"> <div class="detail-row"> <span class="detail-label">Proxy Gateway:</span> <span class="detail-value">proxy.mirapi.io</span> </div> <div class="detail-row"> <span class="detail-label">Target Upstream:</span> <span class="detail-value">mock.mirapi.io/...</span> </div> <div class="detail-row"> <span class="detail-label">JSONPath Header:</span> <span class="detail-value">$.urlResponse</span> </div> </div>
<a href="?action=redirect" class="btn-action">Execute Redirect</a>
<div class="footer-note"> Demonstration of X-Extract-Redirect Header </div> </div></body></html>5. Демонстрація Body Mapping (Правила Дашборду)
Section titled “5. Демонстрація Body Mapping (Правила Дашборду)”Ви можете автоматично перейменовувати та реструктурувати поля у тілі запитів (request payloads) на рівні проксі, що позбавляє від необхідності писати класи-адаптери у ваших сервісах.
Налаштування в Дашборді:
Section titled “Налаштування в Дашборді:”- Зайдіть у редагування вашого маршруту
CascadeURLна дашборді MirApi. - Для цілі (Target), яка веде на
https://mock.mirapi.io/auth2/sameresponseабоauth2/post, встановіть у полі Body Map наступне правило:data.amount=>data.sum, action=>event_type
Запит для перевірки:
Section titled “Запит для перевірки:”Надішліть запит через cURL (замініть API-ключ на ваш власний X-MirApi-Key):
curl --location 'https://proxy.mirapi.io/' \--header 'X-MirApi-Key: la_************************************************' \--header 'X-Route-Key: CascadeURL' \--header 'Content-Type: application/json' \--data '{ "action": "test-webhook", "data": { "amount": 10222 }}'Очікуваний результат:
Section titled “Очікуваний результат:”Оскільки мок-сервер робить ехо-відповідь, ви отримаєте JSON, в якому ключі були автоматично перейменовані проксі-сервером перед відправкою на мок-сервер:
{ "event_type": "test-webhook", "data": { "sum": 10222 }}6. Перевірка Extract JSONPath Map (Трансформація відповіді)
Section titled “6. Перевірка Extract JSONPath Map (Трансформація відповіді)”Цю функцію можна протестувати динамічно за допомогою HTTP-заголовків запиту:
X-Extract-Map— мапить обрані JSONPath-поля у нову структуру відповіді.X-Extract-Redirect— витягує одне поле (наприклад, посилання) і повертає статус302 Found(Redirect) клієнту.
Для тесту використаємо мок-ендпоінт GET https://mock.mirapi.io/echo/get/json, який повертає таку структуру:
{ "success": "true", "message": "reqres standard echo response", "urlResponse": "https://google.com", "amount": "100"}Тест 1: Зміна структури відповіді за допомогою X-Extract-Map
Section titled “Тест 1: Зміна структури відповіді за допомогою X-Extract-Map”Ми хочемо дістати лише суму та повідомлення і перейменувати їх у total та info.
Запустіть запит:
Section titled “Запустіть запит:”curl -i --location 'https://proxy.mirapi.io/' \--header 'X-MirApi-Key: la_************************************************' \--header 'X-Target-URL: https://mock.mirapi.io/echo/get/json' \--header 'X-Extract-Map: $.amount=>total, $.message=>info'Очікуваний результат:
Section titled “Очікуваний результат:”Проксі перехопить оригінальну відповідь мок-сервера, відфільтрує її за вашими правилами та поверне новий JSON:
{ "total": "100", "info": "reqres standard echo response"}Тест 2: Автоматичний редірект за допомогою X-Extract-Redirect
Section titled “Тест 2: Автоматичний редірект за допомогою X-Extract-Redirect”Ми хочемо витягти поле urlResponse (значення якого https://google.com) і змусити проксі зробити автоматичний редірект клієнта туди.
Запустіть запит:
Section titled “Запустіть запит:”(Використовуйте curl без прапорця --location, щоб побачити статус редіректу):
curl -i 'https://proxy.mirapi.io/' \--header 'X-MirApi-Key: la_************************************************' \--header 'X-Target-URL: https://mock.mirapi.io/echo/get/json' \--header 'X-Extract-Redirect: $.urlResponse'Очікуваний результат:
Section titled “Очікуваний результат:”Проксі поверне HTTP-статус 302 Found із заголовком редіректу на Google:
HTTP/1.1 302 FoundLocation: https://google.comContent-Length: 0