feat: v1支付支持微信小程序

This commit is contained in:
wolter 2025-04-25 14:56:44 +08:00
parent 7a2d9fa1d7
commit de6bed55ef
6 changed files with 168 additions and 31 deletions

View File

@ -35,6 +35,7 @@ func PayUrl(c *gin.Context) {
res.Order = thirdpay.NewOrdersResp(pay.Order) res.Order = thirdpay.NewOrdersResp(pay.Order)
res.Url = pay.Url res.Url = pay.Url
res.JsInfo = pay.JsApiInfo
controllers.ApiRes(c, res, pay.PayCode) controllers.ApiRes(c, res, pay.PayCode)
return return
} }

View File

@ -57,8 +57,9 @@ type CloseReqs struct {
// api 接口返回数据, 统一返回结构, order数据会进行加密 // api 接口返回数据, 统一返回结构, order数据会进行加密
type ApiResponse struct { type ApiResponse struct {
Order interface{} `json:"order,omitempty"` Order interface{} `json:"order,omitempty"`
Url string `json:"url,omitempty"` Url string `json:"url,omitempty"`
JsInfo *WxMiniPayResponse `json:"js_info,omitempty"`
} }
type PayChannelListRequest struct { type PayChannelListRequest struct {

View File

@ -1,5 +1,16 @@
package front package front
import (
"PaymentCenter/app/utils"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
type GetWxAuthUrlRequest struct { type GetWxAuthUrlRequest struct {
PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"` PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"`
} }
@ -77,6 +88,36 @@ type WxMiniPayResponse struct {
ReturnUrl string `json:"return_url"` ReturnUrl string `json:"return_url"`
} }
func (this *WxMiniPayResponse) SignPaySign(PrivateKey string) (err error) {
//组装被加密的字符串
targetStr := this.AppId + "\n" + this.TimeStamp + "\n" + this.NonceStr + "\n" + this.Package + "\n"
//var keypath = "商户证书私钥地址"
//key, err := ioutil.ReadFile(keypath)
var key []byte
if PrivateKey == "" {
err = fmt.Errorf("配置的私钥不存在")
return
}
key = []byte(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(targetStr))
digest := h.Sum(nil)
s, _ := rsa.SignPKCS1v15(nil, privateKey.(*rsa.PrivateKey), crypto.SHA256, digest)
this.PaySign = base64.StdEncoding.EncodeToString(s)
return
}
type GetWxMiniSchemeRequest struct { type GetWxMiniSchemeRequest struct {
PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"` PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"`
GenerateSchemeRequest GenerateSchemeRequest

View File

@ -11,3 +11,19 @@ type JumpWxa struct {
Query string `json:"query"` Query string `json:"query"`
EnvVersion string `json:"env_version"` EnvVersion string `json:"env_version"`
} }
type GenerateUrlLinkRequest struct {
Path string `json:"path"`
Query string `json:"query"`
ExpireType int `json:"expire_type"`
ExpireInterval int `json:"expire_interval"`
EnvVersion string `json:"env_version"`
CloudBase CloudBase `json:"cloud_base"`
}
type CloudBase struct {
Env string `json:"env"`
Domain string `json:"domain"`
Path string `json:"path"`
Query string `json:"query"`
}

View File

@ -4,7 +4,6 @@ import (
"PaymentCenter/app/constants/common" "PaymentCenter/app/constants/common"
"PaymentCenter/app/constants/errorcode" "PaymentCenter/app/constants/errorcode"
"PaymentCenter/app/http/entities/front" "PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/merchantmodel" "PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/paychannelmodel" "PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services" "PaymentCenter/app/services"
@ -14,6 +13,7 @@ import (
"context" "context"
"net/url" "net/url"
"strconv" "strconv"
"time"
"xorm.io/builder" "xorm.io/builder"
"PaymentCenter/app/models/ordersmodel" "PaymentCenter/app/models/ordersmodel"
@ -38,9 +38,10 @@ type Pay struct {
PayParam *PayParam PayParam *PayParam
RelationOrder *ordersmodel.Orders RelationOrder *ordersmodel.Orders
Order *ordersmodel.Orders Order *ordersmodel.Orders
PayCode int PayCode int // 支付状态码
Url string Url string // 支付链接
ThirdMsg string ThirdMsg string // 第三方错误信息
JsApiInfo *front.WxMiniPayResponse // jsapi支付参数
} }
func NewPayWithPayCheck(paycheck *PayCheck) *Pay { func NewPayWithPayCheck(paycheck *PayCheck) *Pay {
@ -143,22 +144,36 @@ func (w *Pay) PayUrl() (url string) {
res.Result = config.GetConf().PayService.Host + "/pay/front/api/v1/brokerWechatUrl" + "?url=" + res.Result res.Result = config.GetConf().PayService.Host + "/pay/front/api/v1/brokerWechatUrl" + "?url=" + res.Result
} }
case common.PAY_CHANNEL_WECHAT_MINI: case common.PAY_CHANNEL_WECHAT_MINI:
req := front.GetWxMiniSchemeRequest{ // 微信小程序支付
PayChannelId: strconv.Itoa(int(w.PayParam.Channel.Id)),
GenerateSchemeRequest: front.GenerateSchemeRequest{ // 如果传openid获取支付参数
JumpWxa: front.JumpWxa{Query: "?order_id=" + strconv.Itoa(int(w.Order.Id))}, if w.PayParam.OpenId != "" {
}} res = paymentService.PaymentService(*w.ctx, *thirdPay)
if config.GetEnv() == config.LocalEnv { } else {
req.GenerateSchemeRequest.JumpWxa.EnvVersion = "develop" // 如果没有传openid获取小程序scheme链接
} req := front.GetWxMiniSchemeRequest{
// 微信小程序支付返回小程序的scheme todo 拼接订单id参数 PayChannelId: strconv.Itoa(int(w.PayParam.Channel.Id)),
wx := NewWxMini().WithPayChannel(w.PayParam.Channel) GenerateSchemeRequest: front.GenerateSchemeRequest{
url, err = wx.GetWxMiniScheme(req) JumpWxa: front.JumpWxa{Query: "?order_id=" + strconv.Itoa(int(w.Order.Id))},
res.Result = url }}
if err != nil { if config.GetEnv() == config.LocalEnv {
w.PayCode = errorcode.PayChannelExtJsonError req.GenerateSchemeRequest.JumpWxa.EnvVersion = "develop"
w.ThirdMsg = err.Error() } else if config.GetEnv() == config.DevEnv {
return req.GenerateSchemeRequest.JumpWxa.EnvVersion = "trial"
}
// 微信小程序支付返回小程序的scheme
wx := NewWxMini().WithPayChannel(w.PayParam.Channel)
// 拼接订单id参数
req.JumpWxa.Query = "?order_id=" + strconv.Itoa(int(w.Order.Id))
url, err = wx.GetWxMiniScheme(req)
res.Result = url
if err != nil {
w.PayCode = errorcode.PayChannelExtJsonError
w.ThirdMsg = err.Error()
return
}
} }
default: default:
@ -172,7 +187,27 @@ func (w *Pay) PayUrl() (url string) {
if code != errorcode.Success { if code != errorcode.Success {
w.PayCode = code w.PayCode = code
} }
w.Url = res.Result
// 微信小程序
if w.PayParam.Channel.ChannelType == common.PAY_CHANNEL_WECHAT_MINI {
// 组装返回数据
if res.Result != "" {
w.JsApiInfo = &front.WxMiniPayResponse{
AppId: w.PayParam.Channel.AppId,
TimeStamp: strconv.Itoa(int(time.Now().Unix())),
NonceStr: strconv.Itoa(int(time.Now().Unix())),
Package: "prepay_id=" + res.Result,
SignType: "RSA",
ThirdMsg: res.ErrorMsg,
ReturnUrl: w.PayParam.ReturnUrl,
}
// 获取签名
err = w.JsApiInfo.SignPaySign(thirdPay.Wx.PrivateKey)
}
} else {
w.Url = res.Result
}
} else { } else {
w.PayCode = errorcode.PrePayFail w.PayCode = errorcode.PrePayFail
w.ThirdMsg = res.ErrorMsg w.ThirdMsg = res.ErrorMsg

View File

@ -102,14 +102,14 @@ func (wm *WxMini) generateScheme(accessToken string, param front.GenerateSchemeR
// 小程序 获取sechema 链接 通过 pay_channel_id 获取 微信小程序的appid 和 secret 生成 access_token 然后生成scheme // 小程序 获取sechema 链接 通过 pay_channel_id 获取 微信小程序的appid 和 secret 生成 access_token 然后生成scheme
func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url string, err error) { func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url string, err error) {
var ( var (
has bool has bool
repo = data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb()) repo = data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
payChannel = paychannelmodel.PayChannel{} payConfig = paymentService.PayOrderRequest{}
payConfig = paymentService.PayOrderRequest{}
) )
// 获取支付渠道 // 获取支付渠道
if wm.payChannel == nil { if wm.payChannel == nil {
payChannel := paychannelmodel.PayChannel{}
has, err = repo.PayChannelGet(&payChannel, builder.Eq{"id": param.PayChannelId}) has, err = repo.PayChannelGet(&payChannel, builder.Eq{"id": param.PayChannelId})
if err != nil { if err != nil {
return return
@ -117,14 +117,15 @@ func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url strin
err = fmt.Errorf("获取支付渠道不存在") err = fmt.Errorf("获取支付渠道不存在")
return return
} }
wm.payChannel = &payChannel
} }
// 支付渠道支付配置解析 // 支付渠道支付配置解析
if configFun, ok := PayWayList[payChannel.ChannelType]; !ok { if configFun, ok := PayWayList[wm.payChannel.ChannelType]; !ok {
err = fmt.Errorf("解析支付方式不存在") err = fmt.Errorf("解析支付方式不存在")
return return
} else { } else {
err = configFun(&payConfig, &payChannel) err = configFun(&payConfig, wm.payChannel)
if err != nil { if err != nil {
return return
} }
@ -137,14 +138,14 @@ func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url strin
// 小程序的配置参数解析, 适配不同小程序配置需求 // 小程序的配置参数解析, 适配不同小程序配置需求
if param.JumpWxa.Query == "" { if param.JumpWxa.Query == "" {
err = json.Unmarshal([]byte(payChannel.ExtJson), &param.GenerateSchemeRequest) err = json.Unmarshal([]byte(wm.payChannel.ExtJson), &param.GenerateSchemeRequest)
if err != nil { if err != nil {
return return
} }
} }
// 获取access_token // 获取access_token
accessToken, err := wm.GetAccessToken(payChannel.AppId, payConfig.Wx.Secret) accessToken, err := wm.GetAccessToken(wm.payChannel.AppId, payConfig.Wx.Secret)
if err != nil { if err != nil {
return return
} }
@ -209,3 +210,45 @@ func (wm *WxMini) GetWxAuthMini(param front.GetWxAuthRequest) (rsp front.GetWxAu
return return
} }
// 小程序 获取加密URLLink
func (wm *WxMini) generateUrlLink(accessToken string, param front.GenerateUrlLinkRequest) (url string, err error) {
var (
targetUrl = "https://api.weixin.qq.com/wxa/generate_urllink?access_token=" + accessToken
header = map[string]string{
"Content-Type": "application/json",
}
body []byte
)
type UrlLinkResponse struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
UrlLink string `json:"url_link"`
}
body, err = json.Marshal(param)
if err != nil {
return
}
// 发送请求
response, err := httpclient.FastHttpPost(targetUrl, header, body, 0)
if err != nil {
err = fmt.Errorf("GenerateUrlLink 获取加密scheme码 err:%s", err.Error())
return
}
utils.Log(nil, "GenerateUrlLink 获取加密scheme码 ", "response ="+string(response))
var rsp UrlLinkResponse
err = json.Unmarshal(response, &rsp)
if err != nil {
err = fmt.Errorf("GenerateUrlLink 获取加密scheme码 err:%s", err.Error())
return
} else if rsp.Errcode != 0 {
err = fmt.Errorf("GenerateUrlLink 获取加密scheme码 code:%d err:%s", rsp.Errcode, rsp.Errmsg)
return
}
return
}