Привет тем кто хочет опробовать себя в качестве бизнесмена! Недавно в голову пришла идея, получить некоторый опыт предпринимательства. В качестве продукта выступает доступ к некоторой цифровой услуге, а контроль за оплатой этой услуги ложиться на плечи телеграмм бота. В ходе поисков системы оплаты была найдена Юкасса, одна из немногих систем (если вообще не единственная), которая работает с самозанятыми.
На сайте подробно описана интеграция оплаты в 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