package service import ( "errors" "fmt" "io" "sync" "time" "gopkg.in/redis.v2" "xiaoniaokuaiyan.com/xiaoniao/config" "xiaoniaokuaiyan.com/xiaoniao/pay/wx" "xiaoniaokuaiyan.com/xiaoniao/util" ) type WeixinService struct { accessToken *wx.AccessToken jsTicket *wx.AccessToken wx.Weixin wx.JsSdk WeixinMp wx.Weixin } const REDIS_WX_ACCESSTOKEN_KEY = "wx_access_token" const REDIS_WX_JSAPI_TICKET_KEY = "wx_jsapi_ticket" func (wxSrv *WeixinService) GetWXAccessToken(accountType string) (interface{}, error) { var ( tokenStr string err error //env string = config.IniConf.Section("server").Key("env").Value() suffix string weixin wx.Weixin = wxSrv.Weixin ) //if env == "production" { if accountType == "mp" { suffix = "_mp" weixin = wxSrv.WeixinMp } tokenStr, err = getWxRedisValue(REDIS_WX_ACCESSTOKEN_KEY + suffix) //} else { // tokenStr, err = util.GetRedisValue(REDIS_WX_ACCESSTOKEN_KEY) //} if err != nil || tokenStr == "" { tokenRes, err := weixin.GetAccessToken() if err != nil { return nil, err } //if env == "production" { setWxRedisValue(REDIS_WX_ACCESSTOKEN_KEY+suffix, tokenRes.AccessToken, int64(time.Second)*(tokenRes.ExpiresIn-5)) //} else { // util.SetRedisValue(REDIS_WX_ACCESSTOKEN_KEY, tokenRes.AccessToken, int64(time.Second)*(tokenRes.ExpiresIn-5)) //} return tokenRes.AccessToken, nil } return tokenStr, nil } func (wxSrv *WeixinService) GetJSAPITicket(accountType string) (interface{}, error) { var ( ticketStr string err error // env string = config.IniConf.Section("server").Key("env").Value() suffix string ) if accountType == "mp" { suffix = "_mp" } //if env == "production" { ticketStr, err = getWxRedisValue(REDIS_WX_JSAPI_TICKET_KEY) //} else { // ticketStr, err = util.GetRedisValue(REDIS_WX_JSAPI_TICKET_KEY) //} if err != nil || ticketStr == "" { token, err := wxSrv.GetWXAccessToken(accountType) if err != nil { return nil, err } ticketRes, err := wxSrv.JsSdk.GetTicket(token.(string)) if err != nil { return nil, err } //if env == "production" { setWxRedisValue(REDIS_WX_JSAPI_TICKET_KEY+suffix, ticketRes.Ticket, int64(time.Second)*(ticketRes.ExpiresIn-5)) //} else { // util.SetRedisValue(REDIS_WX_JSAPI_TICKET_KEY, ticketRes.Ticket, int64(time.Second)*(ticketRes.ExpiresIn-5)) //} return ticketRes.Ticket, nil } return ticketStr, nil } func (wxSrv *WeixinService) JsSdkSign(signUrl string, accountType string) (interface{}, error) { accessToken, err := wxSrv.GetWXAccessToken(accountType) if err != nil { return nil, err } ticket, err := wxSrv.GetJSAPITicket(accountType) if err != nil { return nil, err } spkg, err := wxSrv.JsSdk.ComputeSignature(accessToken.(string), ticket.(string), signUrl) if err != nil { return nil, err } delete(spkg, "access_token") delete(spkg, "ticket") if accountType == "mp" { spkg["appid"] = config.IniConf.Section("weixin").Key("wx.mp.appid").Value() } else { spkg["appid"] = config.IniConf.Section("weixin").Key("wx.appid").Value() } return spkg, nil } func (wxSrv *WeixinService) Send(msg map[string]interface{}, accountType string) (interface{}, error) { token, err := wxSrv.GetWXAccessToken(accountType) if err != nil { return nil, err } err = wxSrv.Weixin.Send(msg, token.(string)) if err != nil { return false, err } return true, nil } func (wxSrv *WeixinService) SendTpl(msg string, accountType string) (interface{}, error) { token, err := wxSrv.GetWXAccessToken(accountType) if err != nil { return nil, err } err = wxSrv.Weixin.SendTpl(msg, token.(string), accountType) if err != nil { return false, err } return true, nil } func (wxSrv *WeixinService) ScanPay(extra map[string]string) (interface{}, error) { if _, ok := extra["ip"]; !ok { return nil, errors.New("need request ip") } if _, ok := extra["trade_type"]; !ok { return nil, errors.New("need param trade_type of weixin pay") } if _, ok := extra["device_info"]; !ok && extra["trade_type"] == "JSAPI" { extra["device_info"] = "WEB" } if _, ok := extra["openid"]; !ok && extra["trade_type"] == "JSAPI" { return nil, errors.New("miss param openid") } if _, ok := extra["usefor"]; !ok || extra["usefor"] == "" { return nil, errors.New("param usefor must not equal empty") } var remark interface{} = nil if _, ok := extra["remark"]; ok { remark = extra["remark"] } payNo := GenerateOrderPayNO("SP") wxJsSdk := wx.NewJsSdk() var ( appid string = wx.DefaultConfig["appid"] secret = wx.DefaultConfig["secret"] ) if extra["wx_type"] == "mp" { appid = config.IniConf.Section("weixin").Key("wx.mp.appid").Value() secret = config.IniConf.Section("weixin").Key("wx.mp.secret").Value() } weixin := wx.NewWeixin(appid, secret) nonceStr := wxJsSdk.GenerateNoncestr(16) reqItem := map[string]string{ "appid": appid, "mch_id": wx.DefaultConfig["mch_id"], "nonce_str": nonceStr, "body": "小鸟快验订单-" + payNo, "notify_url": config.IniConf.Section("weixin").Key("wx.scanpaycb_notify_url").Value(), "out_trade_no": payNo, "spbill_create_ip": extra["ip"], "total_fee": extra["total_fee"], "trade_type": extra["trade_type"], } if extra["trade_type"] == "JSAPI" { reqItem["openid"] = extra["openid"] } if extra["device_info"] != "" { reqItem["device_info"] = extra["device_info"] } sign := wxJsSdk.ComputePaySignature(reqItem, wx.DefaultConfig["apikey"]) reqItem["sign"] = sign rel, err := weixin.PrepareOrder(reqItem) if err != nil { return nil, err } if rel.ReturnCode == "FAIL" { return nil, errors.New(rel.ReturnMsg) } db := util.GetWriteSqlDB() sqlResult := db.MustExec("insert into t_custom_wxscan_pay(id, openid, payment, usefor, remark) values(?,?,?,?,?)", payNo, extra["openid"], extra["total_fee"], extra["usefor"], remark) if re, _ := sqlResult.RowsAffected(); re <= 0 { return nil, errors.New("failed to save data") } resultItem := map[string]string{ "appId": appid, "timeStamp": fmt.Sprintf("%d", time.Now().Unix()), "nonceStr": wxJsSdk.GenerateNoncestr(16), "package": "prepay_id=" + rel.PrepayId, "signType": "MD5", "mweb_url": rel.MWebUrl, "code_url": rel.CodeUrl, } resultItem["sign"] = wxJsSdk.ComputePaySignature(resultItem, config.IniConf.Section("weixin").Key("wx.apikey").Value()) resultItem["pay_id"] = payNo return resultItem, nil } func (wxSrv *WeixinService) GetPayReason() (interface{}, error) { db := util.GetSqlDB() var reasonList = []string{} err := db.Select(&reasonList, "select usefor from t_custom_pay_usefor where status = 0;") if err != nil { return nil, err } return reasonList, nil } /*func (wxSrv *WeixinService) GetWXAccessToken() (interface{}, error) { //todo cache to redis if wxSrv.accessToken != nil && !wxSrv.accessToken.IsExpired() { return wxSrv.accessToken.TokenStr, nil } tokenRes, err := wxSrv.Weixin.GetAccessToken() if err != nil { return nil, err } wxSrv.accessToken = &wx.AccessToken{ TokenStr: tokenRes.AccessToken, ExpiresAt: time.Now().Unix() + tokenRes.ExpiresIn, } return wxSrv.accessToken.TokenStr, nil } func (wxSrv *WeixinService) GetJSAPITicket() (interface{}, error) { if wxSrv.jsTicket != nil && !wxSrv.jsTicket.IsExpired() { return wxSrv.jsTicket.TokenStr, nil } token, err := wxSrv.GetWXAccessToken() if err != nil { return nil, err } ticketRes, err := wxSrv.JsSdk.GetTicket(token.(string)) if err != nil { return nil, err } wxSrv.jsTicket = &wx.AccessToken{ TokenStr: ticketRes.Ticket, ExpiresAt: time.Now().Unix() + ticketRes.ExpiresIn, } return wxSrv.jsTicket.TokenStr, nil } */ var wxredisInstance *redis.Client var wxredisOnce sync.Once func getWxRedis() *redis.Client { wxredisOnce.Do(func() { var redisOptions *redis.Options = &redis.Options{ Addr: "101.201.196.166:6379", } wxredisInstance = redis.NewTCPClient(redisOptions) }) cmdStatus := wxredisInstance.Auth("xn9659$") if cmdStatus.Err() == io.EOF { wxredisInstance.Ping() wxredisInstance.Auth("xn9659$") } return wxredisInstance } func getWxRedisValue(key string) (string, error) { client := getWxRedis() client.Select(12) strCmd := client.Get(key) if err := strCmd.Err(); err != nil { return "", err } return strCmd.Val(), nil } func setWxRedisValue(key, val string, period int64) (bool, error) { client := getWxRedis() client.Select(12) statusCmd := client.Set(key, val) if err := statusCmd.Err(); err != nil { return false, err } if int64(period) > 0 { client.Expire(key, time.Duration(period)) } return true, nil }