Получаем статистику расходов по MCC: Тинькофф и Рокетбанк

rfjtn-y0tmebcsaamtvxkibqceo.png

MCC (Merchant Category Code) — код категории продавца, используемый при операциях с банковскими картами. По этому коду банк определяет, товар какой категории приобрёл клиент. От него зависит, начислят вам кэшбэк, комиссию или отменят льготный период.

В век, когда компании зарабатывают на данных, собираемых с пользователей, грех не воспользоваться обратным ходом: использовать данные о себе, собранные компаниями, чтобы, если не заработать на этом, то хотя бы сэкономить.

Зная, какие суммы вы тратите по каждому MCC-коду, можно рассчитать, какая банковская карта принесёт большую выгоду. Поэтому, если вы достаточное время пользовались картой Тинькофф или Рокетбанк, эта статья вам пригодится, чтобы получить статистику трат по каждому MCC.

Личный кабинет Тинькофф Банка показывает MCC операции при клике по ней.

3sx-mpitay6kwlkydd_wlthcv5y.png

Так можно посмотреть с десяток операций, но если нам нужно собрать статистику за всё время использования карты, это дело стоит автоматизировать. Попробуем достать данные в удобном для программной обработки виде.

Для этого потребуются инструменты разработки браузера, а конкретно — анализ сетевой активности (вкладка Network в Google Chrome). Если включить анализатор и после этого попросить личный кабинет отобразить операции «За всё время», то мы быстро найдём желаемый запрос.

xj3btey4mplwedtxsz90hf79gdw.png

Можно заметить, что сервер действительно отдал данные за всё время, а в JSON-ответе присутствуют искомые MCC.

rbhkhnl9dznxkqzttw13cqgecku.png

Для удобства обработки данных сымитируем запрос на Python. Я использую Jupyter Notebook.

tm0jxtg_knssyrpcjmlaokwgzle.png

Отправим POST-запрос на URL https://api.tinkoff.ru/v1/grouped_requests с идентичными параметрами sessionid и _methods и данными requestsData.

import requests

session_id = "b785Q2R5US2AZo2p5JoCtNQNkbmYsJbl.ds-api02"
methods = "operations" # нас интересует получение только операций, payments не нужны
params = {'sessionid': session_id, '_methods': methods}

# не мудрствуя лукаво просто скопируем requestsData из запроса
requests_data = '[{"key":0,"operation":"operations","params":{"wuid":"28a44beaeee7460b94dbdd0aa0dc935a","account":"5059373083","start":1136062800000,"end":1529269199999}}]'
data = {'requestsData': requests_data}

response = requests.post('https://api.tinkoff.ru/v1/grouped_requests', params=params, data=data)

Если мы всё правильно сделали, то ввод response.text в консоли выведет тело ответа. Осталось разобрать эти данные и сохранить нужные нам фрагменты.

import csv

operations = []

payload = response.json()['payload']
for key in payload:
    feed = payload[key]['payload']
    for operation in feed:
        mcc = operation['mcc']

        # MCC со значениями < 100 используются банком для операций не связанных с покупками
        if (mcc > 100):

            # если наименование магазина не указано, возьмём описание операции
            if 'merchant' in operation:
                merchant_name = operation['merchant']['name']
            else:
                merchant_name = operation['description']

            # в accountAmount отображается стоимость покупки в «родной» валюте
            cost = operation['accountAmount']['value']                
            operations.append((mcc, cost, merchant_name))
            print(mcc, cost, merchant_name)

# выведем всё в csv
output = open("tinkoff.csv",'w')
wr = csv.writer(output)

for item in operations:
    wr.writerow(item)

На выходе получим таблицу из MCC-кодов, стоимостей покупок и наименований магазинов.

Рокетбанк показывает MCC операции в квитанции. Поэтому нам потребуется собрать ссылки на квитанции всех операций.

gu5bclay6iqriyw8r6nn6fqc9zi.png

С Рокетбанком чуть сложнее, потому что доступ в личный кабинет осуществляется только через мобильное приложение. Я расскажу только о том, что заработало у меня, и только про Android. Установим на компьютер анализатор Charles и пропустим трафик с телефона через него.

Для этого телефон и компьютер должны быть подключены к одной сети. Потребуется узнать IP компьютера в локальной сети. Например, с помощью ifconfig.

Далее настроим телефон для работы через прокси-сервер Charles. В Android 7.0 это делается в настройках Wi-Fi при длинном нажатии по подключенной сети. Укажем IP компьютера и порт 8888, по умолчанию используемый Charles.

dxxks9hc5_o7xnhtf3ke2mgh5d4.png

Само собой разумеется, что приложение Рокетбанка использует TLS при взаимодействии с сервером и просто так прослушать трафик мы не сможем. Charles поддерживает перехват трафика с подменой TLS-сертификата, то есть реализует MITM-атаку. Однако для этого устройство должно доверять корневому сертификату Charles.

