PaymentCenter/app/services/thirdpay/wx.go

360 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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_tokenopenid
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
}
}