mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-09-11 12:46:19 +00:00
yookassa import
This commit is contained in:
parent
415061bcae
commit
f360034156
6 changed files with 305 additions and 1 deletions
1
go.mod
1
go.mod
|
@ -66,6 +66,7 @@ require (
|
|||
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rvinnie/yookassa-sdk-go v0.0.0-20240629113713-dfd7cc31b343 // indirect
|
||||
github.com/sagernet/sing v0.4.3 // indirect
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -139,6 +139,8 @@ github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
|||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rvinnie/yookassa-sdk-go v0.0.0-20240629113713-dfd7cc31b343 h1:oVZiMHABPKz0OZgx/h1BxMNkRFK4DOBdDpSrMebYmsI=
|
||||
github.com/rvinnie/yookassa-sdk-go v0.0.0-20240629113713-dfd7cc31b343/go.mod h1:flatybkcu+7YLaB7mMnj9JTNKeim4jZ+ZrXNFjVA0pA=
|
||||
github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
|
||||
github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
|
||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||
|
|
|
@ -68,6 +68,14 @@ var defaultValueMap = map[string]string{
|
|||
"subJsonRules": "",
|
||||
"datepicker": "gregorian",
|
||||
"warp": "",
|
||||
"price1": "500.0",
|
||||
"price2": "1400.0",
|
||||
"price3": "2500.0",
|
||||
"price4": "4000.0",
|
||||
"month1": "1",
|
||||
"month2": "3",
|
||||
"month3": "6",
|
||||
"month4": "12",
|
||||
}
|
||||
|
||||
type SettingService struct{}
|
||||
|
@ -231,6 +239,18 @@ func (s *SettingService) getInt(key string) (int, error) {
|
|||
return strconv.Atoi(str)
|
||||
}
|
||||
|
||||
func (s *SettingService) getFloat64(key string) (float64, error) {
|
||||
str, err := s.getString(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseFloat(str, 64)
|
||||
}
|
||||
|
||||
func (s *SettingService) setFloat64(key string, value float64) error {
|
||||
return s.setString(key, strconv.FormatFloat(value, 'f', -1, 64))
|
||||
}
|
||||
|
||||
func (s *SettingService) setInt(key string, value int) error {
|
||||
return s.setString(key, strconv.Itoa(value))
|
||||
}
|
||||
|
@ -480,6 +500,130 @@ func (s *SettingService) GetSubJsonMux() (string, error) {
|
|||
return s.getString("subJsonMux")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFirstPriceFloat() (float64, error) {
|
||||
return s.getFloat64("price1")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecondPriceFloat() (float64, error) {
|
||||
return s.getFloat64("price2")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetThirdPriceFloat() (float64, error) {
|
||||
return s.getFloat64("price3")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFourthPriceFloat() (float64, error) {
|
||||
return s.getFloat64("price4")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFirstPriceString() (string, error) {
|
||||
return s.getString("price1")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecondPriceString() (string, error) {
|
||||
return s.getString("price2")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetThirdPriceString() (string, error) {
|
||||
return s.getString("price3")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFourthPriceString() (string, error) {
|
||||
return s.getString("price4")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetAllPricesString() (string, string, string, string, error) {
|
||||
price1, err := s.GetFirstPriceString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
price2, err := s.GetSecondPriceString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
price3, err := s.GetThirdPriceString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
price4, err := s.GetFourthPriceString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
return price1, price2, price3, price4, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) GetAllPricesFloat() (float64, float64, float64, float64, error) {
|
||||
price1, err := s.GetFirstPriceFloat()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
price2, err := s.GetSecondPriceFloat()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
price3, err := s.GetThirdPriceFloat()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
price4, err := s.GetFourthPriceFloat()
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
return price1, price2, price3, price4, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFirstMonthString() (string, error) {
|
||||
return s.getString("month1")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecondMonthString() (string, error) {
|
||||
return s.getString("month2")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetThirdMonthString() (string, error) {
|
||||
return s.getString("month3")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFourthMonthString() (string, error) {
|
||||
return s.getString("month4")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetAllMonthsString() (string, string, string, string, error) {
|
||||
month1, err := s.GetFirstMonthString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
month2, err := s.GetSecondMonthString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
month3, err := s.GetThirdMonthString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
month4, err := s.GetFourthMonthString()
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
return month1, month2, month3, month4, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFirstMonthInt() (int, error) {
|
||||
return s.getInt("month1")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSecondMonthInt() (int, error) {
|
||||
return s.getInt("month2")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetThirdMonthInt() (int, error) {
|
||||
return s.getInt("month3")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetFourthMonthInt() (int, error) {
|
||||
return s.getInt("month4")
|
||||
}
|
||||
|
||||
func (s *SettingService) GetSubJsonRules() (string, error) {
|
||||
return s.getString("subJsonRules")
|
||||
}
|
||||
|
|
|
@ -881,6 +881,7 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
|||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.clientUsage"))
|
||||
t.getClientUsage(chatId, tgUserID)
|
||||
case "admin_help":
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.adminContact"))
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.adminContact"))
|
||||
case "client_commands":
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
|
||||
|
@ -894,6 +895,38 @@ func (t *Tgbot) answerCallback(callbackQuery *telego.CallbackQuery, isAdmin bool
|
|||
case "commands":
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.commands"))
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.commands.helpAdminCommands"))
|
||||
case "payments":
|
||||
price1, price2, price3, price4, err := t.settingService.GetAllPricesString()
|
||||
if err != nil {
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
|
||||
return
|
||||
}
|
||||
month1, month2, month3, month4, err := t.settingService.GetAllMonthsString()
|
||||
if err != nil {
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.answers.errorOperation"))
|
||||
return
|
||||
}
|
||||
|
||||
t.sendCallbackAnswerTgBot(callbackQuery.ID, t.I18nBot("tgbot.buttons.payments"))
|
||||
t.SendMsgToTgbot(chatId, t.I18nBot("tgbot.answers.payments",
|
||||
"Price1=="+price1,
|
||||
"Month1=="+month1,
|
||||
"Price2=="+price2,
|
||||
"Month2=="+month2,
|
||||
"Price3=="+price3,
|
||||
"Month3=="+month3,
|
||||
"Price4=="+price4,
|
||||
"Month4=="+month4),
|
||||
tu.InlineKeyboard(
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(price1).WithCallbackData(t.encodeQuery("first_price_payment")),
|
||||
tu.InlineKeyboardButton(price2).WithCallbackData(t.encodeQuery("second_price_payment")),
|
||||
),
|
||||
tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(price3).WithCallbackData(t.encodeQuery("third_price_payment")),
|
||||
tu.InlineKeyboardButton(price4).WithCallbackData(t.encodeQuery("fourth_price_payment")),
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -933,6 +966,12 @@ func (t *Tgbot) SendAnswer(chatId int64, msg string, isAdmin bool) {
|
|||
),
|
||||
)
|
||||
|
||||
if t.isClientExist(chatId) {
|
||||
numericKeyboardClient.InlineKeyboard = append(numericKeyboardClient.InlineKeyboard, tu.InlineKeyboardRow(
|
||||
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.payments")).WithCallbackData(t.encodeQuery("payments")),
|
||||
))
|
||||
}
|
||||
|
||||
var ReplyMarkup telego.ReplyMarkup
|
||||
if isAdmin {
|
||||
ReplyMarkup = numericKeyboard
|
||||
|
@ -1457,6 +1496,17 @@ func (t *Tgbot) clientTelegramUserInfo(chatId int64, email string, messageID ...
|
|||
}
|
||||
}
|
||||
|
||||
func (t *Tgbot) isClientExist(tgUserID int64) bool {
|
||||
traffic, err := t.inboundService.GetClientTrafficTgBot(tgUserID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if len(traffic) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tgbot) searchClient(chatId int64, email string, messageID ...int) {
|
||||
traffic, err := t.inboundService.GetClientTrafficByEmail(email)
|
||||
if err != nil {
|
||||
|
|
|
@ -573,6 +573,7 @@
|
|||
"limitTraffic" = "🚧 Лимит трафика"
|
||||
"getBanLogs" = "Логи блокировок"
|
||||
"allClients" = "Все клиенты"
|
||||
"payments" = "Продлить подписку"
|
||||
|
||||
[tgbot.answers]
|
||||
"successfulOperation" = "✅ Успешный!"
|
||||
|
@ -597,3 +598,4 @@
|
|||
"chooseClient" = "Выберите пользователя для подключения {{ .Inbound }}"
|
||||
"chooseInbound" = "Выберите подключение"
|
||||
"adminContact" = "Если вам нужна помощь, свяжитесь с администратором: @sainthrill"
|
||||
"payments" = "Выберите вариант оплаты подписки:\r\n💵 {{ .Price1 }} за {{ .Month1 }} месяцев\r\n💶 {{ .Price2 }} за {{ .Month2 }} месяцев\r\n💷 {{ .Price3 }} за {{ .Month3 }} месяцев\r\n💰 {{ .Price4 }} за {{ .Month4 }} месяцев"
|
105
web/yookassa/yookassa.go
Normal file
105
web/yookassa/yookassa.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package yookassa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mymmrac/telego"
|
||||
"github.com/rvinnie/yookassa-sdk-go/yookassa"
|
||||
yoocommon "github.com/rvinnie/yookassa-sdk-go/yookassa/common"
|
||||
yoopayment "github.com/rvinnie/yookassa-sdk-go/yookassa/payment"
|
||||
|
||||
"x-ui/logger"
|
||||
)
|
||||
|
||||
type YooKassa struct {
|
||||
bot *telego.Bot
|
||||
providerToken string
|
||||
kassa *yookassa.Client
|
||||
returnURL string
|
||||
}
|
||||
|
||||
func New(bot *telego.Bot, providerToken, accountID, secretkey, returnURL string) *YooKassa {
|
||||
return &YooKassa{
|
||||
bot: bot,
|
||||
providerToken: providerToken,
|
||||
kassa: yookassa.NewClient(accountID, secretkey),
|
||||
returnURL: returnURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (k *YooKassa) SendInvoice(chatID telego.ChatID, title, description string, price int) error {
|
||||
_, err := k.bot.SendInvoice(&telego.SendInvoiceParams{
|
||||
ChatID: chatID,
|
||||
Title: title,
|
||||
Description: description,
|
||||
Payload: "UNIQUE_PAYLOAD",
|
||||
ProviderToken: k.providerToken,
|
||||
Currency: "RUB",
|
||||
Prices: []telego.LabeledPrice{
|
||||
{Label: title, Amount: price},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warning("error occurred while sending invoice", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *YooKassa) CreatePayment(amount float64, description, paymentMethod string, userID int64) (*yoopayment.Payment, error) {
|
||||
paymentHandler := yookassa.NewPaymentHandler(k.kassa)
|
||||
|
||||
newPayment, err := paymentHandler.CreatePayment(
|
||||
&yoopayment.Payment{
|
||||
Amount: &yoocommon.Amount{
|
||||
Value: fmt.Sprintf("%.2f", amount),
|
||||
Currency: "RUB",
|
||||
},
|
||||
PaymentMethod: yoopayment.PaymentMethodType(paymentMethod),
|
||||
Confirmation: yoopayment.Redirect{
|
||||
Type: "redirect",
|
||||
ReturnURL: k.returnURL,
|
||||
},
|
||||
Description: description,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warning("error occurred while creating payment", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newPayment, nil
|
||||
}
|
||||
|
||||
func (k *YooKassa) ConfirmPayment(payment *yoopayment.Payment) error {
|
||||
paymentHandler := yookassa.NewPaymentHandler(k.kassa)
|
||||
|
||||
confirmedPayment, err := paymentHandler.CapturePayment(payment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if confirmedPayment.Status != yoopayment.Succeeded {
|
||||
return fmt.Errorf("payment status is not succeeded")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *YooKassa) CancelPayment(paymentID string) error {
|
||||
paymentHandler := yookassa.NewPaymentHandler(k.kassa)
|
||||
|
||||
_, err := paymentHandler.CancelPayment(paymentID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (k *YooKassa) GetPaymentInfo(paymentID string) (*yoopayment.Payment, error) {
|
||||
paymentHandler := yookassa.NewPaymentHandler(k.kassa)
|
||||
|
||||
paymentInfo, err := paymentHandler.FindPayment(paymentID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return paymentInfo, nil
|
||||
}
|
Loading…
Reference in a new issue