Как стать автором
Обновить

Интеграция оплаты Юкасса в telegramm для самозанятых

Время на прочтение4 мин
Количество просмотров19K

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

На сайте подробно описана интеграция оплаты в 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

Теги:
Хабы:
Всего голосов 19: ↑19 и ↓0+19
Комментарии6

Публикации

Истории

Работа

Data Scientist
76 вакансий
Python разработчик
122 вакансии

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань