fix(payment): restore upgrade-safe payment flows

This commit is contained in:
IanShaw027
2026-04-22 14:57:16 +08:00
parent 36aed35957
commit 1aab084ecb
14 changed files with 645 additions and 68 deletions

View File

@@ -204,8 +204,8 @@ func (w *Wxpay) CreatePayment(ctx context.Context, req payment.CreatePaymentRequ
if err == nil {
return resp, nil
}
if strings.Contains(err.Error(), wxpayErrNoAuth) {
return nil, fmt.Errorf("wxpay h5 payments are not authorized for this merchant: %w", err)
if wxpayShouldFallbackToNative(err) {
return w.prepayNativeFallback(ctx, client, req, notifyURL, totalFen)
}
return nil, err
case wxpayModeNative:
@@ -292,6 +292,23 @@ func (w *Wxpay) prepayH5(ctx context.Context, c *core.Client, req payment.Create
return &payment.CreatePaymentResponse{TradeNo: req.OrderID, PayURL: h5URL}, nil
}
func (w *Wxpay) prepayNativeFallback(ctx context.Context, c *core.Client, req payment.CreatePaymentRequest, notifyURL string, totalFen int64) (*payment.CreatePaymentResponse, error) {
resp, err := w.prepayNative(ctx, c, req, notifyURL, totalFen)
if err != nil {
return nil, fmt.Errorf("wxpay native fallback after NO_AUTH: %w", err)
}
nativeURL := strings.TrimSpace(resp.PayURL)
if nativeURL == "" {
nativeURL = strings.TrimSpace(resp.QRCode)
}
if nativeURL == "" {
return resp, nil
}
resp.PayURL = nativeURL
resp.QRCode = nativeURL
return resp, nil
}
func buildWxpayH5Info(config map[string]string) *h5.H5Info {
tp := wxpayH5Type
info := &h5.H5Info{Type: &tp}
@@ -304,6 +321,10 @@ func buildWxpayH5Info(config map[string]string) *h5.H5Info {
return info
}
func wxpayShouldFallbackToNative(err error) bool {
return err != nil && strings.Contains(err.Error(), wxpayErrNoAuth)
}
func resolveWxpayCreateMode(req payment.CreatePaymentRequest) (string, error) {
if strings.TrimSpace(req.OpenID) != "" {
return wxpayModeJSAPI, nil

View File

@@ -8,6 +8,7 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"net/url"
"strings"
"testing"
@@ -641,3 +642,68 @@ func TestCreatePaymentMobileH5IncludesConfiguredSceneInfo(t *testing.T) {
t.Fatalf("pay_url = %q, want redirect_url query appended", resp.PayURL)
}
}
func TestCreatePaymentMobileH5FallsBackToNativeOnNoAuth(t *testing.T) {
origJSAPIPrepay := wxpayJSAPIPrepayWithRequestPayment
origNativePrepay := wxpayNativePrepay
origH5Prepay := wxpayH5Prepay
t.Cleanup(func() {
wxpayJSAPIPrepayWithRequestPayment = origJSAPIPrepay
wxpayNativePrepay = origNativePrepay
wxpayH5Prepay = origH5Prepay
})
jsapiCalls := 0
nativeCalls := 0
h5Calls := 0
wxpayJSAPIPrepayWithRequestPayment = func(ctx context.Context, svc jsapi.JsapiApiService, req jsapi.PrepayRequest) (*jsapi.PrepayWithRequestPaymentResponse, *core.APIResult, error) {
jsapiCalls++
return &jsapi.PrepayWithRequestPaymentResponse{}, nil, nil
}
wxpayH5Prepay = func(ctx context.Context, svc h5.H5ApiService, req h5.PrepayRequest) (*h5.PrepayResponse, *core.APIResult, error) {
h5Calls++
return nil, nil, errors.New("NO_AUTH")
}
wxpayNativePrepay = func(ctx context.Context, svc native.NativeApiService, req native.PrepayRequest) (*native.PrepayResponse, *core.APIResult, error) {
nativeCalls++
return &native.PrepayResponse{
CodeUrl: core.String("weixin://wxpay/bizpayurl?pr=fallback-native"),
}, nil, nil
}
provider := &Wxpay{
config: map[string]string{
"appId": "wx123",
"mchId": "mch123",
},
coreClient: &core.Client{},
}
resp, err := provider.CreatePayment(context.Background(), payment.CreatePaymentRequest{
OrderID: "sub2_100",
Amount: "66.88",
PaymentType: payment.TypeWxpay,
Subject: "Balance Recharge",
NotifyURL: "https://merchant.example/payment/notify",
ClientIP: "203.0.113.10",
IsMobile: true,
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if jsapiCalls != 0 {
t.Fatalf("jsapi prepay calls = %d, want 0", jsapiCalls)
}
if h5Calls != 1 {
t.Fatalf("h5 prepay calls = %d, want 1", h5Calls)
}
if nativeCalls != 1 {
t.Fatalf("native prepay calls = %d, want 1", nativeCalls)
}
if resp.PayURL != "weixin://wxpay/bizpayurl?pr=fallback-native" {
t.Fatalf("pay_url = %q, want native fallback url", resp.PayURL)
}
if resp.QRCode != "weixin://wxpay/bizpayurl?pr=fallback-native" {
t.Fatalf("qr_code = %q, want native fallback url", resp.QRCode)
}
}