57 lines
1.9 KiB
Go
57 lines
1.9 KiB
Go
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
|
||
}
|