diff --git a/db/db.go b/db/db.go index 317445a..cc3e6cc 100644 --- a/db/db.go +++ b/db/db.go @@ -32,7 +32,7 @@ func InitDB(c *md.DBCfg) error { if c.ShowLog { //根据配置文件设置日志 Db.ShowSQL(true) //设置是否打印sql Db.Logger().SetLevel(0) //设置日志等级 - //修改日志文件存放路径文件名是%s.log + //修改日志文件存放路径文件名是%s.logs path := fmt.Sprintf(c.Path, c.Name) f, err = os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0777) if err != nil { diff --git a/go.mod b/go.mod index 7362351..1b2bbe0 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module code.fnuoos.com/go_rely_warehouse/zyos_go_pay.git go 1.15 require ( - github.com/gin-gonic/gin v1.8.0 // indirect + 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 // indirect - gopkg.in/go-playground/assert.v1 v1.2.1 // indirect - gopkg.in/go-playground/validator.v9 v9.29.1 // indirect + 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 ) diff --git a/lib/local_wechat/api.go b/lib/local_wechat/api.go new file mode 100644 index 0000000..b89835a --- /dev/null +++ b/lib/local_wechat/api.go @@ -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 +} diff --git a/md/platform.go b/md/platform.go index d41935d..b479ab5 100644 --- a/md/platform.go +++ b/md/platform.go @@ -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", +} diff --git a/md/wxpay.go b/md/wxpay.go new file mode 100644 index 0000000..7ce6595 --- /dev/null +++ b/md/wxpay.go @@ -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"` +} diff --git a/pay/init.go b/pay/init.go index 223cc47..ae8d935 100644 --- a/pay/init.go +++ b/pay/init.go @@ -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.log" + cfg.Path = "./tmp/%s.logs" if err := db.InitDB(&cfg); err != nil { return err } diff --git a/pay/logs/1.txt b/pay/logs/1.txt new file mode 100644 index 0000000..e69de29 diff --git a/pay/logs/zyos_website.logs b/pay/logs/zyos_website.logs new file mode 100644 index 0000000..504dd55 --- /dev/null +++ b/pay/logs/zyos_website.logs @@ -0,0 +1,2 @@ +[xorm] [info] 2022/05/31 10:40:31.829104 [SQL] SELECT `id`, `channel_id`, `identifier`, `master_id`, `master_nickname`, `app_id` FROM `pay_channel_master` WHERE (app_id = ?) AND (identifier = ?) LIMIT 1 [35618318 mall] - 28.9565ms +[xorm] [info] 2022/05/31 10:41:41.231871 [SQL] SELECT `id`, `channel_id`, `identifier`, `master_id`, `master_nickname`, `app_id` FROM `pay_channel_master` WHERE (app_id = ?) AND (identifier = ?) LIMIT 1 [35618318 mall] - 24.9118ms diff --git a/pay/pay_by_own.go b/pay/pay_by_own.go index 5ef648d..d909cb8 100644 --- a/pay/pay_by_own.go +++ b/pay/pay_by_own.go @@ -2,15 +2,18 @@ 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" ) //自有支付 支付宝 func Alipay(args map[string]string) (string, error) { - var paySet = &md.PayData{ PayAppCertSn: args["pay_app_cert_sn"], PayAlipayRootCertSn: args["pay_alipay_root_cert_sn"], @@ -41,3 +44,166 @@ func Alipay(args map[string]string) (string, error) { } return zhios_pay_utils.AnyToString(param), nil } +func AlipayApp(args map[string]string) (string, error) { + var paySet = &md.PayData{ + PayAppCertSn: args["pay_app_cert_sn"], + PayAlipayRootCertSn: args["pay_alipay_root_cert_sn"], + PayAlipayrsaPublicKey: args["pay_alipayrsa_public_key"], + PayAliUseType: args["pay_ali_use_type"], + WxAppletFilepathUrl: args["wx_applet_filepath_url"], + } + if args["private_key"] == "" || args["app_id"] == "" { + return "", errors.New("请在后台正确配置支付宝") + } + param, err := local_alipay.TradeAppPay(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], paySet) + if err != nil { + fmt.Println("支付宝错误日志") + fmt.Println(param) + fmt.Println(err) + return "", errors.New("支付宝订单创建失败") + } + return zhios_pay_utils.AnyToString(param), nil +} + +func AlipayWap(args map[string]string) (string, error) { + + var paySet = &md.PayData{ + PayAppCertSn: args["pay_app_cert_sn"], + PayAlipayRootCertSn: args["pay_alipay_root_cert_sn"], + PayAlipayrsaPublicKey: args["pay_alipayrsa_public_key"], + PayAliUseType: args["pay_ali_use_type"], + WxAppletFilepathUrl: args["wx_applet_filepath_url"], + } + if args["private_key"] == "" || args["app_id"] == "" { + return "", errors.New("请在后台正确配置支付宝") + } + param, err := local_alipay.TradeWapPay(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], args["page_url"], paySet) + if err != nil { + fmt.Println("支付宝错误日志") + fmt.Println(param) + fmt.Println(err) + return "", errors.New("支付宝订单创建失败") + } + return zhios_pay_utils.AnyToString(param), nil +} + +func AlipayApplet(args map[string]string) (string, error) { + var paySet = &md.PayData{ + PayAppCertSn: args["pay_app_cert_sn"], + PayAlipayRootCertSn: args["pay_alipay_root_cert_sn"], + PayAlipayrsaPublicKey: args["pay_alipayrsa_public_key"], + PayAliUseType: args["pay_ali_use_type"], + WxAppletFilepathUrl: args["wx_applet_filepath_url"], + } + if args["private_key"] == "" || args["app_id"] == "" { + return "", errors.New("请在后台正确配置支付宝") + } + param, err := local_alipay.TradeCreate(args["app_id"], args["private_key"], args["subject"], args["ord_id"], args["amount"], args["notify_url"], args["rsa"], args["pkcs"], paySet) + if err != nil { + fmt.Println("支付宝错误日志") + fmt.Println(param) + fmt.Println(err) + return "", errors.New("支付宝订单创建失败") + } + 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 +} diff --git a/pay/tmp/1.txt b/pay/tmp/1.txt new file mode 100644 index 0000000..e69de29 diff --git a/utils/file.go b/utils/file.go index 0d08244..a1dafed 100644 --- a/utils/file.go +++ b/utils/file.go @@ -13,7 +13,7 @@ func FileExt(fname string) string { } func FilePutContents(fileName string, content string) { - fd, _ := os.OpenFile("./tmp/"+fileName+".log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) + fd, _ := os.OpenFile("./tmp/"+fileName+".logs", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644) fd_time := time.Now().Format("2006-01-02 15:04:05") fd_content := strings.Join([]string{"[", fd_time, "] ", content, "\n"}, "") buf := []byte(fd_content) diff --git a/utils/logx/log.go b/utils/logx/log.go new file mode 100644 index 0000000..ebfb498 --- /dev/null +++ b/utils/logx/log.go @@ -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 +} diff --git a/utils/logx/output.go b/utils/logx/output.go new file mode 100644 index 0000000..2d6aa6a --- /dev/null +++ b/utils/logx/output.go @@ -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 +} diff --git a/utils/logx/sugar.go b/utils/logx/sugar.go new file mode 100644 index 0000000..736a1be --- /dev/null +++ b/utils/logx/sugar.go @@ -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) +}