Привет тем кто хочет опробовать себя в качестве бизнесмена! Недавно в голову пришла идея, получить некоторый опыт предпринимательства. В качестве продукта выступает доступ к некоторой цифровой услуге, а контроль за оплатой этой услуги ложиться на плечи телеграмм бота. В ходе поисков системы оплаты была найдена Юкасса, одна из немногих систем (если вообще не единственная), которая работает с самозанятыми.
На сайте подробно описана интеграция оплаты в telegramm бота. Однако на этапе подписания документов выясняется что интеграция недоступна для самозанятых.

Обидно, но других вариантов оплаты самозанятому найти не удалось, поэтому решено было попробовать написать собственный вариант оплаты. В ходе беглого гугления, не удалось найти готовых решений по самостоятельной реализации оплаты, чему я был сильно огорчен, так-как это скорее всего означало большую сложность собственноручной реализации оплаты. Но так-как, бот был уже готов, я решил попробовать какие-то ещё варианты. Изначально я не думал делать собственную интеграцию, а хотел выставлять многоразовые счета, а потом по примечанию к платежу отслеживать успешные оплаты. Однако, оказалось что интеграция оплаты реализуется гораздо проще, и по факту, в самом дубовом варианте без веб хуков, состоит всего из двух функций.
Интеграция для самозанятых
Реализовать оплату услуг самозанятого можно через сайт юкассы. Я это реализовал следующим образом: клиент запрашивает в боте услугу, в ответ ему приходит ссылка на оплату, клиент переходит и оплачивает товар, после чего его перенаправляет обратно в бот. Обработка же платежа работает следующим образом: как только создается ссылка на оплату, бот запрашивает статус платежа, до тех пор пока статус является «pending». Как только статус меняется на «succeeded» бот выполняет действие (отправляет товар/оказывает услугу и тд).
Для реализации такой схемы необходим модуль yookassa откуда мы возьмем классы Configuration и Payment. Далее необходимо заполнить два поля класса Configuration: Configuration.account_id и Configuration.secret_key , в первый записываем id магазина, во второй api ключ магазина.
import json from yookassa import Configuration,Payment import config Configuration.account_id = config.SHOP_ID Configuration.secret_key = config.SHOP_API_TOKEN
Далее с помощью метода Payment.create , необходимо создать объект платежа. При создании этот метод сам отправит данные в юкассу.
import json from yookassa import Configuration,Payment import config Configuration.account_id = config.SHOP_ID Configuration.secret_key = config.SHOP_API_TOKEN payment = Payment.create({ "amount": { "value": сумма платежа, "currency": "RUB" }, "payment_method_data": { "type": "bank_card" }, "confirmation": { "type": "redirect", "return_url": "Ссылка, куда перенаправить после совершения платежа" }, "capture": True, "description": description })
Платеж создан, теперь необходимо получить id операции (понадобиться позже) и ссылку на оплату, которую мы и отправим пользователю. Для этого воспользуемся методом json(), который запросит данные по платежу на сервере юкассы и вернет их в формате json. Для удобства преобразуем json в словарь python :
import json from yookassa import Configuration,Payment import config Configuration.account_id = config.SHOP_ID Configuration.secret_key = config.SHOP_API_TOKEN payment = Payment.create({ "amount": { "value": сумма платежа, "currency": "RUB" }, "payment_method_data": { "type": "bank_card" }, "confirmation": { "type": "redirect", "return_url": "Ссылка, куда перенаправить после совершения платежа" }, "capture": True, "description": description }) payment_data = json.loads(payment.json()) payment_id = payment_data['id'] payment_url = (payment_data['confirmation'])['confirmation_url']
Теперь можно отправить payment_url пользователю, по которому он сможет оплатить товар. Однако мы пока не знаем оплатил пользователь товар или нет. Для получения статуса платежа реализуем метод Payment.find_one(payment_id)).json(), которые найдет платеж по указанному id (который мы получили на прошлом шаге) и пришлет его статус в формате json. Далее мы будем опрашивать этот метод до тех пор пока статус платежа не измениться с pending на успешный / не успешный.
import json from yookassa import Configuration,Payment import config import time Configuration.account_id = config.SHOP_ID Configuration.secret_key = config.SHOP_API_TOKEN payment = Payment.create({ "amount": { "value": сумма платежа, "currency": "RUB" }, "payment_method_data": { "type": "bank_card" }, "confirmation": { "type": "redirect", "return_url": "Ссылка, куда перенаправить после совершения платежа" }, "capture": True, "description": description }) payment_data = json.loads(payment.json()) payment_id = payment_data['id'] payment_url = (payment_data['confirmation'])['confirmation_url'] payment = json.loads((Payment.find_one(payment_id)).json()) while payment['status'] == 'pending': payment = json.loads((Payment.find_one(payment_id)).json()) time.sleep(время между опросами)
Теперь при успешной оплате или таймауте операции (что-то около 15 минут), мы выйдем из цикла, однако здесь существует огромная проблема, с тем, что если вызывать эти методы из бота, бот будет заблокирован на весь период оплаты. Чтобы избежать подобного поведения будем использовать асинхронный sleep. А также реализуем логику оплаты в виде двух функций: создания и проверки статуса платежа
import json from yookassa import Configuration,Payment import config import asyncio Configuration.account_id = config.SHOP_ID Configuration.secret_key = config.SHOP_API_TOKEN def payment(value,description): payment = Payment.create({ "amount": { "value": value, "currency": "RUB" }, "payment_method_data": { "type": "bank_card" }, "confirmation": { "type": "redirect", "return_url": "урл редиректа" }, "capture": True, "description": description }) return json.loads(payment.json()) async def check_payment(payment_id): payment = json.loads((Payment.find_one(payment_id)).json()) while payment['status'] == 'pending': payment = json.loads((Payment.find_one(payment_id)).json()) await asyncio.sleep(3) if payment['status']=='succeeded': print("SUCCSESS RETURN") print(payment) return True else: print("BAD RETURN") print(payment) return False
Теперь посмотрим как это может выглядеть в боте (aiogram)
import json from aiogram import Bot, Dispatcher, executor, types, filters from yookassa import Configuration,Payment import payment import config API_TOKEN = config.API_TOKEN BASE=config.BASE REFBASE=config.REFBASE CURRENT_ENDPOINT=config.CURRENT_ENDPOINT #--- buttons --- btnbuy = types.KeyboardButton("купить") btnprologue = types.KeyboardButton("продлить") btnstatus = types.KeyboardButton("статус") btnsinfo = types.KeyboardButton("инфо") mainmenu=types.ReplyKeyboardMarkup(resize_keyboard = True).add(btnbuy,btnprologue,btnstatus,btnsinfo) # Initialize bot and dispatcher bot = Bot(token=API_TOKEN) dp = Dispatcher(bot) @dp.message_handler(commands=['start']) async def send_welcome(message: types.Message): await message.answer("Hello", reply_markup = mainmenu) @dp.message_handler(commands=['info']) async def info(message: types.Message): await message.answer("info", disable_web_page_preview=True) @dp.message_handler(commands=['buy']) async def BuyVPNforMonth(message: types.Message): await message.answer(msg.buy_message) payment_deatils = payment.payment(100,'Купить товар1') await message.answer( (payment_deatils['confirmation'])['confirmation_url'] ) if await payment.check_payment(payment_deatils['id']): message.answer("платеж") else: message.answer("платеж не прошел") @dp.message_handler() async def menu_message(message: types.Message): if message.text == "купить": await BuyVPNforMonth(message) if message.text == "инфо": await info(message) if __name__ == '__main__': executor.start_polling(dp, skip_updates=True)
Пример работы бота, с интеграцией оплаты, можно посмотреть здесь
ссылка на оригинал статьи https://habr.com/ru/post/673646/
Добавить комментарий