Merge pull request #1610 from touwaeriol/fix/alipay-wxpay-type-mapping
fix(payment): register Alipay/Wxpay providers for base payment types
This commit is contained in:
@@ -94,17 +94,21 @@ func (lb *DefaultLoadBalancer) SelectInstance(
|
|||||||
return lb.buildSelection(selected.inst)
|
return lb.buildSelection(selected.inst)
|
||||||
}
|
}
|
||||||
|
|
||||||
// queryEnabledInstances returns enabled instances for providerKey that support paymentType.
|
// queryEnabledInstances returns enabled instances that support paymentType.
|
||||||
|
// When providerKey is non-empty, only instances with that provider key are considered.
|
||||||
|
// When providerKey is empty, instances across all providers are considered,
|
||||||
|
// enabling cross-provider load balancing (e.g. EasyPay + Alipay direct for "alipay").
|
||||||
func (lb *DefaultLoadBalancer) queryEnabledInstances(
|
func (lb *DefaultLoadBalancer) queryEnabledInstances(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
providerKey string,
|
providerKey string,
|
||||||
paymentType PaymentType,
|
paymentType PaymentType,
|
||||||
) ([]*dbent.PaymentProviderInstance, error) {
|
) ([]*dbent.PaymentProviderInstance, error) {
|
||||||
instances, err := lb.db.PaymentProviderInstance.Query().
|
query := lb.db.PaymentProviderInstance.Query().
|
||||||
Where(
|
Where(paymentproviderinstance.Enabled(true))
|
||||||
paymentproviderinstance.ProviderKey(providerKey),
|
if providerKey != "" {
|
||||||
paymentproviderinstance.Enabled(true),
|
query = query.Where(paymentproviderinstance.ProviderKey(providerKey))
|
||||||
).
|
}
|
||||||
|
instances, err := query.
|
||||||
Order(dbent.Asc(paymentproviderinstance.FieldSortOrder)).
|
Order(dbent.Asc(paymentproviderinstance.FieldSortOrder)).
|
||||||
All(ctx)
|
All(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -113,12 +117,12 @@ func (lb *DefaultLoadBalancer) queryEnabledInstances(
|
|||||||
|
|
||||||
var matched []*dbent.PaymentProviderInstance
|
var matched []*dbent.PaymentProviderInstance
|
||||||
for _, inst := range instances {
|
for _, inst := range instances {
|
||||||
if paymentType == providerKey || InstanceSupportsType(inst.SupportedTypes, paymentType) {
|
if InstanceSupportsType(inst.SupportedTypes, paymentType) {
|
||||||
matched = append(matched, inst)
|
matched = append(matched, inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(matched) == 0 {
|
if len(matched) == 0 {
|
||||||
return nil, fmt.Errorf("no enabled instance for provider %s type %s", providerKey, paymentType)
|
return nil, fmt.Errorf("no enabled instance for payment type %s", paymentType)
|
||||||
}
|
}
|
||||||
return matched, nil
|
return matched, nil
|
||||||
}
|
}
|
||||||
@@ -258,6 +262,7 @@ func (lb *DefaultLoadBalancer) buildSelection(selected *dbent.PaymentProviderIns
|
|||||||
|
|
||||||
return &InstanceSelection{
|
return &InstanceSelection{
|
||||||
InstanceID: fmt.Sprintf("%d", selected.ID),
|
InstanceID: fmt.Sprintf("%d", selected.ID),
|
||||||
|
ProviderKey: selected.ProviderKey,
|
||||||
Config: config,
|
Config: config,
|
||||||
SupportedTypes: selected.SupportedTypes,
|
SupportedTypes: selected.SupportedTypes,
|
||||||
PaymentMode: selected.PaymentMode,
|
PaymentMode: selected.PaymentMode,
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func (a *Alipay) getClient() (*alipay.Client, error) {
|
|||||||
func (a *Alipay) Name() string { return "Alipay" }
|
func (a *Alipay) Name() string { return "Alipay" }
|
||||||
func (a *Alipay) ProviderKey() string { return payment.TypeAlipay }
|
func (a *Alipay) ProviderKey() string { return payment.TypeAlipay }
|
||||||
func (a *Alipay) SupportedTypes() []payment.PaymentType {
|
func (a *Alipay) SupportedTypes() []payment.PaymentType {
|
||||||
return []payment.PaymentType{payment.TypeAlipayDirect}
|
return []payment.PaymentType{payment.TypeAlipay}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreatePayment creates an Alipay payment page URL.
|
// CreatePayment creates an Alipay payment page URL.
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func NewWxpay(instanceID string, config map[string]string) (*Wxpay, error) {
|
|||||||
func (w *Wxpay) Name() string { return "Wxpay" }
|
func (w *Wxpay) Name() string { return "Wxpay" }
|
||||||
func (w *Wxpay) ProviderKey() string { return payment.TypeWxpay }
|
func (w *Wxpay) ProviderKey() string { return payment.TypeWxpay }
|
||||||
func (w *Wxpay) SupportedTypes() []payment.PaymentType {
|
func (w *Wxpay) SupportedTypes() []payment.PaymentType {
|
||||||
return []payment.PaymentType{payment.TypeWxpayDirect}
|
return []payment.PaymentType{payment.TypeWxpay}
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatPEM(key, keyType string) string {
|
func formatPEM(key, keyType string) string {
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ type RefundResponse struct {
|
|||||||
// InstanceSelection holds the selected provider instance and its decrypted config.
|
// InstanceSelection holds the selected provider instance and its decrypted config.
|
||||||
type InstanceSelection struct {
|
type InstanceSelection struct {
|
||||||
InstanceID string
|
InstanceID string
|
||||||
|
ProviderKey string // Provider key of the selected instance (e.g. "alipay", "easypay")
|
||||||
Config map[string]string
|
Config map[string]string
|
||||||
SupportedTypes string // Comma-separated list of supported payment types from the instance
|
SupportedTypes string // Comma-separated list of supported payment types from the instance
|
||||||
PaymentMode string // Payment display mode: "qrcode", "redirect", "popup"
|
PaymentMode string // Payment display mode: "qrcode", "redirect", "popup"
|
||||||
|
|||||||
@@ -189,19 +189,16 @@ func (s *PaymentService) checkDailyLimit(ctx context.Context, tx *dbent.Tx, user
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.PaymentOrder, req CreateOrderRequest, cfg *PaymentConfig, payAmountStr string, payAmount float64, plan *dbent.SubscriptionPlan) (*CreateOrderResponse, error) {
|
func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.PaymentOrder, req CreateOrderRequest, cfg *PaymentConfig, payAmountStr string, payAmount float64, plan *dbent.SubscriptionPlan) (*CreateOrderResponse, error) {
|
||||||
s.EnsureProviders(ctx)
|
// Select an instance across all providers that support the requested payment type.
|
||||||
providerKey := s.registry.GetProviderKey(req.PaymentType)
|
// This enables cross-provider load balancing (e.g. EasyPay + Alipay direct for "alipay").
|
||||||
if providerKey == "" {
|
sel, err := s.loadBalancer.SelectInstance(ctx, "", req.PaymentType, payment.Strategy(cfg.LoadBalanceStrategy), payAmount)
|
||||||
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", fmt.Sprintf("payment method (%s) is not configured", req.PaymentType))
|
|
||||||
}
|
|
||||||
sel, err := s.loadBalancer.SelectInstance(ctx, providerKey, req.PaymentType, payment.Strategy(cfg.LoadBalanceStrategy), payAmount)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("select provider instance: %w", err)
|
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", fmt.Sprintf("payment method (%s) is not configured", req.PaymentType))
|
||||||
}
|
}
|
||||||
if sel == nil {
|
if sel == nil {
|
||||||
return nil, infraerrors.TooManyRequests("NO_AVAILABLE_INSTANCE", "no available payment instance")
|
return nil, infraerrors.TooManyRequests("NO_AVAILABLE_INSTANCE", "no available payment instance")
|
||||||
}
|
}
|
||||||
prov, err := provider.CreateProvider(providerKey, sel.InstanceID, sel.Config)
|
prov, err := provider.CreateProvider(sel.ProviderKey, sel.InstanceID, sel.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "payment method is temporarily unavailable")
|
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", "payment method is temporarily unavailable")
|
||||||
}
|
}
|
||||||
@@ -209,7 +206,7 @@ func (s *PaymentService) invokeProvider(ctx context.Context, order *dbent.Paymen
|
|||||||
outTradeNo := order.OutTradeNo
|
outTradeNo := order.OutTradeNo
|
||||||
pr, err := prov.CreatePayment(ctx, payment.CreatePaymentRequest{OrderID: outTradeNo, Amount: payAmountStr, PaymentType: req.PaymentType, Subject: subject, ClientIP: req.ClientIP, IsMobile: req.IsMobile, InstanceSubMethods: sel.SupportedTypes})
|
pr, err := prov.CreatePayment(ctx, payment.CreatePaymentRequest{OrderID: outTradeNo, Amount: payAmountStr, PaymentType: req.PaymentType, Subject: subject, ClientIP: req.ClientIP, IsMobile: req.IsMobile, InstanceSubMethods: sel.SupportedTypes})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("[PaymentService] CreatePayment failed", "provider", providerKey, "instance", sel.InstanceID, "error", err)
|
slog.Error("[PaymentService] CreatePayment failed", "provider", sel.ProviderKey, "instance", sel.InstanceID, "error", err)
|
||||||
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", fmt.Sprintf("payment gateway error: %s", err.Error()))
|
return nil, infraerrors.ServiceUnavailable("PAYMENT_GATEWAY_ERROR", fmt.Sprintf("payment gateway error: %s", err.Error()))
|
||||||
}
|
}
|
||||||
_, err = s.entClient.PaymentOrder.UpdateOneID(order.ID).SetNillablePaymentTradeNo(psNilIfEmpty(pr.TradeNo)).SetNillablePayURL(psNilIfEmpty(pr.PayURL)).SetNillableQrCode(psNilIfEmpty(pr.QRCode)).SetNillableProviderInstanceID(psNilIfEmpty(sel.InstanceID)).Save(ctx)
|
_, err = s.entClient.PaymentOrder.UpdateOneID(order.ID).SetNillablePaymentTradeNo(psNilIfEmpty(pr.TradeNo)).SetNillablePayURL(psNilIfEmpty(pr.PayURL)).SetNillableQrCode(psNilIfEmpty(pr.QRCode)).SetNillableProviderInstanceID(psNilIfEmpty(sel.InstanceID)).Save(ctx)
|
||||||
|
|||||||
Reference in New Issue
Block a user