Корневой сертификат Charles устанавливается при переходе по ссылке https://chls.pro/ssl с мобильного устройства при включённом проксировании. Кроме того, нужно добавить rocketbank.ru в список проксируемых хостов в Proxy → SSL Proxying Settings.

hwqiup8wmrvrqlqjdgiwjdfmbbq.png

Но и этого будет мало, так как в Android 7.0 по умолчанию приложения не доверяют пользовательским центрам сертификации. Безопасность! Приложение должно быть скомпилировано с соответствующим разрешением. Препятствие? Ни разу. Декомпилируем и скомпилируем как нам надо.

Сперва достанем пакет приложения с телефона. Это можно сделать с помощью Android Debug Bridge или приложения Apk Extractor. Первый способ у меня на Android 7.0 не сработал, а второй с задачей справился.

Забираем пакет на компьютер и декомпилируем с помощью apktool.

apktool d rocket.apk 

Необходимо добавить файл с конфигурацией сетевой безопасности по пути res/xml/network_security_config.xml. Подробнее про формат файла конфигурации можно почитать здесь, нам же хватит следующей настройки:

 
   
     
      
       
     
   
 

На этот файл конфигурации нужно указать в манифесте приложения (AndroidManifest.xml в корне), добавив параметр android: networkSecurityConfig в тег application.

...

Теперь скомпилируем.

apktool b rocket

Осталось подписать приложение, так как неподписанные приложения не устанавливаются. Используем для этого программу для подписи со встроенным тестовым сертификатом.

java -jar sign.jar rocket.apk

Удалите оригинальное приложение Рокетбанка с телефона, скопируйте изменённый пакет на телефон и установите (например, через приложение «Файлы»).

Теперь перехват трафика в Charles должен работать. Откройте приложение Рокетбанка, авторизуйтесь и смотрите в Charles. Вот она, наша ссылка на квитанцию.

bcsqo4cleywt1uwepjviz_ciqtq.png

Снова вернёмся к Python и сымитируем этот GET-запрос, только не будем мелочиться и попросим сервер отдать нам данные обо всех операциях. Ну или хотя бы о первых 999999.

token = 'c8ccb54b-09e3-4608-a5b4-7914a92c21f3206582'
params = {'token': token, 'page': 1, 'per_page': 999999}

Опытным путём выяснено, что сервер доверяет сессии только в том случае, если вместе с токеном отправлены корректные x-device-id, x-time и x-sig. Нам не жалко, ведь ничего придумывать и считать не придётся, просто скопируем.

x_device_id = 'ANDROID_C6FBB57CD433E756_899EE771-4AC5-46ED-44A1-656CE47A417B'
x_time = '1529194008'
x_sig = 'c486365013ddebe8b7f4599afbf73d26'
headers = {'x-device-id': x_device_id, 'x-time': x_time, 'x-sig': x_sig}

response = requests.get('https://rocketbank.ru/api/v5/operations/sexy_feed', params=params, headers=headers)

Для выдёргивания MCC из квитанции будет достаточно регулярки. Пробегаемся по операциям, читаем квитанцию, прогоняем её через регулярку и всё что надо у нас в кармане.

import re

regex = re.compile('MCC:<.+?>(\d+)')

operations = []
feed = response.json()['feed']

for item in feed:

    if item[0] == 'operation':
        operation = item[1]
        merchant_name = operation['merchant']['name']
        receipt_url = operation['receipt_url']
        cost = operation['money']['amount']

        # считаем только расходы
        if cost < 0:
            receipt = requests.get(receipt_url)
            match = regex.search(receipt.text)

            if match is not None:
                mcc = match[1]
                operations.append((mcc, -cost, merchant_name))
                print(mcc, -cost, merchant_name)
            else:
                # если MCC не найден, то запишем вместо него название магазина,
                # так как оплата услуг проходит без указания MCC
                operations.append((merchant_name, -cost))
                print(merchant_name, -cost)

output = open("rocket.csv",'w')
wr = csv.writer(output)

for item in operations:
    wr.writerow(item)

Пожалуй, можно удалить изменённое приложение и установить обратно оригинальное.

Объединив данные от двух банков, сгруппировав операции по MCC с помощью сводной таблицы (Pivot Table), вручную почистив данные от переводов и снятия наличных и местами сгруппировав в похожие категории, я получил следующую картину:

j15aqayl7cwxi-xcsmpxxg1itb8.png

Теперь, воспользовавшись сервисом вроде mcc-codes.ru, можно подобрать карты с повышенным кэшбэком на наиболее затратные категории. И, соотнеся величину возможного кэшбэка со стоимостью годового обслуживания, определить, целесообразно ли оформлять конкретную карту.

Какая от этого выгода? При моих тратах и правильном подборе карт удастся сэкономить от 10 тысяч рублей в год. Стоит ли это того? Решать вам :)

© Habrahabr.ru