package thirdpay import ( "PaymentCenter/app/constants/common" "PaymentCenter/app/constants/errorcode" "PaymentCenter/app/http/entities/front" "PaymentCenter/app/models/orderrequestlogmodel" "PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/models/paychannelmodel" "PaymentCenter/app/services" "PaymentCenter/app/third/paymentService" "PaymentCenter/app/third/paymentService/payCommon" "PaymentCenter/app/utils" "PaymentCenter/app/utils/httpclient" "PaymentCenter/config" "context" "crypto" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "net/url" "strconv" "time" "xorm.io/builder" ) // 获取授权链接 func GetWxAuthUrl(param front.GetWxAuthUrlRequest) (targetUrl string, code int) { var ( // 重定向地址 redirectUri = config.GetConf().PayService.Host + common.WXCodeRedirectAddress ) baseUrl := "https://open.weixin.qq.com/connect/oauth2/authorize" // 获取支付渠道的配置 id, err := strconv.Atoi(param.PayChannelId) if err != nil { code = handErr(err) return } payChannel := paychannelmodel.PayChannel{Id: int64(id)} code = services.PayChannelGet(&payChannel) if code != 200 { return } appid := payChannel.AppId redirectUri = url.QueryEscape(redirectUri) responseType := "code" scope := "snsapi_base" targetUrl = baseUrl + "?appid=" + appid + "&redirect_uri=" + redirectUri + "&response_type=" + responseType + "&scope=" + scope + "&state=" + param.PayChannelId + "#wechat_redirect" return } // // 通过code换取网页授权access_token,openid func GetWxAuth(param front.GetWxAuthRequest) (rsp front.GetWxAuthResponse, code int) { // 获取支付渠道的配置 id, err := strconv.Atoi(param.PayChannelId) if err != nil { code = handErr(err) return } payChannel := paychannelmodel.PayChannel{Id: int64(id)} code = services.PayChannelGet(&payChannel) if code != 200 { return } //// 配置支付的解析 wxConfig := make(map[string]interface{}) err = json.Unmarshal([]byte(payChannel.ExtJson), &wxConfig) if err != nil { code = handErr(err) return } sk := wxConfig["secret"].(string) if sk == "" { code = errorcode.PayChannelConfigNotFound return } targetUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", payChannel.AppId, sk, param.Code, ) header := map[string]string{ "Content-Type": "application/json", } body := map[string]string{} response, err := httpclient.FastHttpGet(targetUrl, header, body, 0) if err != nil { code = handErr(err) return } utils.Log(nil, "获取微信授权信息", string(response), targetUrl) // 解析返回数据 err = json.Unmarshal(response, &rsp) if err != nil { code = handErr(err) return } if rsp.Openid == "" { code = errorcode.WechatAuthFail return } code = handErr(err) return } // 微信JSAPI 支付接口 /* 1.获取订单请求参数 2.订单信息存数据 3.返回用户授权地址 4.用户授权后获取code 5.通过code获取openid 6.通过openid请求微信下单,返回支付参数 7.支付参数聚合,签名,返回支付参数给前端 */ type wxJsapiPay struct { code int msg string param front.WxJsApiPayRequest order *ordersmodel.Orders payChannel paychannelmodel.PayChannel wxConfig paymentService.WxPay orderPayRequest front.PayReqs openId string // 用户openid } func newWxJsapiPay(param front.WxJsApiPayRequest) *wxJsapiPay { return &wxJsapiPay{param: param} } func (this *wxJsapiPay) getOrder() { order := new(ordersmodel.Orders) this.order, this.code = services.OrderFindOne(order, builder.Eq{"id": this.param.State}) if this.code != errorcode.Success { return } switch this.order.Status { case common.ORDER_STATUS_PAYED: this.code = errorcode.OrderPayed case common.ORDER_STATUS_CLOSE: this.code = errorcode.OrderClosed } } func (this *wxJsapiPay) getPayChannel() { payChannel := paychannelmodel.PayChannel{Id: this.order.PayChannelId} this.code = services.PayChannelGet(&payChannel) this.payChannel = payChannel } // 获取下单的请求参数 func (this *wxJsapiPay) getOrderRequestLogs() { orderRequestLog := orderrequestlogmodel.OrderRequestLog{ OutTradeNo: this.order.OutTradeNo, AppId: this.order.AppId, } has, err := services.OrderRequestLogs(&orderRequestLog) if err != nil { this.code = handErr(err) return } if !has { this.code = errorcode.OrderPayRequestLogNotExist return } err = json.Unmarshal([]byte(orderRequestLog.MerchantRequest), &this.orderPayRequest) if err != nil { this.code = handErr(err) return } } func (this *wxJsapiPay) getOpenId() { var err error //// 配置支付的解析 wxConfig := paymentService.WxPay{} err = json.Unmarshal([]byte(this.payChannel.ExtJson), &wxConfig) if err != nil { this.code = handErr(err) return } sk := wxConfig.Secret if sk == "" { this.code = errorcode.PayChannelConfigNotFound return } this.wxConfig = wxConfig targetUrl := fmt.Sprintf("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code", this.payChannel.AppId, sk, this.param.Code, ) header := map[string]string{ "Content-Type": "application/json", } body := map[string]string{} response, err := httpclient.FastHttpGet(targetUrl, header, body, 0) if err != nil { this.code = handErr(err) return } utils.Log(nil, "获取微信授权信息", string(response), targetUrl) // 解析返回数据 var rsp front.GetWxAuthResponse err = json.Unmarshal(response, &rsp) if err != nil { this.code = handErr(err) return } if rsp.Openid == "" { this.code = errorcode.WechatAuthFail this.msg = string(response) return } this.openId = rsp.Openid this.code = handErr(err) } func (this *wxJsapiPay) Sign(AppId, TimeStamp, NonceStr, Package string) (res string, err error) { //组装被加密的字符串 targetStr := AppId + "\n" + TimeStamp + "\n" + NonceStr + "\n" + Package + "\n" //加密 sign, err := this.SHA256WithRsaBase64(targetStr) return sign, err } func (this *wxJsapiPay) SHA256WithRsaBase64(origData string) (sign string, err error) { //var keypath = "商户证书私钥地址" //key, err := ioutil.ReadFile(keypath) var key []byte if this.wxConfig.PrivateKey == "" { return "", errors.New("配置的私钥不存在") } key = []byte(this.wxConfig.PrivateKey) blocks, _ := pem.Decode(key) if blocks == nil || blocks.Type != "PRIVATE KEY" { utils.Log(nil, "failed to decode PRIVATE KEY") return } privateKey, err := x509.ParsePKCS8PrivateKey(blocks.Bytes) h := sha256.New() h.Write([]byte(origData)) digest := h.Sum(nil) s, _ := rsa.SignPKCS1v15(nil, privateKey.(*rsa.PrivateKey), crypto.SHA256, digest) sign = base64.StdEncoding.EncodeToString(s) return sign, err } func WxJsApiPay(param front.WxJsApiPayRequest) (response front.WxJsApiPayResponse, code int) { var ctx = context.Background() task := newWxJsapiPay(param) // 1 获取订单 task.getOrder() if task.code != errorcode.Success { return response, task.code } // 获取支付渠道配置 task.getPayChannel() if task.code != errorcode.Success { return response, task.code } // 获取下单的请求参数 task.getOrderRequestLogs() if task.code != errorcode.Success { return response, task.code } // 2 通过code获取openid task.getOpenId() if task.code != errorcode.Success { response.ThirdMsg = task.msg return response, task.code } task.wxConfig.AppId = task.payChannel.AppId order := task.order // 通过openid,订单数据,请求微信下单, 获取prepay_id orderRequest := paymentService.PayOrderRequest{ PayChannelId: order.PayChannelId, OrderId: order.Id, ChannelType: task.payChannel.ChannelType, Description: order.Desc, Amount: order.Amount, PayerClientIp: param.ClientIp, ReturnUrl: task.orderPayRequest.ReturnUrl, Wx: task.wxConfig, Ali: paymentService.AliPay{}, OpenId: task.openId, } res := paymentService.PaymentService(ctx, orderRequest) // 下单失败 if res.Code != payCommon.PAY_SUCCESS_CODE { task.code = errorcode.PrePayFail response.ThirdMsg = res.ErrorMsg return response, task.code } // 更新订单状态 order.Status = common.ORDER_STATUS_PAYING task.code = services.OrderUpdate(order, "status") if task.code != errorcode.Success { utils.Log(nil, "成功下单,更新订单状态失败", order) } // 3 组装返回数据 response = front.WxJsApiPayResponse{ AppId: task.payChannel.AppId, TimeStamp: strconv.Itoa(int(time.Now().Unix())), NonceStr: strconv.Itoa(int(time.Now().Unix())), Package: "prepay_id=" + res.Result, SignType: "RSA", PaySign: "", ThirdMsg: "", } // 4 签名 signValue, err := task.Sign(response.AppId, response.TimeStamp, response.NonceStr, response.Package) if err != nil { utils.Log(nil, "签名失败", err) return response, errorcode.WechatAuthFail } response.PaySign = signValue response.ReturnUrl = task.orderPayRequest.ReturnUrl return response, task.code } func handErr(err error) int { if err != nil { utils.Log(nil, "sys err", err.Error()) return errorcode.SystemError } else { return errorcode.Success } }