@@ -6,7 +6,9 @@ require ( | |||
github.com/gin-gonic/gin v1.8.0 | |||
github.com/go-sql-driver/mysql v1.6.0 | |||
github.com/iGoogle-ink/gopay v1.5.36 | |||
github.com/pkg/errors v0.8.1 | |||
github.com/iGoogle-ink/gotil v1.0.20 | |||
github.com/pkg/errors v0.9.1 | |||
github.com/shopspring/decimal v1.2.0 | |||
xorm.io/builder v0.3.10 // indirect | |||
xorm.io/xorm v1.3.0 | |||
) |
@@ -0,0 +1,303 @@ | |||
package local_wxpay | |||
import ( | |||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils/logx" | |||
"fmt" | |||
"github.com/iGoogle-ink/gopay" | |||
"github.com/iGoogle-ink/gopay/pkg/util" | |||
"github.com/iGoogle-ink/gopay/wechat" | |||
v3 "github.com/iGoogle-ink/gopay/wechat/v3" | |||
"strconv" | |||
"time" | |||
) | |||
func NewClient(appId, mchId, apiKey string, isProd bool) *wechat.Client { | |||
// 初始化微信客户端 | |||
// appId:应用ID | |||
// mchId:商户ID | |||
// apiKey:API秘钥值 | |||
// isProd:是否是正式环境 | |||
client := wechat.NewClient(appId, mchId, apiKey, isProd) | |||
// 打开Debug开关,输出请求日志,默认关闭 | |||
client.DebugSwitch = gopay.DebugOn | |||
// 设置国家:不设置默认 中国国内 | |||
// wechat.China:中国国内 | |||
// wechat.China2:中国国内备用 | |||
// wechat.SoutheastAsia:东南亚 | |||
// wechat.Other:其他国家 | |||
client.SetCountry(wechat.China) | |||
// 添加微信证书 Path 路径 | |||
// certFilePath:apiclient_cert.pem 路径 | |||
// keyFilePath:apiclient_key.pem 路径 | |||
// pkcs12FilePath:apiclient_cert.p12 路径 | |||
// 返回err | |||
//client.AddCertFilePath() | |||
// 添加微信证书内容 Content | |||
// certFileContent:apiclient_cert.pem 内容 | |||
// keyFileContent:apiclient_key.pem 内容 | |||
// pkcs12FileContent:apiclient_cert.p12 内容 | |||
// 返回err | |||
//client.AddCertFileContent() | |||
return client | |||
} | |||
// TradeAppPay is 微信APP支付 | |||
func TradeAppPay(client *wechat.Client, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||
// 初始化 BodyMap | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amount). | |||
Set("spbill_create_ip", "127.0.0.1"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_App). | |||
Set("sign_type", wechat.SignType_MD5) | |||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||
// 预下单 | |||
wxRsp, err := client.UnifiedOrder(bm) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
//if !ok { | |||
// return nil, errors.New("验签失败") | |||
//} | |||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
res := map[string]string{ | |||
"appid": client.AppId, | |||
"partnerid": client.MchId, | |||
"prepayid": wxRsp.PrepayId, | |||
"sign": paySign, | |||
"package": "Sign=WXPay", | |||
"noncestr": wxRsp.NonceStr, | |||
"timestamp": timeStamp, | |||
} | |||
return res, nil | |||
} | |||
// TradeAppPay is 微信H5支付 | |||
func TradeH5Pay(client *wechat.Client, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||
// 初始化 BodyMap | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amount). | |||
Set("spbill_create_ip", "121.196.29.49"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_H5). | |||
Set("sign_type", wechat.SignType_MD5). | |||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||
bm.Set("type", "Wap") | |||
bm.Set("wap_url", "https://www.fumm.cc") | |||
bm.Set("wap_name", "zyos") | |||
}) | |||
}) | |||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||
// 预下单 | |||
wxRsp, err := client.UnifiedOrder(bm) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
packages := "prepay_id=" + wxRsp.PrepayId | |||
paySign := wechat.GetH5PaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
fmt.Println("paySign===", paySign) | |||
r := map[string]string{ | |||
"redirect_url": wxRsp.MwebUrl, | |||
} | |||
return r, nil | |||
} | |||
// TradeMiniProgPay is 微信小程序支付 ☑️ | |||
func TradeMiniProgPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||
// 初始化 BodyMap | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("openid", openid). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amount). | |||
Set("spbill_create_ip", "127.0.0.1"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_Mini). | |||
Set("sign_type", wechat.SignType_MD5) | |||
// 预下单 | |||
wxRsp, err := client.UnifiedOrder(bm) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
fmt.Println(wxRsp) | |||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
packages := "prepay_id=" + wxRsp.PrepayId | |||
paySign := wechat.GetMiniPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
res := map[string]string{ | |||
"appId": client.AppId, | |||
"paySign": paySign, | |||
"signType": wechat.SignType_MD5, | |||
"package": packages, | |||
"nonceStr": wxRsp.NonceStr, | |||
"timeStamp": timeStamp, | |||
} | |||
return res, nil | |||
} | |||
// TradeAppPayV3 is 微信APP支付v3 | |||
func TradeAppPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (map[string]string, error) { | |||
// 初始化 BodyMap | |||
amountNew := zhios_pay_utils.AnyToFloat64(amount) * 100 | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amountNew). | |||
Set("spbill_create_ip", "127.0.0.1"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_App). | |||
Set("sign_type", wechat.SignType_MD5) | |||
/*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||
//// 预下单 | |||
//wxRsp, err := v3.UnifiedOrder(bm) | |||
//if err != nil { | |||
// _ = zhios_pay_logx.Warn(err) | |||
// return nil, err | |||
//} | |||
//_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||
//if err != nil { | |||
// _ = zhios_pay_logx.Warn(err) | |||
// return nil, err | |||
//} | |||
////if !ok { | |||
//// return nil, errors.New("验签失败") | |||
////} | |||
//timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
//res := map[string]string{ | |||
// "appid": client.AppId, | |||
// "partnerid": client.MchId, | |||
// "prepayid": wxRsp.PrepayId, | |||
// "sign": paySign, | |||
// "package": "Sign=WXPay", | |||
// "noncestr": wxRsp.NonceStr, | |||
// "timestamp": timeStamp, | |||
//} | |||
//return res, nil | |||
return nil, nil | |||
} | |||
//// TradeJSAPIPay is 微信JSAPI支付 | |||
func TradeJSAPIPay(client *wechat.Client, subject, orderID, amount, notifyUrl, openid string) (map[string]string, error) { | |||
// 初始化 BodyMap | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amount). | |||
Set("spbill_create_ip", "121.196.29.49"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_JsApi). | |||
Set("sign_type", wechat.SignType_MD5). | |||
Set("openid", openid). | |||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||
bm.Set("type", "Wap") | |||
bm.Set("wap_url", "https://www.fumm.cc") | |||
bm.Set("wap_name", "zyos") | |||
}) | |||
}) | |||
// 预下单 | |||
wxRsp, err := client.UnifiedOrder(bm) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
_, err = wechat.VerifySign(client.ApiKey, wechat.SignType_MD5, wxRsp) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return nil, err | |||
} | |||
//if !ok { | |||
// return nil, errors.New("验签失败") | |||
//} | |||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
//paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
packages := "prepay_id=" + wxRsp.PrepayId | |||
paySign := wechat.GetJsapiPaySign(client.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
zhios_pay_logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||
zhios_pay_logx.Info("wxRsp.PrepayId:" + wxRsp.PrepayId) | |||
zhios_pay_logx.Info("wxRsp.PrepayId:" + openid) | |||
res := map[string]string{ | |||
"appid": client.AppId, | |||
"partnerid": client.MchId, | |||
"prepayid": wxRsp.PrepayId, | |||
"sign": paySign, | |||
"package": "prepay_id=" + wxRsp.PrepayId, | |||
"noncestr": wxRsp.NonceStr, | |||
"timestamp": timeStamp, | |||
} | |||
return res, nil | |||
} | |||
// TradeH5PayV3 is 微信H5支付v3 | |||
func TradeH5PayV3(client *wechat.Client, subject, orderID, amount, notifyUrl string) (string, error) { | |||
// 初始化 BodyMap | |||
bm := make(gopay.BodyMap) | |||
bm.Set("nonce_str", util.GetRandomString(32)). | |||
Set("body", subject). | |||
Set("out_trade_no", orderID). | |||
Set("total_fee", amount). | |||
Set("spbill_create_ip", "127.0.0.1"). | |||
Set("notify_url", notifyUrl). | |||
Set("trade_type", wechat.TradeType_App). | |||
Set("device_info", "WEB"). | |||
Set("sign_type", wechat.SignType_MD5). | |||
SetBodyMap("scene_info", func(bm gopay.BodyMap) { | |||
bm.SetBodyMap("h5_info", func(bm gopay.BodyMap) { | |||
bm.Set("type", "Wap") | |||
bm.Set("wap_url", "https://www.fumm.cc") | |||
bm.Set("wap_name", "H5测试支付") | |||
}) | |||
}) /*.Set("openid", "o0Df70H2Q0fY8JXh1aFPIRyOBgu8")*/ | |||
// 预下单 | |||
wxRsp, err := client.UnifiedOrder(bm) | |||
if err != nil { | |||
_ = zhios_pay_logx.Warn(err) | |||
return "", err | |||
} | |||
// ====APP支付 paySign==== | |||
timeStamp := strconv.FormatInt(time.Now().Unix(), 10) | |||
// 获取APP支付的 paySign | |||
// 注意:package 参数因为是固定值,无需开发者再传入 | |||
// appId:AppID | |||
// partnerid:partnerid | |||
// nonceStr:随机字符串 | |||
// prepayId:统一下单成功后得到的值 | |||
// signType:签名方式,务必与统一下单时用的签名方式一致 | |||
// timeStamp:时间 | |||
// apiKey:API秘钥值 | |||
paySign := wechat.GetAppPaySign(client.AppId, client.MchId, wxRsp.NonceStr, wxRsp.PrepayId, wechat.SignType_MD5, timeStamp, client.ApiKey) | |||
return paySign, nil | |||
} | |||
// TradeMiniProgPayV3 is 微信小程序支付v3 | |||
func TradeMiniProgPayV3(client *v3.ClientV3, subject, orderID, amount, notifyUrl string) (string, error) { | |||
return "", nil | |||
} |
@@ -2,8 +2,39 @@ package md | |||
const ( | |||
/*********** DEVICE ***********/ | |||
PLATFORM_ALIPAY_APPLET = "alipay_applet" | |||
PLATFORM_WAP = "wap" //h5 | |||
PLATFORM_ANDROID = "android" | |||
PLATFORM_IOS = "ios" | |||
PLATFORM_WX_APPLET = "wx_applet" // 小程序 | |||
PLATFORM_TOUTIAO_APPLET = "toutiao_applet" | |||
PLATFORM_TIKTOK_APPLET = "tiktok_applet" | |||
PLATFORM_BAIDU_APPLET = "baidu_applet" | |||
PLATFORM_ALIPAY_APPLET = "alipay_applet" | |||
PLATFORM_WAP = "wap" //h5 | |||
PLATFORM_ANDROID = "android" | |||
PLATFORM_IOS = "ios" | |||
PLATFORM_PC = "pc" | |||
PLATFORM_JSAPI = "jsapi" // 公众号 | |||
) | |||
const WX_PAY_BROWSER = "wx_pay_browser" // 用于判断显示支付方式 | |||
var PlatformList = map[string]struct{}{ | |||
PLATFORM_WX_APPLET: {}, | |||
PLATFORM_TOUTIAO_APPLET: {}, | |||
PLATFORM_TIKTOK_APPLET: {}, | |||
PLATFORM_BAIDU_APPLET: {}, | |||
PLATFORM_ALIPAY_APPLET: {}, | |||
PLATFORM_WAP: {}, | |||
PLATFORM_ANDROID: {}, | |||
PLATFORM_IOS: {}, | |||
PLATFORM_PC: {}, | |||
} | |||
var PlatformMap = map[string]string{ | |||
"android": "2", | |||
"ios": "2", | |||
"wap": "4", // 和小程序公用模板 | |||
"wx_applet": "4", //微信小程序 | |||
"tiktok_applet": "4", | |||
"baidu_applet": "4", | |||
"alipay_applet": "4", | |||
"toutiao_applet": "4", | |||
} |
@@ -0,0 +1,35 @@ | |||
package md | |||
type WxPayParams struct { | |||
Subject string `json:"subject" binding:"required"` | |||
Amount string `json:"amount" binding:"required"` | |||
OrderType string `json:"order_type" binding:"required"` | |||
OrdId string `json:"ord_id"` | |||
PayWxAppid string `json:"pay_wx_appid"` | |||
PayWxMchId string `json:"pay_wx_mch_id"` | |||
PayWxApiKey string `json:"pay_wx_api_key"` | |||
NotifyUrl string `json:"notify_url"` | |||
ThirdPartyWechatOpenid string `json:"third_party_wechat_openid"` | |||
} | |||
type WxPayCallback struct { | |||
AppId string `json:"appid"` | |||
BankType string `json:"bank_type"` | |||
CashFee string `json:"cash_fee"` | |||
FeeType string `json:"fee_type"` | |||
IsSubscribe string `json:"is_subscribe"` | |||
MasterID string `json:"master_id"` | |||
MchID string `json:"mch_id"` | |||
NonceStr string `json:"nonce_str"` | |||
Openid string `json:"openid"` | |||
OrderType string `json:"order_type"` | |||
OutTradeNo string `json:"out_trade_no"` | |||
PayMethod string `json:"pay_method"` | |||
ResultCode string `json:"result_code"` | |||
ReturnCode string `json:"return_code"` | |||
Sign string `json:"sign"` | |||
TimeEnd string `json:"time_end"` | |||
TotalFee string `json:"total_fee"` | |||
TradeType string `json:"trade_type"` | |||
TransactionID string `json:"transaction_id"` | |||
} |
@@ -26,7 +26,7 @@ func Init(host, name, user, psw string) error { | |||
cfg.MaxLifetime = 30 | |||
cfg.MaxOpenConns = 100 | |||
cfg.MaxIdleConns = 100 | |||
cfg.Path = "./logs/%s.logs" | |||
cfg.Path = "./tmp/%s.logs" | |||
if err := db.InitDB(&cfg); err != nil { | |||
return err | |||
} | |||
@@ -2,10 +2,15 @@ package pay | |||
import ( | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/local_alipay" | |||
local_wxpay "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/lib/local_wechat" | |||
"code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/md" | |||
zhios_pay_utils "code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git/utils" | |||
"fmt" | |||
"github.com/iGoogle-ink/gopay" | |||
v3 "github.com/iGoogle-ink/gopay/wechat/v3" | |||
"github.com/iGoogle-ink/gotil/xlog" | |||
"github.com/pkg/errors" | |||
"github.com/shopspring/decimal" | |||
) | |||
//自有支付 支付宝 | |||
@@ -41,3 +46,110 @@ func Alipay(args map[string]string) (string, error) { | |||
} | |||
return zhios_pay_utils.AnyToString(param), nil | |||
} | |||
//自有支付 微信支付 | |||
// app支付v2 | |||
func WxAppPay(params *md.WxPayParams) (map[string]string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
apiKey := params.PayWxApiKey | |||
notifyUrl := params.NotifyUrl | |||
client := local_wxpay.NewClient(appId, mchId, apiKey, true) | |||
r, err := local_wxpay.TradeAppPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl) | |||
return r, err | |||
} | |||
// H5支付v2 | |||
func WxH5Pay(params *md.WxPayParams) (map[string]string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
apiKey := params.PayWxApiKey | |||
notifyUrl := params.NotifyUrl | |||
client := local_wxpay.NewClient(appId, mchId, apiKey, true) | |||
r, err := local_wxpay.TradeH5Pay(client, params.Subject, params.OrdId, params.Amount, notifyUrl) | |||
return r, err | |||
} | |||
// 小程序v2 | |||
func WxMiniProgPay(params *md.WxPayParams) (map[string]string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
apiKey := params.PayWxApiKey | |||
notifyUrl := params.NotifyUrl | |||
client := local_wxpay.NewClient(appId, mchId, apiKey, true) | |||
if params.ThirdPartyWechatOpenid == "" { | |||
return nil, errors.New("请先授权微信") | |||
} | |||
r, err := local_wxpay.TradeMiniProgPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl, params.ThirdPartyWechatOpenid) | |||
return r, err | |||
} | |||
// app支付V3 | |||
func WxAppPayV3(params *md.WxPayParams) (map[string]string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
notifyUrl := params.NotifyUrl | |||
SerialNo := params.PayWxApiKey | |||
ApiV3Key := params.PayWxApiKey | |||
PKContent := params.PayWxApiKey | |||
client, err := v3.NewClientV3(appId, mchId, SerialNo, ApiV3Key, PKContent) | |||
if err != nil { | |||
xlog.Error(err) | |||
return nil, err | |||
} | |||
client.DebugSwitch = gopay.DebugOff | |||
r, err := local_wxpay.TradeAppPayV3(client, params.Subject, params.OrdId, params.Amount, notifyUrl) | |||
return r, err | |||
} | |||
// 微信JSAPI支付 | |||
func WxAppJSAPIPay(params *md.WxPayParams) (map[string]string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
apiKey := params.PayWxApiKey | |||
notifyUrl := params.NotifyUrl | |||
client := local_wxpay.NewClient(appId, mchId, apiKey, true) | |||
if params.ThirdPartyWechatOpenid == "" { | |||
return nil, errors.New("请先授权微信") | |||
} | |||
r, err := local_wxpay.TradeJSAPIPay(client, params.Subject, params.OrdId, params.Amount, notifyUrl, params.ThirdPartyWechatOpenid) | |||
return r, err | |||
} | |||
// H5支付V3 | |||
func WxH5PayV3(params *md.WxPayParams) (string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
apiKey := params.PayWxApiKey | |||
notifyUrl := params.NotifyUrl | |||
client := local_wxpay.NewClient(appId, mchId, apiKey, false) | |||
_, err := local_wxpay.TradeH5Pay(client, params.Subject, params.OrdId, params.Amount, notifyUrl) | |||
return "", err | |||
} | |||
// 小程序V3 | |||
func WxMiniProgPayV3(params *md.WxPayParams) (string, error) { | |||
appId := params.PayWxAppid | |||
mchId := params.PayWxMchId | |||
notifyUrl := params.NotifyUrl | |||
SerialNo := params.PayWxApiKey | |||
ApiV3Key := params.PayWxApiKey | |||
PKContent := params.PayWxApiKey | |||
client, err := v3.NewClientV3(appId, mchId, SerialNo, ApiV3Key, PKContent) | |||
if err != nil { | |||
xlog.Error(err) | |||
return "", err | |||
} | |||
client.DebugSwitch = gopay.DebugOff | |||
r, err := local_wxpay.TradeMiniProgPayV3(client, params.Subject, params.OrdId, params.Amount, notifyUrl) | |||
return r, err | |||
} | |||
// 微信金额乘100 | |||
func wxMoneyMulHundred(m string) string { | |||
amount, _ := decimal.NewFromString(m) | |||
newM := amount.Mul(decimal.NewFromInt(100)) | |||
return newM.String() | |||
} |
@@ -0,0 +1,245 @@ | |||
package zhios_pay_logx | |||
import ( | |||
"os" | |||
"strings" | |||
"time" | |||
"go.uber.org/zap" | |||
"go.uber.org/zap/zapcore" | |||
) | |||
type LogConfig struct { | |||
AppName string `yaml:"app_name" json:"app_name" toml:"app_name"` | |||
Level string `yaml:"level" json:"level" toml:"level"` | |||
StacktraceLevel string `yaml:"stacktrace_level" json:"stacktrace_level" toml:"stacktrace_level"` | |||
IsStdOut bool `yaml:"is_stdout" json:"is_stdout" toml:"is_stdout"` | |||
TimeFormat string `yaml:"time_format" json:"time_format" toml:"time_format"` // second, milli, nano, standard, iso, | |||
Encoding string `yaml:"encoding" json:"encoding" toml:"encoding"` // console, json | |||
Skip int `yaml:"skip" json:"skip" toml:"skip"` | |||
IsFileOut bool `yaml:"is_file_out" json:"is_file_out" toml:"is_file_out"` | |||
FileDir string `yaml:"file_dir" json:"file_dir" toml:"file_dir"` | |||
FileName string `yaml:"file_name" json:"file_name" toml:"file_name"` | |||
FileMaxSize int `yaml:"file_max_size" json:"file_max_size" toml:"file_max_size"` | |||
FileMaxAge int `yaml:"file_max_age" json:"file_max_age" toml:"file_max_age"` | |||
} | |||
var ( | |||
l *LogX = defaultLogger() | |||
conf *LogConfig | |||
) | |||
// default logger setting | |||
func defaultLogger() *LogX { | |||
conf = &LogConfig{ | |||
Level: "debug", | |||
StacktraceLevel: "error", | |||
IsStdOut: true, | |||
TimeFormat: "standard", | |||
Encoding: "console", | |||
Skip: 2, | |||
} | |||
writers := []zapcore.WriteSyncer{os.Stdout} | |||
lg, lv := newZapLogger(setLogLevel(conf.Level), setLogLevel(conf.StacktraceLevel), conf.Encoding, conf.TimeFormat, conf.Skip, zapcore.NewMultiWriteSyncer(writers...)) | |||
zap.RedirectStdLog(lg) | |||
return &LogX{logger: lg, atomLevel: lv} | |||
} | |||
// initial standard log, if you don't init, it will use default logger setting | |||
func InitDefaultLogger(cfg *LogConfig) { | |||
var writers []zapcore.WriteSyncer | |||
if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { | |||
writers = append(writers, os.Stdout) | |||
} | |||
if cfg.IsFileOut { | |||
writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) | |||
} | |||
lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) | |||
zap.RedirectStdLog(lg) | |||
if cfg.AppName != "" { | |||
lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 | |||
} | |||
l = &LogX{logger: lg, atomLevel: lv} | |||
} | |||
// create a new logger | |||
func NewLogger(cfg *LogConfig) *LogX { | |||
var writers []zapcore.WriteSyncer | |||
if cfg.IsStdOut || (!cfg.IsStdOut && !cfg.IsFileOut) { | |||
writers = append(writers, os.Stdout) | |||
} | |||
if cfg.IsFileOut { | |||
writers = append(writers, NewRollingFile(cfg.FileDir, cfg.FileName, cfg.FileMaxSize, cfg.FileMaxAge)) | |||
} | |||
lg, lv := newZapLogger(setLogLevel(cfg.Level), setLogLevel(cfg.StacktraceLevel), cfg.Encoding, cfg.TimeFormat, cfg.Skip, zapcore.NewMultiWriteSyncer(writers...)) | |||
zap.RedirectStdLog(lg) | |||
if cfg.AppName != "" { | |||
lg = lg.With(zap.String("app", cfg.AppName)) // 加上应用名称 | |||
} | |||
return &LogX{logger: lg, atomLevel: lv} | |||
} | |||
// create a new zaplog logger | |||
func newZapLogger(level, stacktrace zapcore.Level, encoding, timeType string, skip int, output zapcore.WriteSyncer) (*zap.Logger, *zap.AtomicLevel) { | |||
encCfg := zapcore.EncoderConfig{ | |||
TimeKey: "T", | |||
LevelKey: "L", | |||
NameKey: "N", | |||
CallerKey: "C", | |||
MessageKey: "M", | |||
StacktraceKey: "S", | |||
LineEnding: zapcore.DefaultLineEnding, | |||
EncodeCaller: zapcore.ShortCallerEncoder, | |||
EncodeDuration: zapcore.NanosDurationEncoder, | |||
EncodeLevel: zapcore.LowercaseLevelEncoder, | |||
} | |||
setTimeFormat(timeType, &encCfg) // set time type | |||
atmLvl := zap.NewAtomicLevel() // set level | |||
atmLvl.SetLevel(level) | |||
encoder := zapcore.NewJSONEncoder(encCfg) // 确定encoder格式 | |||
if encoding == "console" { | |||
encoder = zapcore.NewConsoleEncoder(encCfg) | |||
} | |||
return zap.New(zapcore.NewCore(encoder, output, atmLvl), zap.AddCaller(), zap.AddStacktrace(stacktrace), zap.AddCallerSkip(skip)), &atmLvl | |||
} | |||
// set log level | |||
func setLogLevel(lvl string) zapcore.Level { | |||
switch strings.ToLower(lvl) { | |||
case "panic": | |||
return zapcore.PanicLevel | |||
case "fatal": | |||
return zapcore.FatalLevel | |||
case "error": | |||
return zapcore.ErrorLevel | |||
case "warn", "warning": | |||
return zapcore.WarnLevel | |||
case "info": | |||
return zapcore.InfoLevel | |||
default: | |||
return zapcore.DebugLevel | |||
} | |||
} | |||
// set time format | |||
func setTimeFormat(timeType string, z *zapcore.EncoderConfig) { | |||
switch strings.ToLower(timeType) { | |||
case "iso": // iso8601 standard | |||
z.EncodeTime = zapcore.ISO8601TimeEncoder | |||
case "sec": // only for unix second, without millisecond | |||
z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { | |||
enc.AppendInt64(t.Unix()) | |||
} | |||
case "second": // unix second, with millisecond | |||
z.EncodeTime = zapcore.EpochTimeEncoder | |||
case "milli", "millisecond": // millisecond | |||
z.EncodeTime = zapcore.EpochMillisTimeEncoder | |||
case "nano", "nanosecond": // nanosecond | |||
z.EncodeTime = zapcore.EpochNanosTimeEncoder | |||
default: // standard format | |||
z.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { | |||
enc.AppendString(t.Format("2006-01-02 15:04:05.000")) | |||
} | |||
} | |||
} | |||
func GetLevel() string { | |||
switch l.atomLevel.Level() { | |||
case zapcore.PanicLevel: | |||
return "panic" | |||
case zapcore.FatalLevel: | |||
return "fatal" | |||
case zapcore.ErrorLevel: | |||
return "error" | |||
case zapcore.WarnLevel: | |||
return "warn" | |||
case zapcore.InfoLevel: | |||
return "info" | |||
default: | |||
return "debug" | |||
} | |||
} | |||
func SetLevel(lvl string) { | |||
l.atomLevel.SetLevel(setLogLevel(lvl)) | |||
} | |||
// temporary add call skip | |||
func AddCallerSkip(skip int) *LogX { | |||
l.logger.WithOptions(zap.AddCallerSkip(skip)) | |||
return l | |||
} | |||
// permanent add call skip | |||
func AddDepth(skip int) *LogX { | |||
l.logger = l.logger.WithOptions(zap.AddCallerSkip(skip)) | |||
return l | |||
} | |||
// permanent add options | |||
func AddOptions(opts ...zap.Option) *LogX { | |||
l.logger = l.logger.WithOptions(opts...) | |||
return l | |||
} | |||
func AddField(k string, v interface{}) { | |||
l.logger.With(zap.Any(k, v)) | |||
} | |||
func AddFields(fields map[string]interface{}) *LogX { | |||
for k, v := range fields { | |||
l.logger.With(zap.Any(k, v)) | |||
} | |||
return l | |||
} | |||
// Normal log | |||
func Debug(e interface{}, args ...interface{}) error { | |||
return l.Debug(e, args...) | |||
} | |||
func Info(e interface{}, args ...interface{}) error { | |||
return l.Info(e, args...) | |||
} | |||
func Warn(e interface{}, args ...interface{}) error { | |||
return l.Warn(e, args...) | |||
} | |||
func Error(e interface{}, args ...interface{}) error { | |||
return l.Error(e, args...) | |||
} | |||
func Panic(e interface{}, args ...interface{}) error { | |||
return l.Panic(e, args...) | |||
} | |||
func Fatal(e interface{}, args ...interface{}) error { | |||
return l.Fatal(e, args...) | |||
} | |||
// Format logs | |||
func Debugf(format string, args ...interface{}) error { | |||
return l.Debugf(format, args...) | |||
} | |||
func Infof(format string, args ...interface{}) error { | |||
return l.Infof(format, args...) | |||
} | |||
func Warnf(format string, args ...interface{}) error { | |||
return l.Warnf(format, args...) | |||
} | |||
func Errorf(format string, args ...interface{}) error { | |||
return l.Errorf(format, args...) | |||
} | |||
func Panicf(format string, args ...interface{}) error { | |||
return l.Panicf(format, args...) | |||
} | |||
func Fatalf(format string, args ...interface{}) error { | |||
return l.Fatalf(format, args...) | |||
} | |||
func formatFieldMap(m FieldMap) []Field { | |||
var res []Field | |||
for k, v := range m { | |||
res = append(res, zap.Any(k, v)) | |||
} | |||
return res | |||
} |
@@ -0,0 +1,105 @@ | |||
package zhios_pay_logx | |||
import ( | |||
"bytes" | |||
"io" | |||
"os" | |||
"path/filepath" | |||
"time" | |||
"gopkg.in/natefinch/lumberjack.v2" | |||
) | |||
// output interface | |||
type WriteSyncer interface { | |||
io.Writer | |||
Sync() error | |||
} | |||
// split writer | |||
func NewRollingFile(dir, filename string, maxSize, MaxAge int) WriteSyncer { | |||
s, err := os.Stat(dir) | |||
if err != nil || !s.IsDir() { | |||
os.RemoveAll(dir) | |||
if err := os.MkdirAll(dir, 0766); err != nil { | |||
panic(err) | |||
} | |||
} | |||
return newLumberjackWriteSyncer(&lumberjack.Logger{ | |||
Filename: filepath.Join(dir, filename), | |||
MaxSize: maxSize, // megabytes, MB | |||
MaxAge: MaxAge, // days | |||
LocalTime: true, | |||
Compress: false, | |||
}) | |||
} | |||
type lumberjackWriteSyncer struct { | |||
*lumberjack.Logger | |||
buf *bytes.Buffer | |||
logChan chan []byte | |||
closeChan chan interface{} | |||
maxSize int | |||
} | |||
func newLumberjackWriteSyncer(l *lumberjack.Logger) *lumberjackWriteSyncer { | |||
ws := &lumberjackWriteSyncer{ | |||
Logger: l, | |||
buf: bytes.NewBuffer([]byte{}), | |||
logChan: make(chan []byte, 5000), | |||
closeChan: make(chan interface{}), | |||
maxSize: 1024, | |||
} | |||
go ws.run() | |||
return ws | |||
} | |||
func (l *lumberjackWriteSyncer) run() { | |||
ticker := time.NewTicker(1 * time.Second) | |||
for { | |||
select { | |||
case <-ticker.C: | |||
if l.buf.Len() > 0 { | |||
l.sync() | |||
} | |||
case bs := <-l.logChan: | |||
_, err := l.buf.Write(bs) | |||
if err != nil { | |||
continue | |||
} | |||
if l.buf.Len() > l.maxSize { | |||
l.sync() | |||
} | |||
case <-l.closeChan: | |||
l.sync() | |||
return | |||
} | |||
} | |||
} | |||
func (l *lumberjackWriteSyncer) Stop() { | |||
close(l.closeChan) | |||
} | |||
func (l *lumberjackWriteSyncer) Write(bs []byte) (int, error) { | |||
b := make([]byte, len(bs)) | |||
for i, c := range bs { | |||
b[i] = c | |||
} | |||
l.logChan <- b | |||
return 0, nil | |||
} | |||
func (l *lumberjackWriteSyncer) Sync() error { | |||
return nil | |||
} | |||
func (l *lumberjackWriteSyncer) sync() error { | |||
defer l.buf.Reset() | |||
_, err := l.Logger.Write(l.buf.Bytes()) | |||
if err != nil { | |||
return err | |||
} | |||
return nil | |||
} |
@@ -0,0 +1,192 @@ | |||
package zhios_pay_logx | |||
import ( | |||
"errors" | |||
"fmt" | |||
"strconv" | |||
"go.uber.org/zap" | |||
) | |||
type LogX struct { | |||
logger *zap.Logger | |||
atomLevel *zap.AtomicLevel | |||
} | |||
type Field = zap.Field | |||
type FieldMap map[string]interface{} | |||
// 判断其他类型--start | |||
func getFields(msg string, format bool, args ...interface{}) (string, []Field) { | |||
var str []interface{} | |||
var fields []zap.Field | |||
if len(args) > 0 { | |||
for _, v := range args { | |||
if f, ok := v.(Field); ok { | |||
fields = append(fields, f) | |||
} else if f, ok := v.(FieldMap); ok { | |||
fields = append(fields, formatFieldMap(f)...) | |||
} else { | |||
str = append(str, AnyToString(v)) | |||
} | |||
} | |||
if format { | |||
return fmt.Sprintf(msg, str...), fields | |||
} | |||
str = append([]interface{}{msg}, str...) | |||
return fmt.Sprintln(str...), fields | |||
} | |||
return msg, []Field{} | |||
} | |||
func (l *LogX) Debug(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Debug(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) Info(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Info(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) Warn(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Warn(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) Error(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Error(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) DPanic(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.DPanic(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) Panic(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Panic(msg, field...) | |||
} | |||
return e | |||
} | |||
func (l *LogX) Fatal(s interface{}, args ...interface{}) error { | |||
es, e := checkErr(s) | |||
if es != "" { | |||
msg, field := getFields(es, false, args...) | |||
l.logger.Fatal(msg, field...) | |||
} | |||
return e | |||
} | |||
func checkErr(s interface{}) (string, error) { | |||
switch e := s.(type) { | |||
case error: | |||
return e.Error(), e | |||
case string: | |||
return e, errors.New(e) | |||
case []byte: | |||
return string(e), nil | |||
default: | |||
return "", nil | |||
} | |||
} | |||
func (l *LogX) LogError(err error) error { | |||
return l.Error(err.Error()) | |||
} | |||
func (l *LogX) Debugf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Debug(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) Infof(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Info(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) Warnf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Warn(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) Errorf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Error(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) DPanicf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.DPanic(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) Panicf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Panic(s, f...) | |||
return errors.New(s) | |||
} | |||
func (l *LogX) Fatalf(msg string, args ...interface{}) error { | |||
s, f := getFields(msg, true, args...) | |||
l.logger.Fatal(s, f...) | |||
return errors.New(s) | |||
} | |||
func AnyToString(raw interface{}) string { | |||
switch i := raw.(type) { | |||
case []byte: | |||
return string(i) | |||
case int: | |||
return strconv.FormatInt(int64(i), 10) | |||
case int64: | |||
return strconv.FormatInt(i, 10) | |||
case float32: | |||
return strconv.FormatFloat(float64(i), 'f', 2, 64) | |||
case float64: | |||
return strconv.FormatFloat(i, 'f', 2, 64) | |||
case uint: | |||
return strconv.FormatInt(int64(i), 10) | |||
case uint8: | |||
return strconv.FormatInt(int64(i), 10) | |||
case uint16: | |||
return strconv.FormatInt(int64(i), 10) | |||
case uint32: | |||
return strconv.FormatInt(int64(i), 10) | |||
case uint64: | |||
return strconv.FormatInt(int64(i), 10) | |||
case int8: | |||
return strconv.FormatInt(int64(i), 10) | |||
case int16: | |||
return strconv.FormatInt(int64(i), 10) | |||
case int32: | |||
return strconv.FormatInt(int64(i), 10) | |||
case string: | |||
return i | |||
case error: | |||
return i.Error() | |||
} | |||
return fmt.Sprintf("%#v", raw) | |||
} |