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) {
|
switch payment.ParsePlatform(platform) {
|
||||||
case payment.EPay:
|
case payment.EPay, payment.CryptoSaaS:
|
||||||
req := &types.EPayNotifyRequest{}
|
req := &types.EPayNotifyRequest{}
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
result.HttpResult(c, nil, err)
|
result.HttpResult(c, nil, err)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/perfect-panel/server/pkg/result"
|
"github.com/perfect-panel/server/pkg/result"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Purchase Checkout
|
// PurchaseCheckoutHandler Purchase Checkout
|
||||||
func PurchaseCheckoutHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
func PurchaseCheckoutHandler(svcCtx *svc.ServiceContext) func(c *gin.Context) {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
var req types.CheckoutOrderRequest
|
var req types.CheckoutOrderRequest
|
||||||
|
|||||||
@ -55,10 +55,9 @@ func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentM
|
|||||||
Token: random.KeyNew(8, 1),
|
Token: random.KeyNew(8, 1),
|
||||||
}
|
}
|
||||||
err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
err = l.svcCtx.PaymentModel.Transaction(l.ctx, func(tx *gorm.DB) error {
|
||||||
|
|
||||||
if req.Platform == "Stripe" {
|
if req.Platform == "Stripe" {
|
||||||
var cfg paymentModel.StripeConfig
|
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())
|
l.Errorf("[CreatePaymentMethod] unmarshal stripe config error: %s", err.Error())
|
||||||
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "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())
|
return errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "create stripe webhook endpoint error: %s", err.Error())
|
||||||
}
|
}
|
||||||
cfg.WebhookSecret = endpoint.Secret
|
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 {
|
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())
|
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 {
|
func parsePaymentPlatformConfig(ctx context.Context, platform payment.Platform, config interface{}) string {
|
||||||
data, err := json.Marshal(config)
|
data, err := json.Marshal(config)
|
||||||
if err != nil {
|
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 {
|
switch platform {
|
||||||
case payment.Stripe:
|
case payment.Stripe:
|
||||||
stripe := &paymentModel.StripeConfig{}
|
return handleConfig("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()
|
|
||||||
case payment.AlipayF2F:
|
case payment.AlipayF2F:
|
||||||
alipay := &paymentModel.AlipayF2FConfig{}
|
return handleConfig("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()
|
|
||||||
case payment.EPay:
|
case payment.EPay:
|
||||||
epay := &paymentModel.EPayConfig{}
|
return handleConfig("Epay", &paymentModel.EPayConfig{})
|
||||||
if err := epay.Unmarshal(string(data)); err != nil {
|
case payment.CryptoSaaS:
|
||||||
logger.WithContext(ctx).Errorw("parse epay config error", logger.Field("config", string(data)), logger.Field("error", err.Error()))
|
return handleConfig("CryptoSaaS", &paymentModel.CryptoSaaSConfig{})
|
||||||
}
|
|
||||||
return epay.Marshal()
|
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ type UpdatePaymentMethodLogic struct {
|
|||||||
svcCtx *svc.ServiceContext
|
svcCtx *svc.ServiceContext
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update Payment Method
|
// NewUpdatePaymentMethodLogic Update Payment Method
|
||||||
func NewUpdatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePaymentMethodLogic {
|
func NewUpdatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePaymentMethodLogic {
|
||||||
return &UpdatePaymentMethodLogic{
|
return &UpdatePaymentMethodLogic{
|
||||||
Logger: logger.WithContext(ctx),
|
Logger: logger.WithContext(ctx),
|
||||||
|
|||||||
@ -106,6 +106,17 @@ func (l *PurchaseCheckoutLogic) PurchaseCheckout(req *types.CheckoutOrderRequest
|
|||||||
CheckoutUrl: url,
|
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:
|
case paymentPlatform.Balance:
|
||||||
// Process balance payment - validate user and process payment immediately
|
// Process balance payment - validate user and process payment immediately
|
||||||
if orderInfo.UserId == 0 {
|
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
|
// 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) {
|
func (l *PurchaseCheckoutLogic) alipayF2fPayment(pay *payment.Payment, info *order.Order) (string, error) {
|
||||||
// Parse Alipay F2F configuration from payment settings
|
// Parse Alipay F2F configuration from payment settings
|
||||||
f2FConfig := payment.AlipayF2FConfig{}
|
f2FConfig := &payment.AlipayF2FConfig{}
|
||||||
if err := json.Unmarshal([]byte(pay.Config), &f2FConfig); err != nil {
|
if err := f2FConfig.Unmarshal([]byte(pay.Config)); err != nil {
|
||||||
l.Errorw("[PurchaseCheckout] Unmarshal Alipay config error", logger.Field("error", err.Error()))
|
l.Errorw("[PurchaseCheckout] Unmarshal Alipay config error", logger.Field("error", err.Error()))
|
||||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", 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
|
// 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) {
|
func (l *PurchaseCheckoutLogic) stripePayment(config string, info *order.Order, identifier string) (*types.StripePayment, error) {
|
||||||
// Parse Stripe configuration from payment settings
|
// Parse Stripe configuration from payment settings
|
||||||
stripeConfig := payment.StripeConfig{}
|
stripeConfig := &payment.StripeConfig{}
|
||||||
if err := json.Unmarshal([]byte(config), &stripeConfig); err != nil {
|
|
||||||
|
if err := stripeConfig.Unmarshal([]byte(config)); err != nil {
|
||||||
l.Errorw("[PurchaseCheckout] Unmarshal Stripe config error", logger.Field("error", err.Error()))
|
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())
|
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
|
// 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) {
|
func (l *PurchaseCheckoutLogic) epayPayment(config *payment.Payment, info *order.Order, returnUrl string) (string, error) {
|
||||||
// Parse EPay configuration from payment settings
|
// Parse EPay configuration from payment settings
|
||||||
epayConfig := payment.EPayConfig{}
|
epayConfig := &payment.EPayConfig{}
|
||||||
if err := json.Unmarshal([]byte(config.Config), &epayConfig); err != nil {
|
if err := epayConfig.Unmarshal([]byte(config.Config)); err != nil {
|
||||||
l.Errorw("[PurchaseCheckout] Unmarshal EPay config error", logger.Field("error", err.Error()))
|
l.Errorw("[PurchaseCheckout] Unmarshal EPay config error", logger.Field("error", err.Error()))
|
||||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
return "", errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Unmarshal error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize EPay client with merchant credentials
|
// Initialize EPay client with merchant credentials
|
||||||
client := epay.NewClient(epayConfig.Pid, epayConfig.Url, epayConfig.Key)
|
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
|
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
|
// queryExchangeRate converts the order amount from system currency to target currency
|
||||||
// It retrieves the current exchange rate and performs currency conversion if needed
|
// It retrieves the current exchange rate and performs currency conversion if needed
|
||||||
func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount float64, err error) {
|
func (l *PurchaseCheckoutLogic) queryExchangeRate(to string, src int64) (amount float64, err error) {
|
||||||
|
|||||||
@ -46,13 +46,19 @@ type StripeConfig struct {
|
|||||||
Payment string `json:"payment"`
|
Payment string `json:"payment"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StripeConfig) Marshal() string {
|
func (l *StripeConfig) Marshal() ([]byte, error) {
|
||||||
b, _ := json.Marshal(l)
|
type Alias StripeConfig
|
||||||
return string(b)
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(l),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *StripeConfig) Unmarshal(s string) error {
|
func (l *StripeConfig) Unmarshal(data []byte) error {
|
||||||
return json.Unmarshal([]byte(s), l)
|
type Alias StripeConfig
|
||||||
|
aux := (*Alias)(l)
|
||||||
|
return json.Unmarshal(data, &aux)
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlipayF2FConfig struct {
|
type AlipayF2FConfig struct {
|
||||||
@ -63,13 +69,19 @@ type AlipayF2FConfig struct {
|
|||||||
Sandbox bool `json:"sandbox"`
|
Sandbox bool `json:"sandbox"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *AlipayF2FConfig) Marshal() string {
|
func (l *AlipayF2FConfig) Marshal() ([]byte, error) {
|
||||||
b, _ := json.Marshal(l)
|
type Alias AlipayF2FConfig
|
||||||
return string(b)
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(l),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *AlipayF2FConfig) Unmarshal(s string) error {
|
func (l *AlipayF2FConfig) Unmarshal(data []byte) error {
|
||||||
return json.Unmarshal([]byte(s), l)
|
type Alias AlipayF2FConfig
|
||||||
|
aux := (*Alias)(l)
|
||||||
|
return json.Unmarshal(data, &aux)
|
||||||
}
|
}
|
||||||
|
|
||||||
type EPayConfig struct {
|
type EPayConfig struct {
|
||||||
@ -78,11 +90,38 @@ type EPayConfig struct {
|
|||||||
Key string `json:"key"`
|
Key string `json:"key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *EPayConfig) Marshal() string {
|
func (l *EPayConfig) Marshal() ([]byte, error) {
|
||||||
b, _ := json.Marshal(l)
|
type Alias EPayConfig
|
||||||
return string(b)
|
return json.Marshal(&struct {
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Alias: (*Alias)(l),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *EPayConfig) Unmarshal(s string) error {
|
func (l *EPayConfig) Unmarshal(data []byte) error {
|
||||||
return json.Unmarshal([]byte(s), l)
|
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
|
AlipayF2F
|
||||||
EPay
|
EPay
|
||||||
Balance
|
Balance
|
||||||
UNSUPPORTED
|
CryptoSaaS
|
||||||
|
UNSUPPORTED Platform = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
var platformNames = map[string]Platform{
|
var platformNames = map[string]Platform{
|
||||||
|
"CryptoSaaS": CryptoSaaS,
|
||||||
"Stripe": Stripe,
|
"Stripe": Stripe,
|
||||||
"AlipayF2F": AlipayF2F,
|
"AlipayF2F": AlipayF2F,
|
||||||
"EPay": EPay,
|
"EPay": EPay,
|
||||||
@ -68,5 +70,14 @@ func GetSupportedPlatforms() []types.PlatformInfo {
|
|||||||
"key": "Key",
|
"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