feat(payment): add support for CryptoSaaS payment platform and enhance configuration handling
This commit is contained in:
parent
47446ef410
commit
4d95834c22
@ -26,7 +26,7 @@ func PaymentNotifyHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
}
|
||||
|
||||
switch payment.ParsePlatform(platform) {
|
||||
case payment.EPay:
|
||||
case payment.EPay, payment.CryptoSaaS:
|
||||
req := &types.EPayNotifyRequest{}
|
||||
if err := c.ShouldBind(req); err != nil {
|
||||
result.HttpResult(c, nil, err)
|
||||
|
||||
@ -8,7 +8,7 @@ import (
|
||||
"github.com/perfect-panel/server/pkg/result"
|
||||
)
|
||||
|
||||
// Purchase Checkout
|
||||
// PurchaseCheckoutHandler Purchase Checkout
|
||||
func PurchaseCheckoutHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||
return func(c *gin.Context) {
|
||||
var req types.CheckoutOrderRequest
|
||||
|
||||
@ -55,10 +55,9 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
Token: random.KeyNew(8, 1),
|
||||
}
|
||||
err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||
|
||||
if req.Platform == "Stripe" {
|
||||
var cfg paymentModel.StripeConfig
|
||||
if err := cfg.Unmarshal(paymentMethod.Config); err != nil {
|
||||
if err = cfg.Unmarshal([]byte(paymentMethod.Config)); err != nil {
|
||||
l.Errorf("[CreatePaymentMethod] unmarshal stripe config error: %s", err.Error())
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "unmarshal stripe config error: %s", err.Error())
|
||||
}
|
||||
@ -79,7 +78,8 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "create stripe webhook endpoint error: %s", err.Error())
|
||||
}
|
||||
cfg.WebhookSecret = endpoint.Secret
|
||||
paymentMethod.Config = cfg.Marshal()
|
||||
content, _ := cfg.Marshal()
|
||||
paymentMethod.Config = string(content)
|
||||
}
|
||||
if err = tx.Model(&paymentModel.Payment{}).Create(paymentMethod).Error; err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseInsertError), "insert payment method error: %s", err.Error())
|
||||
@ -101,27 +101,36 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
||||
func parsePaymentPlatformConfig(ctx context.Context, platform payment.Platform, config interface{}) string {
|
||||
data, err := json.Marshal(config)
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse payment platform config error", logger.Field("platform", platform), logger.Field("config", config), logger.Field("error", err.Error()))
|
||||
logger.WithContext(ctx).Errorw("marshal config error", logger.Field("platform", platform), logger.Field("config", config), logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
|
||||
// 通用处理函数
|
||||
handleConfig := func(name string, target interface {
|
||||
Unmarshal([]byte) error
|
||||
Marshal() ([]byte, error)
|
||||
}) string {
|
||||
if err = target.Unmarshal(data); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse "+name+" config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
content, err := target.Marshal()
|
||||
if err != nil {
|
||||
logger.WithContext(ctx).Errorw("marshal "+name+" config error", logger.Field("error", err.Error()))
|
||||
return ""
|
||||
}
|
||||
return string(content)
|
||||
}
|
||||
|
||||
switch platform {
|
||||
case payment.Stripe:
|
||||
stripe := &paymentModel.StripeConfig{}
|
||||
if err := stripe.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse stripe config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return stripe.Marshal()
|
||||
return handleConfig("Stripe", &paymentModel.StripeConfig{})
|
||||
case payment.AlipayF2F:
|
||||
alipay := &paymentModel.AlipayF2FConfig{}
|
||||
if err := alipay.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse alipay config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return alipay.Marshal()
|
||||
return handleConfig("Alipay", &paymentModel.AlipayF2FConfig{})
|
||||
case payment.EPay:
|
||||
epay := &paymentModel.EPayConfig{}
|
||||
if err := epay.Unmarshal(string(data)); err != nil {
|
||||
logger.WithContext(ctx).Errorw("parse epay config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
||||
}
|
||||
return epay.Marshal()
|
||||
return handleConfig("Epay", &paymentModel.EPayConfig{})
|
||||
case payment.CryptoSaaS:
|
||||
return handleConfig("CryptoSaaS", &paymentModel.CryptoSaaSConfig{})
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ type UpdatePaymentMethodLogic struct {
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
// Update Payment Method
|
||||
// NewUpdatePaymentMethodLogic Update Payment Method
|
||||
func NewUpdatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePaymentMethodLogic {
|
||||
return &UpdatePaymentMethodLogic{
|
||||
Logger: logger.WithContext(ctx),
|
||||
|
||||
@ -106,6 +106,17 @@ func (l *PurchaseCheckoutLogic) PurchaseCheckout(req *types.CheckoutOrderRequest
|
||||
CheckoutUrl: url,
|
||||
}
|
||||
|
||||
case paymentPlatform.CryptoSaaS:
|
||||
// Process EPay payment - generates payment URL for redirect
|
||||
url, err := l.epayPayment(paymentConfig, orderInfo, req.ReturnUrl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "epayPayment error: %v", err.Error())
|
||||
}
|
||||
resp = &types.CheckoutOrderResponse{
|
||||
CheckoutUrl: url,
|
||||
Type: "url", // Client should redirect to URL
|
||||
}
|
||||
|
||||
case paymentPlatform.Balance:
|
||||
// Process balance payment - validate user and process payment immediately
|
||||
if orderInfo.UserId == 0 {
|
||||
@ -140,8 +151,8 @@ func (l *PurchaseCheckoutLogic) PurchaseCheckout(req *types.CheckoutOrderRequest
|
||||
// It handles currency conversion and creates a pre-payment trade for QR code scanning
|
||||
func (l *PurchaseCheckoutLogic) alipayF2fPayment(pay *payment.Payment, info *order.Order) (string, error) {
|
||||
// Parse Alipay F2F configuration from payment settings
|
||||
f2FConfig := payment.AlipayF2FConfig{}
|
||||
if err := json.Unmarshal([]byte(pay.Config), &f2FConfig); err != nil {
|
||||
f2FConfig := &payment.AlipayF2FConfig{}
|
||||
if err := f2FConfig.Unmarshal([]byte(pay.Config)); err != nil {
|
||||
l.Errorw("[PurchaseCheckout] Unmarshal Alipay config error", logger.Field("error", err.Error()))
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
@ -191,8 +202,9 @@ func (l *PurchaseCheckoutLogic) alipayF2fPayment(pay *payment.Payment, info *ord
|
||||
// It supports various payment methods including WeChat Pay and Alipay through Stripe
|
||||
func (l *PurchaseCheckoutLogic) stripePayment(config string, info *order.Order, identifier string) (*types.StripePayment, error) {
|
||||
// Parse Stripe configuration from payment settings
|
||||
stripeConfig := payment.StripeConfig{}
|
||||
if err := json.Unmarshal([]byte(config), &stripeConfig); err != nil {
|
||||
stripeConfig := &payment.StripeConfig{}
|
||||
|
||||
if err := stripeConfig.Unmarshal([]byte(config)); err != nil {
|
||||
l.Errorw("[PurchaseCheckout] Unmarshal Stripe config error", logger.Field("error", err.Error()))
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
@ -249,12 +261,11 @@ func (l *PurchaseCheckoutLogic) stripePayment(config string, info *order.Order,
|
||||
// It handles currency conversion and creates a payment URL for external payment processing
|
||||
func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order.Order, returnUrl string) (string, error) {
|
||||
// Parse EPay configuration from payment settings
|
||||
epayConfig := payment.EPayConfig{}
|
||||
if err := json.Unmarshal([]byte(config.Config), &epayConfig); err != nil {
|
||||
epayConfig := &payment.EPayConfig{}
|
||||
if err := epayConfig.Unmarshal([]byte(config.Config)); err != nil {
|
||||
l.Errorw("[PurchaseCheckout] Unmarshal EPay config error", logger.Field("error", err.Error()))
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
|
||||
// Initialize EPay client with merchant credentials
|
||||
client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key)
|
||||
|
||||
@ -288,6 +299,48 @@ func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// CryptoSaaSPayment processes CryptoSaaSPayment payment by generating a payment URL for redirect
|
||||
// It handles currency conversion and creates a payment URL for external payment processing
|
||||
func (l *PurchaseCheckoutLogic) CryptoSaaSPayment(config *payment.Payment, info *order.Order, returnUrl string) (string, error) {
|
||||
// Parse EPay configuration from payment settings
|
||||
epayConfig := &payment.CryptoSaaSConfig{}
|
||||
if err := epayConfig.Unmarshal([]byte(config.Config)); err != nil {
|
||||
l.Errorw("[PurchaseCheckout] Unmarshal EPay config error", logger.Field("error", err.Error()))
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||
}
|
||||
// Initialize EPay client with merchant credentials
|
||||
client := epay.NewClient(epayConfig.AccountID, epayConfig.Endpoint, epayConfig.SecretKey)
|
||||
|
||||
// Convert order amount to CNY using current exchange rate
|
||||
amount, err := l.queryExchangeRate("CNY", info.Amount)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Build notification URL for payment status callbacks
|
||||
notifyUrl := ""
|
||||
if config.Domain != "" {
|
||||
notifyUrl = config.Domain + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||
} else {
|
||||
host, ok := l.ctx.Value(constant.CtxKeyRequestHost).(string)
|
||||
if !ok {
|
||||
host = l.svcCtx.Config.Host
|
||||
}
|
||||
notifyUrl = "https://" + host + "/v1/notify/" + config.Platform + "/" + config.Token
|
||||
}
|
||||
|
||||
// Create payment URL for user redirection
|
||||
url := client.CreatePayUrl(epay.Order{
|
||||
Name: l.svcCtx.Config.Site.SiteName,
|
||||
Amount: amount,
|
||||
OrderNo: info.OrderNo,
|
||||
SignType: "MD5",
|
||||
NotifyUrl: notifyUrl,
|
||||
ReturnUrl: returnUrl,
|
||||
})
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// queryExchangeRate converts the order amount from system currency to target currency
|
||||
// It retrieves the current exchange rate and performs currency conversion if needed
|
||||
func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount float64, err error) {
|
||||
|
||||
@ -46,13 +46,19 @@ type StripeConfig struct {
|
||||
Payment string `json:"payment"`
|
||||
}
|
||||
|
||||
func (l *StripeConfig) Marshal() string {
|
||||
b, _ := json.Marshal(l)
|
||||
return string(b)
|
||||
func (l *StripeConfig) Marshal() ([]byte, error) {
|
||||
type Alias StripeConfig
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(l),
|
||||
})
|
||||
}
|
||||
|
||||
func (l *StripeConfig) Unmarshal(s string) error {
|
||||
return json.Unmarshal([]byte(s), l)
|
||||
func (l *StripeConfig) Unmarshal(data []byte) error {
|
||||
type Alias StripeConfig
|
||||
aux := (*Alias)(l)
|
||||
return json.Unmarshal(data, &aux)
|
||||
}
|
||||
|
||||
type AlipayF2FConfig struct {
|
||||
@ -63,13 +69,19 @@ type AlipayF2FConfig struct {
|
||||
Sandbox bool `json:"sandbox"`
|
||||
}
|
||||
|
||||
func (l *AlipayF2FConfig) Marshal() string {
|
||||
b, _ := json.Marshal(l)
|
||||
return string(b)
|
||||
func (l *AlipayF2FConfig) Marshal() ([]byte, error) {
|
||||
type Alias AlipayF2FConfig
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(l),
|
||||
})
|
||||
}
|
||||
|
||||
func (l *AlipayF2FConfig) Unmarshal(s string) error {
|
||||
return json.Unmarshal([]byte(s), l)
|
||||
func (l *AlipayF2FConfig) Unmarshal(data []byte) error {
|
||||
type Alias AlipayF2FConfig
|
||||
aux := (*Alias)(l)
|
||||
return json.Unmarshal(data, &aux)
|
||||
}
|
||||
|
||||
type EPayConfig struct {
|
||||
@ -78,11 +90,38 @@ type EPayConfig struct {
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
func (l *EPayConfig) Marshal() string {
|
||||
b, _ := json.Marshal(l)
|
||||
return string(b)
|
||||
func (l *EPayConfig) Marshal() ([]byte, error) {
|
||||
type Alias EPayConfig
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(l),
|
||||
})
|
||||
}
|
||||
|
||||
func (l *EPayConfig) Unmarshal(s string) error {
|
||||
return json.Unmarshal([]byte(s), l)
|
||||
func (l *EPayConfig) Unmarshal(data []byte) error {
|
||||
type Alias EPayConfig
|
||||
aux := (*Alias)(l)
|
||||
return json.Unmarshal(data, &aux)
|
||||
}
|
||||
|
||||
type CryptoSaaSConfig struct {
|
||||
Endpoint string `json:"endpoint"`
|
||||
AccountID string `json:"account_id"`
|
||||
SecretKey string `json:"secret_key"`
|
||||
}
|
||||
|
||||
func (l *CryptoSaaSConfig) Marshal() ([]byte, error) {
|
||||
type Alias CryptoSaaSConfig
|
||||
return json.Marshal(&struct {
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(l),
|
||||
})
|
||||
}
|
||||
|
||||
func (l *CryptoSaaSConfig) Unmarshal(data []byte) error {
|
||||
type Alias CryptoSaaSConfig
|
||||
aux := (*Alias)(l)
|
||||
return json.Unmarshal(data, &aux)
|
||||
}
|
||||
|
||||
@ -9,10 +9,12 @@ const (
|
||||
AlipayF2F
|
||||
EPay
|
||||
Balance
|
||||
UNSUPPORTED
|
||||
CryptoSaaS
|
||||
UNSUPPORTED Platform = -1
|
||||
)
|
||||
|
||||
var platformNames = map[string]Platform{
|
||||
"CryptoSaaS": CryptoSaaS,
|
||||
"Stripe": Stripe,
|
||||
"AlipayF2F": AlipayF2F,
|
||||
"EPay": EPay,
|
||||
@ -68,5 +70,14 @@ func GetSupportedPlatforms() []types.PlatformInfo {
|
||||
"key": "Key",
|
||||
},
|
||||
},
|
||||
{
|
||||
Platform: CryptoSaaS.String(),
|
||||
PlatformUrl: "https://t.me/CryptoSaaSBot",
|
||||
PlatformFieldDescription: map[string]string{
|
||||
"endpoint": "API Endpoint",
|
||||
"account_id": "Account ID",
|
||||
"secret_key": "Secret Key",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user