package wechat import ( "encoding/json" "fmt" "soul-api/internal/config" "soul-api/internal/wechat/transferv3" ) // MerchantBalance 商户余额(微信返回,单位:分) type MerchantBalance struct { AvailableAmount int64 `json:"available_amount"` // 可用余额(分) PendingAmount int64 `json:"pending_amount"` // 不可用/待结算余额(分) } // QueryMerchantBalance 查询商户平台账户实时余额(API: GET /v3/merchant/fund/balance/{account_type}) // accountType: BASIC(基本户) | OPERATION(运营账户) | FEES(手续费账户),默认 BASIC // 注意:普通商户可能需向微信申请开通权限,403 NO_AUTH 时表示未开通 func QueryMerchantBalance(accountType string) (*MerchantBalance, error) { if accountType == "" { accountType = "BASIC" } cfg := config.Get() if cfg == nil { return nil, fmt.Errorf("配置未加载") } key, err := transferv3.LoadPrivateKeyFromPath(cfg.WechatKeyPath) if err != nil { return nil, fmt.Errorf("加载商户私钥失败: %w", err) } client := transferv3.NewClient(cfg.WechatMchID, cfg.WechatAppID, cfg.WechatSerialNo, key) data, status, err := client.GetMerchantBalance(accountType) if err != nil { return nil, fmt.Errorf("请求微信接口失败: %w", err) } if status != 200 { // 403 NO_AUTH 时返回友好提示,便于管理端展示 if status == 403 { var errResp struct { Code string `json:"code"` Message string `json:"message"` } _ = json.Unmarshal(data, &errResp) if errResp.Code == "NO_AUTH" { return nil, fmt.Errorf("NO_AUTH: 当前商户号未开通余额查询权限,请登录微信商户平台联系客服申请") } } return nil, fmt.Errorf("微信返回 %d: %s", status, string(data)) } var bal MerchantBalance if err := json.Unmarshal(data, &bal); err != nil { return nil, fmt.Errorf("解析余额响应失败: %w", err) } return &bal, nil }