package service import ( "encoding/json" "errors" "fmt" "io" "net/http" "strconv" "strings" "time" "yuleduiPay/common" "yuleduiPay/repo" "yuleduiPay/service/po" "yuleduiPay/service/vo" "context" "math/rand" "github.com/gogf/gf/os/gctx" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/guid" ) type Pay struct { payOrderRepo repo.PayOrder shopRepo repo.Shop userRepo repo.User } // 微信和支付宝小程序,扫码支付 func (t *Pay) GetReduction(ctx context.Context, req *vo.GetReductionReq) (resp *vo.GetReductionResp, err error) { resp = &vo.GetReductionResp{} shopAdmin, err := t.shopRepo.GetShopAdminByShopId(req.ShopId) if err != nil { return } rand_reduction := shopAdmin.RandReduction r := rand.New(rand.NewSource(time.Now().UnixNano())) //以当前时间纳秒为种子 fmt.Println(rand_reduction) resp.Reduction = r.Int63n(rand_reduction + 1) return } // 微信和支付宝小程序,扫码支付 func (t *Pay) PayQrCode(ctx context.Context, req *vo.PayQrCodeReq) (resp *vo.PayQrCodeResp, err error) { g.Log().Line().Printf(nil, "%+v", req) //计算金豆和随机立减,算出最终打给银盛的钱 payOrder, err := t.getAmount(req.Price, req.Reduction, req.Bean) if err != nil { return } //获取商铺信息 shop, err := t.shopRepo.GetShopById(req.ShopId) if err != nil { return } //创建订单记录 payOrder.OrderId = guid.S() payOrder.OpenId = req.OpenId payOrder.Mobile = req.Mobile payOrder.ShopId = req.ShopId payOrder.Channel = req.Channel payOrder.Created = gtime.Now() payOrder.Updated = payOrder.Created err = t.payOrderRepo.CreatePayOrder(payOrder) if err != nil { return } yseRespBytes, err := t.ysePayRequestJson(shop, payOrder) //访问java项目,获取银盛返回的结果 if err != nil { return } fmt.Println(string(yseRespBytes)) g.Log().Line().Printf(nil, string(yseRespBytes)) yseResp, err := t.yseParseResp(payOrder.Channel, yseRespBytes) //解析报文返回的报文 if err != nil { return } fmt.Println(yseResp) if yseResp.OutTradeNo != payOrder.OrderId { errStr := fmt.Sprintf("银盛返回订单号错误,银盛订单号:%s,余乐兑订单号:%s", yseResp.OutTradeNo, payOrder.OpenId) err = errors.New(errStr) return } //更新支付订单表字段 updates := g.Map{"ysePayStatus": yseResp.TradeStatus, "updated": time.Now()} err = t.payOrderRepo.UpdatePayOrderByOrderId(updates, payOrder.OpenId) if err != nil { return } resp = &vo.PayQrCodeResp{} resp.OrderId = payOrder.OrderId resp.Bean = payOrder.Bean resp.Reduction = payOrder.RandReduce resp.JsapiPayInfo = yseResp.JsapiPayInfo return } func (t *Pay) getAmount(orderPrice string, reduction int, bean float64) (payOrder *po.PayOrder, err error) { payOrder = &po.PayOrder{} /* shopAdmin, err := t.shopRepo.GetShopAdminByShopId(shopId) if err != nil { return payOrder, err } */ price, err := strconv.ParseFloat(orderPrice, 64) if err != nil { return } payOrder.Price = price payOrder.Bean = bean * 0.15 price = price - payOrder.Bean //去除金豆后的价格 payOrder.RandReduce = float64(reduction) * 0.01 payOrder.Amount = price - payOrder.RandReduce //去除随机立减和金豆后的价格 if payOrder.Amount < 0.01 { err = errors.New("支付金额小于1分,失败") return } return } // 微信用户登录 func (t *Pay) Login(ctx context.Context, req *vo.WxLoginReq) (res *vo.WxLoginRes, err error) { fmt.Println("11-----", req) res, err = t.wxAuthorize(req.JsCode) if err != nil { return } //根据openId查询手机号 userItem, _ := t.userRepo.GetUserByWxId(res.OpenId) res.Mobile = userItem.Mobile res.Bean = userItem.Bean return } // 用户注册 func (t *Pay) WxRegister(ctx context.Context, req *vo.WxMobileReq) (res *vo.WxMobileRes, err error) { fmt.Println("WxRegister----", req) value, err := g.Redis().Get(ctx, "WxAccessToken") if err != nil { return } fmt.Println("1111111", err) accessToken := value.String() fmt.Println("-------", accessToken) if accessToken == "" { //从微信小程序获取token,并更新redis fmt.Println("2222222") accessToken, err = t.getWeiXinAccessToken() if err != nil { fmt.Println("444--", err) return } } fmt.Println("----> ", accessToken) //获取手机号 mobile, err := t.getUserPhoneNumber(req.Code, accessToken) if err != nil { return } res = &vo.WxMobileRes{} res.Mobile = mobile //查询此手机号在数据库中是否存在 ok, err := t.userRepo.MobileExist(mobile) if err != nil { return } if ok { //如果存在,更新openId updates := g.Map{"wx_id": req.OpenId, "update_time": gtime.Now()} err = t.userRepo.UpdatesUser(updates, mobile) return } //不存在的,创建 user := po.User{} user.UserId = guid.S() user.WxId = req.OpenId user.Mobile = mobile user.CreateTime = gtime.Now() user.UpdateTime = user.CreateTime err = t.userRepo.CreateUser(&user) if err != nil { return } res = &vo.WxMobileRes{} res.Mobile = mobile return } func (t *Pay) yseParseResp(channel int, respBytes []byte) (*vo.BusPayResp, error) { yepayOnlineWeixinPayResponse := &vo.BusPayResp{} if channel == 1 { //微信 ysePayResp := &vo.WxPayResp{} fmt.Println("999-------> ", string(respBytes)) err := json.Unmarshal(respBytes, ysePayResp) if err != nil { return nil, err } fmt.Println("银盛返回的应答:", ysePayResp) yepayOnlineWeixinPayResponse = &ysePayResp.YsepayOnlineWeixinPayResponse } else { //支付宝 ysePayResp := &vo.ZfbPayResp{} err := json.Unmarshal(respBytes, ysePayResp) if err != nil { return nil, err } fmt.Println("银盛返回的应答:", ysePayResp) yepayOnlineWeixinPayResponse = &ysePayResp.YsepayOnlineAlijsapiPayResponse } if yepayOnlineWeixinPayResponse.Code != "10000" { return nil, errors.New("银盛应答返回错误:" + yepayOnlineWeixinPayResponse.Msg) } return yepayOnlineWeixinPayResponse, nil } func (t *Pay) ysePayRequestJson(shop *po.Shop, payOrder *po.PayOrder) ([]byte, error) { //业务请求参数 busReq := vo.PayBusReq{} busReq.OutTradeNo = payOrder.OrderId //订单编号 busReq.Shopdate = payOrder.Created.Format("20060102") //商户系统的交易发生日期格式 busReq.Subject = "余乐兑小程序订单" //订单备注 busReq.TotalAmount = fmt.Sprintf("%.2f", payOrder.Amount) //该笔订单的资金总额,保留两位小数 busReq.Currency = "CNY" //默认人民币 busReq.SellerId = shop.ChildMerchNumber //银盛商户ID busReq.TimeoutExpress = "1h" //设置未付款交易的超时时间,一个小时 busReq.BusinessCode = g.Cfg().MustGet(nil, "ysepay.busCode").String() //业务代码 var bjson []byte var err error if payOrder.Channel == 1 { //微信渠道 wxBusReq := vo.PayWxBusReq{PayBusReq: busReq} wxBusReq.SubOpenid = payOrder.OpenId //微信用户ID wxBusReq.IsMinipg = "1" //微信小程序支付:1 wxBusReq.Appid = g.Cfg().MustGet(nil, "weixin.appId").String() //微信小程序APPID bjson, err = json.Marshal(wxBusReq) if err != nil { return nil, err } } else { //支付宝渠道 zfBusReq := vo.PayZfBusReq{PayBusReq: busReq} zfBusReq.BuyerId = payOrder.OpenId //支付宝用户ID bjson, err = json.Marshal(zfBusReq) if err != nil { return nil, err } } commonReq := vo.PayCommonReq{} if payOrder.Channel == 1 { //微信渠道 commonReq.Method = "ysepay.online.weixin.pay" } else { //支付宝渠道 commonReq.Method = "ysepay.online.alijsapi.pay" } commonReq.PartnerId = g.Cfg().MustGet(nil, "ysepay.CERTID").String() //在银盛支付开设的服务商商户号 commonReq.Timestamp = time.Now().Format("2006-01-02 15:04:05") //发送请求的时间 commonReq.Charset = "UTF-8" //商户网站使用的编码格式 commonReq.SignType = "SM" //报文签名算法 commonReq.NotifyUrl = g.Cfg().MustGet(nil, "ysepay.notifyUrl").String() //交易成功异步通知到商户的后台地址 commonReq.Version = "3.0" //接口版本 commonReq.BizContent = string(bjson) bjson, err = json.Marshal(&commonReq) if err != nil { return nil, err } url := g.Cfg().MustGet(nil, "ysepay.url").String() xxx := string(bjson) fmt.Println(xxx) //发送给java项目,让其访问银盛接口 bjson, err = common.HttpPost(url, "application/json", bjson) if err != nil { return nil, err } return bjson, err } // 微信授权获取openId func (t *Pay) wxAuthorize(jsCode string) (*vo.WxLoginRes, error) { var urlResp *http.Response var err error appid := g.Cfg().MustGet(nil, "weixin.appId").String() wxSecret := g.Cfg().MustGet(nil, "weixin.secret").String() wx_url := "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + wxSecret + "&js_code=" + jsCode + "&grant_type=authorization_code" fmt.Println("jsCode:", jsCode) for i := 0; i < 2; i++ { // 失败一次,再次调用 urlResp, err = http.Get(wx_url) if err == nil { // 成功的话,跳出 break } } if err != nil { return nil, err } body, err := io.ReadAll(urlResp.Body) if err != nil { return nil, err } urlResp.Body.Close() sessionResp := vo.WxLoginRes{} // 用户标志信息 err = json.Unmarshal(body, &sessionResp) if err != nil { return nil, err } if sessionResp.ErrCode != 0 { err = errors.New("errCode:" + strconv.Itoa(sessionResp.ErrCode) + " errMsg:" + sessionResp.ErrMsg) return nil, err } return &sessionResp, err } // 获取accessToken func (t *Pay) getWeiXinAccessToken() (string, error) { type WeiXinAccessResp struct { AccessToken string `json:"access_token"` // 获取到的凭证 ExpiresIn int `json:"expires_in"` // 凭证有效时间 单位:秒。目前是7200秒之内的值。 Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` } type WxSessionResp struct { OpenId string `json:"openid"` // 用户唯一标识 SessionKey string `json:"session_key"` // 会话密钥 UnionId string `json:"unionid"` // 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台帐号下会返回 ErrCode int `json:"errcode"` // 错误码 ErrMsg string `json:"errmsg"` // 错误信息 } resp := WeiXinAccessResp{} appid := g.Cfg().MustGet(nil, "weixin.appId").String() wxSecret := g.Cfg().MustGet(nil, "weixin.secret").String() url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + wxSecret fmt.Println("获取token:", url) var urlResp *http.Response var err error for i := 0; i < 2; i++ { // 失败一次,再次调用 urlResp, err = http.Get(url) if err == nil { // 成功的话,跳出 break } } if err != nil { return "", err } body, err := io.ReadAll(urlResp.Body) if err != nil { return "", err } urlResp.Body.Close() fmt.Println(string(body)) sessionResp := WxSessionResp{} err = json.Unmarshal(body, &resp) if err != nil { return "", err } if resp.Errcode != 0 { err = errors.New(sessionResp.ErrMsg) return "", err } // AccessToken 存入Redis中 live := resp.ExpiresIn - 60*10 // 提前10分钟过期 ctx := gctx.New() err = g.Redis().SetEX(ctx, "WxAccessToken", resp.AccessToken, int64(live)) if err != nil { return "", err } return resp.AccessToken, nil } // 获取手机号 func (s *Pay) getUserPhoneNumber(code, accessToken string) (string, error) { type WeiXinWatermark struct { Appid string `json:"appid"` // 小程序appid Timestamp int `json:"timestamp"` // 用户获取手机号操作的时间戳 } type WeixinPhoneInfo struct { PhoneNumber string `json:"phoneNumber"` // 用户绑定的手机号(国外手机号会有区号) PurePhoneNumber string `json:"purePhoneNumber"` // 没有区号的手机号 CountryCode string `json:"countryCode"` // 区号 Watermark WeiXinWatermark `json:"watermark"` // 数据水印 } type WeiXinMobileResp struct { Errcode int `json:"errcode"` Errmsg string `json:"errmsg"` PhoneInfo WeixinPhoneInfo `json:"phone_info"` // 用户手机号信息 } type WeiXinMobileReq struct { Code string `json:"code"` // 前端传的Code } req := WeiXinMobileReq{Code: code} url := "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken fmt.Println("Code:", req.Code) reqParam, err := json.Marshal(&req) if err != nil { return "", err } reqBody := strings.NewReader(string(reqParam)) httpReq, err := http.NewRequest("POST", url, reqBody) if err != nil { return "", err } httpReq.Header.Add("Content-Type", "application/json") var urlResp *http.Response for i := 0; i < 2; i++ { // 失败一次,再次调用 urlResp, err = http.DefaultClient.Do(httpReq) if err == nil { // 成功的话,跳出 break } } if err != nil { return "", err } body, err := io.ReadAll(urlResp.Body) urlResp.Body.Close() resp := WeiXinMobileResp{} fmt.Println(string(body)) err = json.Unmarshal(body, &resp) if err != nil { return "", err } if resp.Errcode != 0 { fmt.Println(resp) err = errors.New(resp.Errmsg) return "", err } return resp.PhoneInfo.PhoneNumber, nil }