И так в марте 2022 Steam отключила в российском сегменте Steam все основные способы оплаты для пользователей из России.
У игроков осталось 3 пути:
Сразу приложу ссылку на репозиторий, в котором храниться этот проект и оговорюсь что его поддержку я закончил несколько месяце назад в связи с участившимся отказом QIWI в переводах на Steam через API, якобы из-за недостатка средств на балансе, при том что баланс всегда был с запасом, и вручную оплата спокойно проводилась.
Пример
Основные проблемы пользователя которые должен устранять бот.
Телеграмм бот.
Фронт: осуществляет интерфейс взаимодействия с пользователем
Бек: отправляет API запросы и анализирует ответы, обновляет данные в базе
MS SQL Server.
Ответственен за хранение всей информации в проекте, ниже расписаны содержащиеся в ней таблицы и назначение полей в них.
customers — Аккаунты которыми оперировал пользователь.
No - Номер строки (ПК)
TgID - Идентификатор пользователя в Телеграмм
NickName - Логин пользователя в Steam
KZ - Баланс тенге
Logined - Флаг указывающий на выбраны в данный момент аккаунт Steam
orders - Информация о заказах
No - номер заказа (ПК)
NickName - целевой аккаунт (внешний ключ к customers)
RU - Количество рублей прошедших через заказ
KZ - Количество тенге прошедших через заказ
Status - Статус заказа
Url - ссылка на форму оплаты
CreateDateTime - Дата и Время создания
PiadDateTime - дата и Время исполнения
config - Настройки работы проекта
commission - комиссия за выполнение (в основном использовалась как поправочный коэффициент покрывающий комиссию QIWI за переводы и конвертацию валют)
wallet - Состояние и баланс кошелька
Name - имя QIWI кошелька (ПК)
Is_default - Выбранный кошелек для операций
Интеграция с QIWI API
Эта часть по сути является набором готовых методов которые можно использовать в боте для взаимодействия с кошельком.
Схема проекта
После произведения оплаты, пользователь может выбрать вторую опцию и проверить статус его перевода, я заложил следующие
Если заказ застревал где-то в цепочке исполнения, эта информация передавалась пользователю, и он мог или попробовать снова протолкнуть оплату сам или воспользоваться поддержкой в лице меня
Третья опция позволяла сменить аккаунт Steam на который создавались заказы, так пользователь мог пополнять кошельки не только себе но и знакомым, которые бы опасались сами использовать нового бота, и тем самым продвигая его.
И так общий план приложения намечен, можно было приступать к разработке, весь процесс я поделил на четыре стадии.
Первым делом разметим наборы кнопок которые будут отображаться пользователю в соответствующих меню. Реализованы они с помощью ReplyKeyboardMarkup, это шаблоны сообщений, в них не заложить логики, но они помогают наглядно указать пользователю какие действия ему сейчас доступны.
Код
# Пустой набор
Delete_markup = types.ReplyKeyboardRemove()
# Регистрация
Regestration_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Regestration_markup.add(types.KeyboardButton("Вход"))
# Главное меню
Main_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Main_menu_markup.add(types.KeyboardButton("Создать ссылку на пополнение Steam"))
Main_menu_markup.add(types.KeyboardButton("Подтвердить статус оплаты"))
Main_menu_markup.add(types.KeyboardButton("Менеджер акаунтов"))
# Менеждер акаунтов
Nick_Name_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Nick_Name_menu_markup.add(types.KeyboardButton("Добавить новый акаунт Steam"))
Nick_Name_menu_markup.add(types.KeyboardButton("Сменить Steam акаунт"))
Nick_Name_menu_markup.add(types.KeyboardButton("Назад"))
# Заказы, предпологал возможность расширения опций
Order_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Order_menu_markup.add(types.KeyboardButton("Назад"))
Затем нужно сделать функций для декоратора обработчика сообщений.
По сути все сводится к тому, что надо определить какую команду ввел пользователь, ответить ему и заменить набор опций при необходимости
reply_markup = *набор команд*
после чего указать какой из обработчиков будет взаимодействовать с этим пользователем.
Bot.register_next_step_handler(*сообщение от пользователя*, *обработчик*)
Код
@Bot.message_handler(content_types=['text'])
def main(message):
if "Создать ссылку на пополнение Steam" == message.text:
Bot.send_message(message.chat.id, 'Введите сумму на котору пополнить акаунт\n'+nick_name+'\nМинимум 85 (требование QIWI)',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
if "Подтвердить статус оплаты" == message.text:
Bot.send_message(message.chat.id, 'Подтверждено пополнений '+0+'\nЗаказов отправлено на Steam '+0+'\nБудем рады если вы оставите отзыв от том какая сумма пришла на Steam\nЭто поможет нам улучшить сервис\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
Bot.send_message(message.chat.id, 'Оплат по ссылкам не найдено !\nЕсли вы производили оплату свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
if "Менеджер акаунтов" == message.text:
Bot.send_message(message.chat.id, 'Ваш текущий ник: '+ 'Ander_kot',reply_markup= Nick_Name_menu_markup)
Bot.register_next_step_handler(message,NickNameMenu)
Обертка над API QIWI
Как уже говорил у QIWI есть хорошая документация по API с примерами использования на разных языках и ожидаемыми ответами.
https://developer.qiwi.com/ru/p2p-payments/#p2p-
Самое интересное тут это отправка средств на счет клиента и получение ссылки на оплату.
Код
# Создание заказа в QIWI API
url = "https://api.qiwi.com/partner/bill/v1/bills/"+str(order_ID)
end_datetime = datetime.date.today() + datetime.timedelta(1) # Дата и время когда QIWI поститает заказ просроченым
# Заголовок
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_secret_token # Ваш P2P кльч https://qiwi.com/p2p-admin/api
# Данные
post_json = {"amount": {"currency": "RUB","value": ""},"comment": "","expirationDateTime": "","customer": {"phone": "","email": "","account": ""},"customFields" : {"paySourcesFilter":"","themeCode": "","yourParam1": "","yourParam2": ""}}
post_json["amount"]["value"] = amount_str
post_json["comment"] = comment+': '+str(nick_name)
post_json["expirationDateTime"] = str(end_datetime.isoformat())+'T12:00:00+03:00'
post_json["customer"]["account"] = str(nick_name)
# Запрос
respons = requests.put(url, headers=headers_API, json=post_json)
if respons.ok:
# Получение ссылки на оплату
respons_Json = respons.json()
url = str(respons_Json['payUrl'])
print(url)
return {'successfully':True, 'data':url}
else:
return {'successfully':False, 'data':''}
# Перевод на стим
def Send_To_Steam(api_access_token, nickName, amount_KZT, order_ID):
amount_KZT_str = str(amount_KZT)
url = "https://edge.qiwi.com/sinap/api/v2/terms/31212/payments"
# Заголовок
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_access_token
# Данные
json_API = {"id":"","sum": {"amount":"","currency":"398"},"paymentMethod": {"type":"Account","accountId":"398"},"fields": {"account":""}}
json_API['id'] = str(order_ID)
json_API['sum']['amount'] = amount_KZT_str
json_API['fields']['account'] = nickName
# Запрос
respons = requests.post(url, headers=headers_API, json=json_API)
if respons.ok:
return {'successfully':True, 'data':''}
else:
return {'successfully':False, 'data':respons.text} # тест ошибки
Тут упомяну "прикол" который может сэкономить кому-то время при разработке собственного приложения, дело в том что для обращения к некоторым методам API QIWI нужно присылать уникальный ID операции, так вот у меня этот параметр был привязан к номеру заказа, который изначально был обычным авто инкрементом в БД, что привело к падению приложения на каждом 2м заказе, дело полагаю в том что перевод на Steam из тенге по сути представляет собой 2 операции, конвертацию в доллары и уже затем перевод, из-за забиваются сразу 2 ID операции вместо одного и поэтому когда я пытался отправить тенге для второго заказа у которого ID+1 API возвращало мне ошибку "такой ID уже бы использован."
Работа с сервером MS SQL
Все свое общение с SQL я реализовал через 1 процедуру, которую приведу ниже, алгоритм простой, но позволял в ран тайме понимать что где и когда сработало не так.
Всего в нем пара траев.
Первый был подключением к БД.
Второй на получение информации.
Как я уже говорил об ORM я узнал значительно позже, вследствие чего все SQL запросы я составлял ручками и не о чем не жалею, практика есть практика )
Под катом также расположены методы которые я использовал для работы с заказами в БД.
Код
# Подключение к серверу SQL --
def Create_SQL_connection(host_name, user_name, user_password, db_name):
connection = None
try:
connection = mysql.connector.connect(
host=host_name,
user=user_name,
passwd=user_password,
database=db_name
)
except Error as e:
print(f"Ошибка подключения к MySQL '{e}'")
return connection
# Отправка запроса SQL
def execute_query(query, tip='не определено'):
connection = Create_SQL_connection(SQLHostName,SQLUserName,SQLRassword,SQLBaseName)
cursor = connection.cursor()
try:
cursor.execute(query)
result = cursor.fetchall()
connection.commit()
print('Запрос на '+tip+' отправлен')
return {'successfully':True, 'data':result}
except Error as e:
print(f"Ошибка в запросе '{e}'")
return {'successfully':False, 'data':''}
# Создание заказа
def Create_order(api_secret_token, amount, comment, nick_name):
datetime_str = str(datetime.datetime.today().replace(microsecond=0).isoformat())
print(datetime_str)
print(api_secret_token)
# Запрос коммиссии
respons_SQL = Get_Commission()
if respons_SQL['successfully'] and respons_SQL['data']:
# Расчет стоимости заказа
commission = respons_SQL['data']
amount_decimal = Decimal(amount)
commission_decimal = Decimal(commission)/Decimal(100)+Decimal(1)
amount_str = str(round(amount_decimal*commission_decimal,2))
# Создание заказа в QSL
query = "SELECT MAX(No) FROM orders;"
respons_SQL = execute_query(query,'Сбор ID заказа')
order_ID = respons_SQL['data'][0][0]+5 # отстум в 5 ID из-за того самого "прикола"
query = "INSERT INTO orders(No,NickName,RU,CreateDateTime) VALUES ("+str(order_ID)+",'"+nick_name+"',"+amount_str+",'"+datetime_str+"');"
respons_SQL = execute_query(query,'Создание pаказа для '+nick_name)
return respons_SQL
else:
return {'successfully':False, 'data':''}
# Добавить Url к заказу
def Add_URL(order_URL,order_ID):
order_ID_str = str(order_ID)
query = "UPDATE orders SET Url = '"+order_URL+"' WHERE No = "+order_ID_str+";"
respons_SQL = execute_query(query,'Установка URL заказу '+str(order_ID)+': '+str(order_URL))
if respons_SQL['successfully']:
return {'successfully':True, 'data':''}
else:
return {'successfully':False, 'data':''}
Естественно часто приходилось делать "гибридные" функции, которые одновременно работают как с БД так и с API QIWI, самым простым из примеров будет обновление статуса заказа.
Код
# Обновление статуса заказа
def Check_Oreder(api_secret_token, order_ID):
# API ---
url = "https://api.qiwi.com/partner/bill/v1/bills/"+str(order_ID)
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_secret_token
respons = requests.get(url, headers=headers_API)
if respons.ok:
respons_Json = respons.json()
status = str(respons_Json['status']['value'])
# SQL ---
query = "UPDATE orders SET Status = '"+status+"' WHERE No = '"+str(order_ID)+"';"
if execute_query(query,'Обновление pаказа '+status+'|'+str(order_ID)):
return {'successfully':True, 'data':status}
else:
return {'successfully':False, 'data':''}
return {'successfully':False, 'data':''}
Интеграция функционала в интерфейс
Когда все базовые взаимодействия с окружением были готовы настало время добавить их в интерфейс, это уже не составляло особого труда.
Смотришь что нажал пользователь, пытаешься произвести действие, получилось ?
Отлично можно двигать его дальше по интерфейсу.
Произошла ошибка?
Выводим оповещение с описанием ошибки, просим повторить, если ситуация хуже откатываем на предыдущую позицию в интерфейсе, если совсем все плохо, просим обратиться в поддержку.
Ниже описан обработчик сообщений в меню заказа.
Код
# Пользователь нажал на "Создать ссылку на пополнение Steam"
# Предыдущий обработчик попросил пользователя ввечсти желаемую сумму и перевел управление сюда
def createpayment(message):
# Возврат в главное меню
if("Назад" == message.text):
Bot.send_message(message.chat.id, 'Выберите действие',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
# Проверка на "число"
if message.text.isdigit():
amount_Dec = round(Decimal(message.text),2)
# Для перевода на стим есть минимальный лимит
# Если пользователь попытается сделать заказ меньше он просто не пройдет
# Поэтму заранее отсекаем такие заказы
if amount_Dec >= round(Decimal('85'),2):
# Если введенная сумма верна создаем заказ, это может занять время
# Оповещаем пользователя что процесс пошел
Bot.send_message(message.chat.id, 'Создание ссылки для оплаты')
# Получаем целевой ник Steam
respons_SQL = QIWI_API.Check_Customer(message.chat.id)
if respons_SQL['successfully'] and respons_SQL['data']:
nick_name = respons_SQL['data'][0][0]
# Создаем заказ
respons_SQL = QIWI_API.Create_order( QIWI_API.SecretKey,message.text,'Account replenishment',nick_name)
if respons_SQL['successfully'] and respons_SQL['data']:
# Если все Ок отдаем ссылку пользователю и возвращаем к меню, где он может проверить статус заказа
order_URL = respons_SQL['data']
Bot.send_message(message.chat.id, 'После оплаты нажмите на "Подтвердить статус оплаты"\nВаша ссылка для оплаты:\n'+order_URL,reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
print('У клиента ошибка ! '+str(message.chat.id)+'\nСсылка на заказ не создана')
Bot.send_message(message.chat.id, 'Ошибка!\nСсылка не создана\nПовторите попытку или свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
print('У клиента ошибка ! '+str(message.chat.id)+'\nНе найден ник при создании заказа')
Bot.send_message(message.chat.id, 'Ошибка!\nВаш ник не найден\nПовторите попытку или свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup = Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
Bot.send_message(message.chat.id, 'Платеж должен составлять минимум 85 (требование QIWI)',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
else:
Bot.send_message(message.chat.id, 'Используйте только цифры',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
За это время он помог перевести 41051р на кошельки Steam.
Самая большая разовая сумма 2100р.
87% пользователей первым взносом выбирали самую низкую сумму из возможных 105р.
Заработано было 0р 0к, комиссию за собственный сервис я не брал.
В общем это был интересный опыт по проработке собственного сервиса, который позволил мне погрузиться в мир API и SQL в купе с практикой по Python.
У игроков осталось 3 пути:
- Продавать вещи со своего инвентаря.
- Покупать ключи и аккаунты в сомнительных магазинах.
- Возиться с QIWI для перевода через Тенге.
Сразу приложу ссылку на репозиторий, в котором храниться этот проект и оговорюсь что его поддержку я закончил несколько месяце назад в связи с участившимся отказом QIWI в переводах на Steam через API, якобы из-за недостатка средств на балансе, при том что баланс всегда был с запасом, и вручную оплата спокойно проводилась.
Пример
Основные проблемы пользователя которые должен устранять бот.
- Необходимость иметь собственный QIWI кошелёк - все операции должны выполняются на уже созданном кошельке. В силу того что я не ожидал большого потока пользователей для начала я решил использовать свой личный аккаунт.
- Ручной перевод средств - после того как бот получал подтверждения оплаты, он должен автоматически переводить рубли в тенге и отправлять их на аккаунт пользователя используя средства предоставляемые API кошелька.
Структура проекта
Проект планировалось создать из 3х основных частейТелеграмм бот.
Фронт: осуществляет интерфейс взаимодействия с пользователем
Бек: отправляет API запросы и анализирует ответы, обновляет данные в базе
MS SQL Server.
Ответственен за хранение всей информации в проекте, ниже расписаны содержащиеся в ней таблицы и назначение полей в них.
customers — Аккаунты которыми оперировал пользователь.
No - Номер строки (ПК)
TgID - Идентификатор пользователя в Телеграмм
NickName - Логин пользователя в Steam
KZ - Баланс тенге
Logined - Флаг указывающий на выбраны в данный момент аккаунт Steam
orders - Информация о заказах
No - номер заказа (ПК)
NickName - целевой аккаунт (внешний ключ к customers)
RU - Количество рублей прошедших через заказ
KZ - Количество тенге прошедших через заказ
Status - Статус заказа
Url - ссылка на форму оплаты
CreateDateTime - Дата и Время создания
PiadDateTime - дата и Время исполнения
config - Настройки работы проекта
commission - комиссия за выполнение (в основном использовалась как поправочный коэффициент покрывающий комиссию QIWI за переводы и конвертацию валют)
wallet - Состояние и баланс кошелька
Name - имя QIWI кошелька (ПК)
Is_default - Выбранный кошелек для операций
Интеграция с QIWI API
Эта часть по сути является набором готовых методов которые можно использовать в боте для взаимодействия с кошельком.
Схема проекта
Общий принцип работы
При первом заходе пользователь вводил свой логин от аккаунта, после чего ему становится доступен основное меню, так у него на выбор несколько действий.- Создать ссылку на пополнение Steam
- Подтвердить статус оплаты
- Менеджер аккаунтов
После произведения оплаты, пользователь может выбрать вторую опцию и проверить статус его перевода, я заложил следующие
- Ожидание (WAITING) - заказ был инициализирован через API и ожидает оплаты.
- Оплачен (PAID) - пользователь внес средства в рублях.
- Конвертирован (CROSSED) - рубли были конвертированы в тенге.
- Исполнен (COMPLETED) - тенге были доставлены на аккаунт.
Если заказ застревал где-то в цепочке исполнения, эта информация передавалась пользователю, и он мог или попробовать снова протолкнуть оплату сам или воспользоваться поддержкой в лице меня
Третья опция позволяла сменить аккаунт Steam на который создавались заказы, так пользователь мог пополнять кошельки не только себе но и знакомым, которые бы опасались сами использовать нового бота, и тем самым продвигая его.
Реализация
Процесс реализации в цветеИ так общий план приложения намечен, можно было приступать к разработке, весь процесс я поделил на четыре стадии.
- Разработка интерфейса - создание основных элементов меню, навигация, предстояло продумать пользовательские сценарии.
- Создание обертки над API QIWI - на тот момент я не имел опыта работы с сторонними сервисами и мне предстояло создать для точки взаимодействия с API QIWI, благо на мой взгляд там достаточно хорошая документация.
- Создание прослойки для работы с сервером MS SQL, ох знал бы я тогда что такое ORM с экономил бы кучу времени )
- Интеграция функционала в интерфейс, собственно последним этом предстояло привязать готовые наборы действий кнопкам на интерфейсе.
Первым делом разметим наборы кнопок которые будут отображаться пользователю в соответствующих меню. Реализованы они с помощью ReplyKeyboardMarkup, это шаблоны сообщений, в них не заложить логики, но они помогают наглядно указать пользователю какие действия ему сейчас доступны.
Код
# Пустой набор
Delete_markup = types.ReplyKeyboardRemove()
# Регистрация
Regestration_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Regestration_markup.add(types.KeyboardButton("Вход"))
# Главное меню
Main_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Main_menu_markup.add(types.KeyboardButton("Создать ссылку на пополнение Steam"))
Main_menu_markup.add(types.KeyboardButton("Подтвердить статус оплаты"))
Main_menu_markup.add(types.KeyboardButton("Менеджер акаунтов"))
# Менеждер акаунтов
Nick_Name_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Nick_Name_menu_markup.add(types.KeyboardButton("Добавить новый акаунт Steam"))
Nick_Name_menu_markup.add(types.KeyboardButton("Сменить Steam акаунт"))
Nick_Name_menu_markup.add(types.KeyboardButton("Назад"))
# Заказы, предпологал возможность расширения опций
Order_menu_markup = types.ReplyKeyboardMarkup(resize_keyboard = True)
Order_menu_markup.add(types.KeyboardButton("Назад"))
Затем нужно сделать функций для декоратора обработчика сообщений.
По сути все сводится к тому, что надо определить какую команду ввел пользователь, ответить ему и заменить набор опций при необходимости
reply_markup = *набор команд*
после чего указать какой из обработчиков будет взаимодействовать с этим пользователем.
Bot.register_next_step_handler(*сообщение от пользователя*, *обработчик*)
Код
@Bot.message_handler(content_types=['text'])
def main(message):
if "Создать ссылку на пополнение Steam" == message.text:
Bot.send_message(message.chat.id, 'Введите сумму на котору пополнить акаунт\n'+nick_name+'\nМинимум 85 (требование QIWI)',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
if "Подтвердить статус оплаты" == message.text:
Bot.send_message(message.chat.id, 'Подтверждено пополнений '+0+'\nЗаказов отправлено на Steam '+0+'\nБудем рады если вы оставите отзыв от том какая сумма пришла на Steam\nЭто поможет нам улучшить сервис\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
Bot.send_message(message.chat.id, 'Оплат по ссылкам не найдено !\nЕсли вы производили оплату свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
if "Менеджер акаунтов" == message.text:
Bot.send_message(message.chat.id, 'Ваш текущий ник: '+ 'Ander_kot',reply_markup= Nick_Name_menu_markup)
Bot.register_next_step_handler(message,NickNameMenu)
Обертка над API QIWI
Как уже говорил у QIWI есть хорошая документация по API с примерами использования на разных языках и ожидаемыми ответами.
https://developer.qiwi.com/ru/p2p-payments/#p2p-
Самое интересное тут это отправка средств на счет клиента и получение ссылки на оплату.
Код
# Создание заказа в QIWI API
url = "https://api.qiwi.com/partner/bill/v1/bills/"+str(order_ID)
end_datetime = datetime.date.today() + datetime.timedelta(1) # Дата и время когда QIWI поститает заказ просроченым
# Заголовок
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_secret_token # Ваш P2P кльч https://qiwi.com/p2p-admin/api
# Данные
post_json = {"amount": {"currency": "RUB","value": ""},"comment": "","expirationDateTime": "","customer": {"phone": "","email": "","account": ""},"customFields" : {"paySourcesFilter":"","themeCode": "","yourParam1": "","yourParam2": ""}}
post_json["amount"]["value"] = amount_str
post_json["comment"] = comment+': '+str(nick_name)
post_json["expirationDateTime"] = str(end_datetime.isoformat())+'T12:00:00+03:00'
post_json["customer"]["account"] = str(nick_name)
# Запрос
respons = requests.put(url, headers=headers_API, json=post_json)
if respons.ok:
# Получение ссылки на оплату
respons_Json = respons.json()
url = str(respons_Json['payUrl'])
print(url)
return {'successfully':True, 'data':url}
else:
return {'successfully':False, 'data':''}
# Перевод на стим
def Send_To_Steam(api_access_token, nickName, amount_KZT, order_ID):
amount_KZT_str = str(amount_KZT)
url = "https://edge.qiwi.com/sinap/api/v2/terms/31212/payments"
# Заголовок
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_access_token
# Данные
json_API = {"id":"","sum": {"amount":"","currency":"398"},"paymentMethod": {"type":"Account","accountId":"398"},"fields": {"account":""}}
json_API['id'] = str(order_ID)
json_API['sum']['amount'] = amount_KZT_str
json_API['fields']['account'] = nickName
# Запрос
respons = requests.post(url, headers=headers_API, json=json_API)
if respons.ok:
return {'successfully':True, 'data':''}
else:
return {'successfully':False, 'data':respons.text} # тест ошибки
Тут упомяну "прикол" который может сэкономить кому-то время при разработке собственного приложения, дело в том что для обращения к некоторым методам API QIWI нужно присылать уникальный ID операции, так вот у меня этот параметр был привязан к номеру заказа, который изначально был обычным авто инкрементом в БД, что привело к падению приложения на каждом 2м заказе, дело полагаю в том что перевод на Steam из тенге по сути представляет собой 2 операции, конвертацию в доллары и уже затем перевод, из-за забиваются сразу 2 ID операции вместо одного и поэтому когда я пытался отправить тенге для второго заказа у которого ID+1 API возвращало мне ошибку "такой ID уже бы использован."
Работа с сервером MS SQL
Все свое общение с SQL я реализовал через 1 процедуру, которую приведу ниже, алгоритм простой, но позволял в ран тайме понимать что где и когда сработало не так.
Всего в нем пара траев.
Первый был подключением к БД.
Второй на получение информации.
Как я уже говорил об ORM я узнал значительно позже, вследствие чего все SQL запросы я составлял ручками и не о чем не жалею, практика есть практика )
Под катом также расположены методы которые я использовал для работы с заказами в БД.
Код
# Подключение к серверу SQL --
def Create_SQL_connection(host_name, user_name, user_password, db_name):
connection = None
try:
connection = mysql.connector.connect(
host=host_name,
user=user_name,
passwd=user_password,
database=db_name
)
except Error as e:
print(f"Ошибка подключения к MySQL '{e}'")
return connection
# Отправка запроса SQL
def execute_query(query, tip='не определено'):
connection = Create_SQL_connection(SQLHostName,SQLUserName,SQLRassword,SQLBaseName)
cursor = connection.cursor()
try:
cursor.execute(query)
result = cursor.fetchall()
connection.commit()
print('Запрос на '+tip+' отправлен')
return {'successfully':True, 'data':result}
except Error as e:
print(f"Ошибка в запросе '{e}'")
return {'successfully':False, 'data':''}
# Создание заказа
def Create_order(api_secret_token, amount, comment, nick_name):
datetime_str = str(datetime.datetime.today().replace(microsecond=0).isoformat())
print(datetime_str)
print(api_secret_token)
# Запрос коммиссии
respons_SQL = Get_Commission()
if respons_SQL['successfully'] and respons_SQL['data']:
# Расчет стоимости заказа
commission = respons_SQL['data']
amount_decimal = Decimal(amount)
commission_decimal = Decimal(commission)/Decimal(100)+Decimal(1)
amount_str = str(round(amount_decimal*commission_decimal,2))
# Создание заказа в QSL
query = "SELECT MAX(No) FROM orders;"
respons_SQL = execute_query(query,'Сбор ID заказа')
order_ID = respons_SQL['data'][0][0]+5 # отстум в 5 ID из-за того самого "прикола"
query = "INSERT INTO orders(No,NickName,RU,CreateDateTime) VALUES ("+str(order_ID)+",'"+nick_name+"',"+amount_str+",'"+datetime_str+"');"
respons_SQL = execute_query(query,'Создание pаказа для '+nick_name)
return respons_SQL
else:
return {'successfully':False, 'data':''}
# Добавить Url к заказу
def Add_URL(order_URL,order_ID):
order_ID_str = str(order_ID)
query = "UPDATE orders SET Url = '"+order_URL+"' WHERE No = "+order_ID_str+";"
respons_SQL = execute_query(query,'Установка URL заказу '+str(order_ID)+': '+str(order_URL))
if respons_SQL['successfully']:
return {'successfully':True, 'data':''}
else:
return {'successfully':False, 'data':''}
Естественно часто приходилось делать "гибридные" функции, которые одновременно работают как с БД так и с API QIWI, самым простым из примеров будет обновление статуса заказа.
Код
# Обновление статуса заказа
def Check_Oreder(api_secret_token, order_ID):
# API ---
url = "https://api.qiwi.com/partner/bill/v1/bills/"+str(order_ID)
headers_API = CaseInsensitiveDict()
headers_API["content-type"] = "application/json"
headers_API["accept"] = "application/json"
headers_API["Authorization"] = "Bearer " + api_secret_token
respons = requests.get(url, headers=headers_API)
if respons.ok:
respons_Json = respons.json()
status = str(respons_Json['status']['value'])
# SQL ---
query = "UPDATE orders SET Status = '"+status+"' WHERE No = '"+str(order_ID)+"';"
if execute_query(query,'Обновление pаказа '+status+'|'+str(order_ID)):
return {'successfully':True, 'data':status}
else:
return {'successfully':False, 'data':''}
return {'successfully':False, 'data':''}
Интеграция функционала в интерфейс
Когда все базовые взаимодействия с окружением были готовы настало время добавить их в интерфейс, это уже не составляло особого труда.
Смотришь что нажал пользователь, пытаешься произвести действие, получилось ?
Отлично можно двигать его дальше по интерфейсу.
Произошла ошибка?
Выводим оповещение с описанием ошибки, просим повторить, если ситуация хуже откатываем на предыдущую позицию в интерфейсе, если совсем все плохо, просим обратиться в поддержку.
Ниже описан обработчик сообщений в меню заказа.
Код
# Пользователь нажал на "Создать ссылку на пополнение Steam"
# Предыдущий обработчик попросил пользователя ввечсти желаемую сумму и перевел управление сюда
def createpayment(message):
# Возврат в главное меню
if("Назад" == message.text):
Bot.send_message(message.chat.id, 'Выберите действие',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
# Проверка на "число"
if message.text.isdigit():
amount_Dec = round(Decimal(message.text),2)
# Для перевода на стим есть минимальный лимит
# Если пользователь попытается сделать заказ меньше он просто не пройдет
# Поэтму заранее отсекаем такие заказы
if amount_Dec >= round(Decimal('85'),2):
# Если введенная сумма верна создаем заказ, это может занять время
# Оповещаем пользователя что процесс пошел
Bot.send_message(message.chat.id, 'Создание ссылки для оплаты')
# Получаем целевой ник Steam
respons_SQL = QIWI_API.Check_Customer(message.chat.id)
if respons_SQL['successfully'] and respons_SQL['data']:
nick_name = respons_SQL['data'][0][0]
# Создаем заказ
respons_SQL = QIWI_API.Create_order( QIWI_API.SecretKey,message.text,'Account replenishment',nick_name)
if respons_SQL['successfully'] and respons_SQL['data']:
# Если все Ок отдаем ссылку пользователю и возвращаем к меню, где он может проверить статус заказа
order_URL = respons_SQL['data']
Bot.send_message(message.chat.id, 'После оплаты нажмите на "Подтвердить статус оплаты"\nВаша ссылка для оплаты:\n'+order_URL,reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
print('У клиента ошибка ! '+str(message.chat.id)+'\nСсылка на заказ не создана')
Bot.send_message(message.chat.id, 'Ошибка!\nСсылка не создана\nПовторите попытку или свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup= Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
print('У клиента ошибка ! '+str(message.chat.id)+'\nНе найден ник при создании заказа')
Bot.send_message(message.chat.id, 'Ошибка!\nВаш ник не найден\nПовторите попытку или свяжитесь с подержкой!\nhttps://t.me/ander_kot_1',reply_markup = Main_menu_markup)
Bot.register_next_step_handler(message,main)
else:
Bot.send_message(message.chat.id, 'Платеж должен составлять минимум 85 (требование QIWI)',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
else:
Bot.send_message(message.chat.id, 'Используйте только цифры',reply_markup = Order_menu_markup)
Bot.register_next_step_handler(message,createpayment)
Итоги
Бот проработал с 16 марта 2022 по 15 октября того же года (8 месяцев).За это время он помог перевести 41051р на кошельки Steam.
Самая большая разовая сумма 2100р.
87% пользователей первым взносом выбирали самую низкую сумму из возможных 105р.
Заработано было 0р 0к, комиссию за собственный сервис я не брал.
В общем это был интересный опыт по проработке собственного сервиса, который позволил мне погрузиться в мир API и SQL в купе с практикой по Python.
EasySteamPaybot или как я помогал людям пополнять Steam
КДПВ И так в марте 2022 Steam отключила в российском сегменте Steam все основные способы оплаты для пользователей из России. У игроков осталось 3 пути: Продавать вещи со своего инвентаря. Покупать...
habr.com