sync: bring over remaining release/custom-0.1.115 changes

- Extract PublicSettingsInjectionPayload named struct with drift test
- Add channel_monitor_default_interval_seconds to SSR injection
- Add image_output_price to SupportedModelChip
- Simplify AppSidebar buildSelfNavItems (admins see available channels)
- Add gateway WARN logs for 503 no-available-accounts branches
- Wire ChannelMonitorRunner into provideCleanup for graceful shutdown
- Add migrations 130/131 (CC template userid fix + mimicry field cleanup)
- Clean up fork-only features (sora, claude max simulation, client affinity)
- Remove ~320 obsolete i18n keys
- Add codexUsage utility, WechatServiceButton, BulkEditAccountModal
- Tidy go.sum
This commit is contained in:
erio
2026-04-23 20:55:18 +08:00
parent d5dac84e12
commit 748a84d871
76 changed files with 1380 additions and 1699 deletions

View File

@@ -15,9 +15,8 @@ import (
// Alipay product codes.
const (
alipayProductCodePreCreate = "FACE_TO_FACE_PAYMENT"
alipayProductCodeWapPay = "QUICK_WAP_WAY"
alipayProductCodePagePay = "FAST_INSTANT_TRADE_PAY"
alipayProductCodeWapPay = "QUICK_WAP_WAY"
alipayProductCodePagePay = "FAST_INSTANT_TRADE_PAY"
)
// Alipay response constants.
@@ -31,9 +30,6 @@ var (
alipayTradeWapPay = func(client *alipay.Client, param alipay.TradeWapPay) (*url.URL, error) {
return client.TradeWapPay(param)
}
alipayTradePreCreate = func(ctx context.Context, client *alipay.Client, param alipay.TradePreCreate) (*alipay.TradePreCreateRsp, error) {
return client.TradePreCreate(ctx, param)
}
alipayTradePagePay = func(client *alipay.Client, param alipay.TradePagePay) (*url.URL, error) {
return client.TradePagePay(param)
}
@@ -103,13 +99,13 @@ func (a *Alipay) MerchantIdentityMetadata() map[string]string {
return map[string]string{"app_id": appID}
}
// CreatePayment creates an Alipay payment using the following routing:
// - Mobile (H5): alipay.trade.wap.pay — browser redirect into Alipay.
// - Desktop: prefer alipay.trade.precreate to get a scan payload directly.
// - Desktop fallback: if precreate is unavailable for the merchant, fall back
// to alipay.trade.page.pay and expose both pay_url and qr_code so the
// frontend can render a QR while still allowing direct page open.
func (a *Alipay) CreatePayment(ctx context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
// CreatePayment creates an Alipay payment using redirect-only flow:
// - Mobile (H5): alipay.trade.wap.pay — returns a URL the browser jumps to.
// - PC: alipay.trade.page.pay — returns a gateway URL the browser opens in a
// new window; Alipay's own page then shows login/QR. We intentionally do
// NOT encode the URL into a QR on the client (it isn't a scannable payload
// and would produce an invalid scan result).
func (a *Alipay) CreatePayment(_ context.Context, req payment.CreatePaymentRequest) (*payment.CreatePaymentResponse, error) {
client, err := a.getClient()
if err != nil {
return nil, err
@@ -127,7 +123,7 @@ func (a *Alipay) CreatePayment(ctx context.Context, req payment.CreatePaymentReq
if req.IsMobile {
return a.createWapTrade(client, req, notifyURL, returnURL)
}
return a.createDesktopTrade(ctx, client, req, notifyURL, returnURL)
return a.createPagePayTrade(client, req, notifyURL, returnURL)
}
func (a *Alipay) createWapTrade(client *alipay.Client, req payment.CreatePaymentRequest, notifyURL, returnURL string) (*payment.CreatePaymentResponse, error) {
@@ -149,48 +145,6 @@ func (a *Alipay) createWapTrade(client *alipay.Client, req payment.CreatePayment
}, nil
}
func (a *Alipay) createDesktopTrade(ctx context.Context, client *alipay.Client, req payment.CreatePaymentRequest, notifyURL, returnURL string) (*payment.CreatePaymentResponse, error) {
resp, precreateErr := a.createPrecreateTrade(ctx, client, req, notifyURL)
if precreateErr == nil {
return resp, nil
}
resp, pagePayErr := a.createPagePayTrade(client, req, notifyURL, returnURL)
if pagePayErr == nil {
return resp, nil
}
return nil, fmt.Errorf("alipay desktop payment failed: precreate=%v; pagepay=%w", precreateErr, pagePayErr)
}
func (a *Alipay) createPrecreateTrade(ctx context.Context, client *alipay.Client, req payment.CreatePaymentRequest, notifyURL string) (*payment.CreatePaymentResponse, error) {
param := alipay.TradePreCreate{}
param.OutTradeNo = req.OrderID
param.TotalAmount = req.Amount
param.Subject = req.Subject
param.ProductCode = alipayProductCodePreCreate
param.NotifyURL = notifyURL
rsp, err := alipayTradePreCreate(ctx, client, param)
if err != nil {
return nil, fmt.Errorf("alipay TradePreCreate: %w", err)
}
if rsp == nil {
return nil, fmt.Errorf("alipay TradePreCreate: empty response")
}
if rsp.IsFailure() {
return nil, fmt.Errorf("alipay TradePreCreate failed: %s", rsp.Error.Error())
}
if strings.TrimSpace(rsp.QRCode) == "" {
return nil, fmt.Errorf("alipay TradePreCreate: empty qr_code")
}
return &payment.CreatePaymentResponse{
TradeNo: req.OrderID,
QRCode: rsp.QRCode,
}, nil
}
func (a *Alipay) createPagePayTrade(client *alipay.Client, req payment.CreatePaymentRequest, notifyURL, returnURL string) (*payment.CreatePaymentResponse, error) {
param := alipay.TradePagePay{}
param.OutTradeNo = req.OrderID
@@ -207,7 +161,6 @@ func (a *Alipay) createPagePayTrade(client *alipay.Client, req payment.CreatePay
return &payment.CreatePaymentResponse{
TradeNo: req.OrderID,
PayURL: payURL.String(),
QRCode: payURL.String(),
}, nil
}
@@ -239,15 +192,7 @@ func (a *Alipay) QueryOrder(ctx context.Context, tradeNo string) (*payment.Query
amount, err := strconv.ParseFloat(result.TotalAmount, 64)
if err != nil {
amount, err = parseAlipayAmount(
result.TotalAmount,
result.ReceiptAmount,
result.BuyerPayAmount,
result.InvoiceAmount,
)
if err != nil {
return nil, fmt.Errorf("alipay parse amount: %w", err)
}
return nil, fmt.Errorf("alipay parse amount %q: %w", result.TotalAmount, err)
}
return &payment.QueryOrderResponse{
@@ -283,14 +228,7 @@ func (a *Alipay) VerifyNotification(ctx context.Context, rawBody string, _ map[s
amount, err := strconv.ParseFloat(notification.TotalAmount, 64)
if err != nil {
amount, err = parseAlipayAmount(
notification.TotalAmount,
notification.ReceiptAmount,
notification.BuyerPayAmount,
)
if err != nil {
return nil, fmt.Errorf("alipay parse notification amount: %w", err)
}
return nil, fmt.Errorf("alipay parse notification amount %q: %w", notification.TotalAmount, err)
}
metadata := a.MerchantIdentityMetadata()
@@ -368,20 +306,6 @@ func isTradeNotExist(err error) bool {
return strings.Contains(err.Error(), alipayErrTradeNotExist)
}
func parseAlipayAmount(values ...string) (float64, error) {
for _, raw := range values {
raw = strings.TrimSpace(raw)
if raw == "" {
continue
}
amount, err := strconv.ParseFloat(raw, 64)
if err == nil {
return amount, nil
}
}
return 0, fmt.Errorf("no valid amount field")
}
// Ensure interface compliance.
var (
_ payment.Provider = (*Alipay)(nil)