refactor payment platform canonicalization and order method consistency
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 8m1s
Some checks failed
Build docker and publish / build (20.15.1) (push) Failing after 8m1s
This commit is contained in:
parent
82626dd749
commit
0c544268e5
@ -36,11 +36,13 @@ func NewCreatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentMethodRequest) (resp *types.PaymentConfig, err error) {
|
func (l *CreatePaymentMethodLogic) CreatePaymentMethod(req *types.CreatePaymentMethodRequest) (resp *types.PaymentConfig, err error) {
|
||||||
if payment.ParsePlatform(req.Platform) == payment.UNSUPPORTED {
|
platformType := payment.ParsePlatform(req.Platform)
|
||||||
|
if platformType == payment.UNSUPPORTED {
|
||||||
l.Errorw("unsupported payment platform", logger.Field("mark", req.Platform))
|
l.Errorw("unsupported payment platform", logger.Field("mark", req.Platform))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(400, "UNSUPPORTED_PAYMENT_PLATFORM"), "unsupported payment platform: %s", req.Platform)
|
return nil, errors.Wrapf(xerr.NewErrCodeMsg(400, "UNSUPPORTED_PAYMENT_PLATFORM"), "unsupported payment platform: %s", req.Platform)
|
||||||
}
|
}
|
||||||
config := parsePaymentPlatformConfig(l.ctx, payment.ParsePlatform(req.Platform), req.Config)
|
req.Platform = platformType.String()
|
||||||
|
config := parsePaymentPlatformConfig(l.ctx, platformType, req.Config)
|
||||||
var paymentMethod = &paymentModel.Payment{
|
var paymentMethod = &paymentModel.Payment{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Platform: req.Platform,
|
Platform: req.Platform,
|
||||||
@ -55,7 +57,7 @@ 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 platformType == payment.Stripe {
|
||||||
var cfg paymentModel.StripeConfig
|
var cfg paymentModel.StripeConfig
|
||||||
if err = cfg.Unmarshal([]byte(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())
|
||||||
|
|||||||
@ -29,7 +29,8 @@ func NewUpdatePaymentMethodLogic(ctx context.Context, svcCtx *svc.ServiceContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *UpdatePaymentMethodLogic) UpdatePaymentMethod(req *types.UpdatePaymentMethodRequest) (resp *types.PaymentConfig, err error) {
|
func (l *UpdatePaymentMethodLogic) UpdatePaymentMethod(req *types.UpdatePaymentMethodRequest) (resp *types.PaymentConfig, err error) {
|
||||||
if payment.ParsePlatform(req.Platform) == payment.UNSUPPORTED {
|
platformType := payment.ParsePlatform(req.Platform)
|
||||||
|
if platformType == payment.UNSUPPORTED {
|
||||||
l.Errorw("unsupported payment platform", logger.Field("mark", req.Platform))
|
l.Errorw("unsupported payment platform", logger.Field("mark", req.Platform))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(400, "UNSUPPORTED_PAYMENT_PLATFORM"), "unsupported payment platform: %s", req.Platform)
|
return nil, errors.Wrapf(xerr.NewErrCodeMsg(400, "UNSUPPORTED_PAYMENT_PLATFORM"), "unsupported payment platform: %s", req.Platform)
|
||||||
}
|
}
|
||||||
@ -38,7 +39,13 @@ func (l *UpdatePaymentMethodLogic) UpdatePaymentMethod(req *types.UpdatePaymentM
|
|||||||
l.Errorw("find payment method error", logger.Field("id", req.Id), logger.Field("error", err.Error()))
|
l.Errorw("find payment method error", logger.Field("id", req.Id), logger.Field("error", err.Error()))
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find payment method error: %s", err.Error())
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "find payment method error: %s", err.Error())
|
||||||
}
|
}
|
||||||
config := parsePaymentPlatformConfig(l.ctx, payment.ParsePlatform(req.Platform), req.Config)
|
existingPlatformType := payment.ParsePlatform(method.Platform)
|
||||||
|
if existingPlatformType != payment.UNSUPPORTED && existingPlatformType != platformType {
|
||||||
|
l.Errorw("payment platform mismatch", logger.Field("id", req.Id), logger.Field("current", method.Platform), logger.Field("request", req.Platform))
|
||||||
|
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.InvalidParams, "payment platform mismatch"), "payment platform mismatch: %s -> %s", method.Platform, req.Platform)
|
||||||
|
}
|
||||||
|
req.Platform = platformType.String()
|
||||||
|
config := parsePaymentPlatformConfig(l.ctx, platformType, req.Config)
|
||||||
tool.DeepCopy(method, req, tool.CopyWithIgnoreEmpty(false))
|
tool.DeepCopy(method, req, tool.CopyWithIgnoreEmpty(false))
|
||||||
method.Config = config
|
method.Config = config
|
||||||
if err := l.svcCtx.PaymentModel.Update(l.ctx, method); err != nil {
|
if err := l.svcCtx.PaymentModel.Update(l.ctx, method); err != nil {
|
||||||
|
|||||||
11
internal/logic/public/order/paymentMethod.go
Normal file
11
internal/logic/public/order/paymentMethod.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package order
|
||||||
|
|
||||||
|
import paymentPlatform "github.com/perfect-panel/server/pkg/payment"
|
||||||
|
|
||||||
|
func canonicalOrderMethod(method string) string {
|
||||||
|
platform := paymentPlatform.ParsePlatform(method)
|
||||||
|
if platform == paymentPlatform.UNSUPPORTED {
|
||||||
|
return method
|
||||||
|
}
|
||||||
|
return platform.String()
|
||||||
|
}
|
||||||
@ -224,7 +224,7 @@ func (l *PurchaseLogic) Purchase(req *types.PurchaseOrderRequest) (resp *types.P
|
|||||||
Coupon: req.Coupon,
|
Coupon: req.Coupon,
|
||||||
CouponDiscount: coupon,
|
CouponDiscount: coupon,
|
||||||
PaymentId: payment.Id,
|
PaymentId: payment.Id,
|
||||||
Method: payment.Platform,
|
Method: canonicalOrderMethod(payment.Platform),
|
||||||
FeeAmount: feeAmount,
|
FeeAmount: feeAmount,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
IsNew: isNew,
|
IsNew: isNew,
|
||||||
|
|||||||
@ -88,7 +88,7 @@ func (l *RechargeLogic) Recharge(req *types.RechargeOrderRequest) (resp *types.R
|
|||||||
Amount: totalAmount,
|
Amount: totalAmount,
|
||||||
FeeAmount: feeAmount,
|
FeeAmount: feeAmount,
|
||||||
PaymentId: payment.Id,
|
PaymentId: payment.Id,
|
||||||
Method: payment.Platform,
|
Method: canonicalOrderMethod(payment.Platform),
|
||||||
Status: 1,
|
Status: 1,
|
||||||
IsNew: isNew,
|
IsNew: isNew,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -175,7 +175,7 @@ func (l *RenewalLogic) Renewal(req *types.RenewalOrderRequest) (resp *types.Rene
|
|||||||
Coupon: req.Coupon,
|
Coupon: req.Coupon,
|
||||||
CouponDiscount: coupon,
|
CouponDiscount: coupon,
|
||||||
PaymentId: payment.Id,
|
PaymentId: payment.Id,
|
||||||
Method: payment.Platform,
|
Method: canonicalOrderMethod(payment.Platform),
|
||||||
FeeAmount: feeAmount,
|
FeeAmount: feeAmount,
|
||||||
Status: 1,
|
Status: 1,
|
||||||
SubscribeId: userSubscribe.SubscribeId,
|
SubscribeId: userSubscribe.SubscribeId,
|
||||||
|
|||||||
@ -90,7 +90,7 @@ func (l *ResetTrafficLogic) ResetTraffic(req *types.ResetTrafficOrderRequest) (r
|
|||||||
GiftAmount: deductionAmount,
|
GiftAmount: deductionAmount,
|
||||||
FeeAmount: feeAmount,
|
FeeAmount: feeAmount,
|
||||||
PaymentId: payment.Id,
|
PaymentId: payment.Id,
|
||||||
Method: payment.Platform,
|
Method: canonicalOrderMethod(payment.Platform),
|
||||||
Status: 1,
|
Status: 1,
|
||||||
SubscribeId: userSubscribe.SubscribeId,
|
SubscribeId: userSubscribe.SubscribeId,
|
||||||
SubscribeToken: userSubscribe.Token,
|
SubscribeToken: userSubscribe.Token,
|
||||||
|
|||||||
@ -89,6 +89,8 @@ func NewResponseWriter(c *gin.Context, srvCtx *svc.ServiceContext) (rw *Response
|
|||||||
rw = &ResponseWriter{
|
rw = &ResponseWriter{
|
||||||
c: c,
|
c: c,
|
||||||
body: new(bytes.Buffer),
|
body: new(bytes.Buffer),
|
||||||
|
size: noWritten,
|
||||||
|
status: defaultStatus,
|
||||||
ResponseWriter: c.Writer,
|
ResponseWriter: c.Writer,
|
||||||
}
|
}
|
||||||
rw.encryptionKey = srvCtx.Config.Device.SecuritySecret
|
rw.encryptionKey = srvCtx.Config.Device.SecuritySecret
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
package payment
|
package payment
|
||||||
|
|
||||||
import "github.com/perfect-panel/server/internal/types"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/perfect-panel/server/internal/types"
|
||||||
|
)
|
||||||
|
|
||||||
type Platform int
|
type Platform int
|
||||||
|
|
||||||
@ -14,32 +18,74 @@ const (
|
|||||||
UNSUPPORTED Platform = -1
|
UNSUPPORTED Platform = -1
|
||||||
)
|
)
|
||||||
|
|
||||||
var platformNames = map[string]Platform{
|
const (
|
||||||
"CryptoSaaS": CryptoSaaS,
|
platformNameStripe = "Stripe"
|
||||||
"Stripe": Stripe,
|
platformNameAlipayF2F = "AlipayF2F"
|
||||||
"AlipayF2F": AlipayF2F,
|
platformNameEPay = "EPay"
|
||||||
"EPay": EPay,
|
platformNameBalance = "balance"
|
||||||
"AppleIAP": AppleIAP,
|
platformNameCryptoSaaS = "CryptoSaaS"
|
||||||
|
platformNameAppleIAP = "AppleIAP"
|
||||||
|
platformNameUnsupported = "unsupported"
|
||||||
|
)
|
||||||
|
|
||||||
|
var platformAliasToType = map[string]Platform{
|
||||||
|
"stripe": Stripe,
|
||||||
|
"alipayf2f": AlipayF2F,
|
||||||
|
"alipay_f2f": AlipayF2F,
|
||||||
|
"epay": EPay,
|
||||||
"balance": Balance,
|
"balance": Balance,
|
||||||
"unsupported": UNSUPPORTED,
|
"cryptosaas": CryptoSaaS,
|
||||||
|
"crypto_saas": CryptoSaaS,
|
||||||
|
"appleiap": AppleIAP,
|
||||||
|
"apple_iap": AppleIAP,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Platform) String() string {
|
func (p Platform) String() string {
|
||||||
for k, v := range platformNames {
|
switch p {
|
||||||
if v == p {
|
case Stripe:
|
||||||
return k
|
return platformNameStripe
|
||||||
}
|
case AlipayF2F:
|
||||||
|
return platformNameAlipayF2F
|
||||||
|
case EPay:
|
||||||
|
return platformNameEPay
|
||||||
|
case Balance:
|
||||||
|
return platformNameBalance
|
||||||
|
case CryptoSaaS:
|
||||||
|
return platformNameCryptoSaaS
|
||||||
|
case AppleIAP:
|
||||||
|
return platformNameAppleIAP
|
||||||
|
default:
|
||||||
|
return platformNameUnsupported
|
||||||
}
|
}
|
||||||
return "unsupported"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePlatform(s string) Platform {
|
func ParsePlatform(s string) Platform {
|
||||||
if p, ok := platformNames[s]; ok {
|
normalized := normalizePlatformAlias(s)
|
||||||
|
if p, ok := platformAliasToType[normalized]; ok {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
compact := strings.ReplaceAll(normalized, "_", "")
|
||||||
|
if p, ok := platformAliasToType[compact]; ok {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
return UNSUPPORTED
|
return UNSUPPORTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CanonicalPlatformName(input string) (string, bool) {
|
||||||
|
platform := ParsePlatform(input)
|
||||||
|
if platform == UNSUPPORTED {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return platform.String(), true
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizePlatformAlias(input string) string {
|
||||||
|
normalized := strings.ToLower(strings.TrimSpace(input))
|
||||||
|
normalized = strings.ReplaceAll(normalized, "-", "_")
|
||||||
|
normalized = strings.ReplaceAll(normalized, " ", "")
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
func GetSupportedPlatforms() []types.PlatformInfo {
|
func GetSupportedPlatforms() []types.PlatformInfo {
|
||||||
return []types.PlatformInfo{
|
return []types.PlatformInfo{
|
||||||
{
|
{
|
||||||
|
|||||||
69
pkg/payment/platform_test.go
Normal file
69
pkg/payment/platform_test.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package payment
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParsePlatform(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected Platform
|
||||||
|
}{
|
||||||
|
{name: "exact AppleIAP", input: "AppleIAP", expected: AppleIAP},
|
||||||
|
{name: "snake apple_iap", input: "apple_iap", expected: AppleIAP},
|
||||||
|
{name: "kebab apple-iap", input: "apple-iap", expected: AppleIAP},
|
||||||
|
{name: "compact appleiap", input: "appleiap", expected: AppleIAP},
|
||||||
|
{name: "trimmed value", input: " apple_iap ", expected: AppleIAP},
|
||||||
|
{name: "legacy exact CryptoSaaS", input: "CryptoSaaS", expected: CryptoSaaS},
|
||||||
|
{name: "snake crypto_saas", input: "crypto_saas", expected: CryptoSaaS},
|
||||||
|
{name: "unsupported", input: "unknown_gateway", expected: UNSUPPORTED},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
got := ParsePlatform(testCase.input)
|
||||||
|
if got != testCase.expected {
|
||||||
|
t.Fatalf("ParsePlatform(%q) = %v, expected %v", testCase.input, got, testCase.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlatformStringIsCanonical(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
input Platform
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{name: "stripe", input: Stripe, expected: "Stripe"},
|
||||||
|
{name: "alipay", input: AlipayF2F, expected: "AlipayF2F"},
|
||||||
|
{name: "epay", input: EPay, expected: "EPay"},
|
||||||
|
{name: "balance", input: Balance, expected: "balance"},
|
||||||
|
{name: "crypto", input: CryptoSaaS, expected: "CryptoSaaS"},
|
||||||
|
{name: "apple", input: AppleIAP, expected: "AppleIAP"},
|
||||||
|
{name: "unsupported", input: UNSUPPORTED, expected: "unsupported"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
got := testCase.input.String()
|
||||||
|
if got != testCase.expected {
|
||||||
|
t.Fatalf("Platform.String() = %q, expected %q", got, testCase.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCanonicalPlatformName(t *testing.T) {
|
||||||
|
canonical, ok := CanonicalPlatformName("apple_iap")
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected apple_iap to be supported")
|
||||||
|
}
|
||||||
|
if canonical != "AppleIAP" {
|
||||||
|
t.Fatalf("canonical name mismatch: got %q", canonical)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok = CanonicalPlatformName("not_exists")
|
||||||
|
if ok {
|
||||||
|
t.Fatalf("expected unsupported platform to return ok=false")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user