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.Url = pay.Url
res.JsInfo = pay.JsApiInfo
controllers.ApiRes(c, res, pay.PayCode)
return
}

View File

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

View File

@ -1,5 +1,16 @@
package front
import (
"PaymentCenter/app/utils"
"crypto"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
type GetWxAuthUrlRequest struct {
PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"`
}
@ -77,6 +88,36 @@ type WxMiniPayResponse struct {
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 {
PayChannelId string `json:"pay_channel_id" form:"pay_channel_id" validate:"required"`
GenerateSchemeRequest

View File

@ -11,3 +11,19 @@ type JumpWxa struct {
Query string `json:"query"`
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/errorcode"
"PaymentCenter/app/http/entities/front"
"PaymentCenter/app/models/merchantmodel"
"PaymentCenter/app/models/paychannelmodel"
"PaymentCenter/app/services"
@ -14,6 +13,7 @@ import (
"context"
"net/url"
"strconv"
"time"
"xorm.io/builder"
"PaymentCenter/app/models/ordersmodel"
@ -38,9 +38,10 @@ type Pay struct {
PayParam *PayParam
RelationOrder *ordersmodel.Orders
Order *ordersmodel.Orders
PayCode int
Url string
ThirdMsg string
PayCode int // 支付状态码
Url string // 支付链接
ThirdMsg string // 第三方错误信息
JsApiInfo *front.WxMiniPayResponse // jsapi支付参数
}
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
}
case common.PAY_CHANNEL_WECHAT_MINI:
req := front.GetWxMiniSchemeRequest{
PayChannelId: strconv.Itoa(int(w.PayParam.Channel.Id)),
GenerateSchemeRequest: front.GenerateSchemeRequest{
JumpWxa: front.JumpWxa{Query: "?order_id=" + strconv.Itoa(int(w.Order.Id))},
}}
if config.GetEnv() == config.LocalEnv {
req.GenerateSchemeRequest.JumpWxa.EnvVersion = "develop"
}
// 微信小程序支付返回小程序的scheme todo 拼接订单id参数
wx := NewWxMini().WithPayChannel(w.PayParam.Channel)
url, err = wx.GetWxMiniScheme(req)
res.Result = url
if err != nil {
w.PayCode = errorcode.PayChannelExtJsonError
w.ThirdMsg = err.Error()
return
// 微信小程序支付
// 如果传openid获取支付参数
if w.PayParam.OpenId != "" {
res = paymentService.PaymentService(*w.ctx, *thirdPay)
} else {
// 如果没有传openid获取小程序scheme链接
req := front.GetWxMiniSchemeRequest{
PayChannelId: strconv.Itoa(int(w.PayParam.Channel.Id)),
GenerateSchemeRequest: front.GenerateSchemeRequest{
JumpWxa: front.JumpWxa{Query: "?order_id=" + strconv.Itoa(int(w.Order.Id))},
}}
if config.GetEnv() == config.LocalEnv {
req.GenerateSchemeRequest.JumpWxa.EnvVersion = "develop"
} else if config.GetEnv() == config.DevEnv {
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:
@ -172,7 +187,27 @@ func (w *Pay) PayUrl() (url string) {
if code != errorcode.Success {
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 {
w.PayCode = errorcode.PrePayFail
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
func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url string, err error) {
var (
has bool
repo = data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
payChannel = paychannelmodel.PayChannel{}
payConfig = paymentService.PayOrderRequest{}
has bool
repo = data.NewPayChannelRepo(paychannelmodel.GetInstance().GetDb())
payConfig = paymentService.PayOrderRequest{}
)
// 获取支付渠道
if wm.payChannel == nil {
payChannel := paychannelmodel.PayChannel{}
has, err = repo.PayChannelGet(&payChannel, builder.Eq{"id": param.PayChannelId})
if err != nil {
return
@ -117,14 +117,15 @@ func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url strin
err = fmt.Errorf("获取支付渠道不存在")
return
}
wm.payChannel = &payChannel
}
// 支付渠道支付配置解析
if configFun, ok := PayWayList[payChannel.ChannelType]; !ok {
if configFun, ok := PayWayList[wm.payChannel.ChannelType]; !ok {
err = fmt.Errorf("解析支付方式不存在")
return
} else {
err = configFun(&payConfig, &payChannel)
err = configFun(&payConfig, wm.payChannel)
if err != nil {
return
}
@ -137,14 +138,14 @@ func (wm *WxMini) GetWxMiniScheme(param front.GetWxMiniSchemeRequest) (url strin
// 小程序的配置参数解析, 适配不同小程序配置需求
if param.JumpWxa.Query == "" {
err = json.Unmarshal([]byte(payChannel.ExtJson), &param.GenerateSchemeRequest)
err = json.Unmarshal([]byte(wm.payChannel.ExtJson), &param.GenerateSchemeRequest)
if err != nil {
return
}
}
// 获取access_token
accessToken, err := wm.GetAccessToken(payChannel.AppId, payConfig.Wx.Secret)
accessToken, err := wm.GetAccessToken(wm.payChannel.AppId, payConfig.Wx.Secret)
if err != nil {
return
}
@ -209,3 +210,45 @@ func (wm *WxMini) GetWxAuthMini(param front.GetWxAuthRequest) (rsp front.GetWxAu
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
}