panghu 7 mēneši atpakaļ
vecāks
revīzija
d1c1bce07b
7 mainītis faili ar 250 papildinājumiem un 30 dzēšanām
  1. 12 4
      api/shop_order.go
  2. 3 0
      go.mod
  3. 4 0
      go.sum
  4. 22 0
      pkg/utils/utils.go
  5. 4 26
      server/aliPayServer.go
  6. 93 0
      server/weChatServer.go
  7. 112 0
      server/weChatV2Server.go

+ 12 - 4
api/shop_order.go

@@ -121,9 +121,11 @@ func (shopOrder ShopOrderController) GetOrderSn(c *gin.Context) {
 	}
 
 	chinaumsServer := chinaums.GetOrderSnServerInstance()
-	weChatServer := server.GetWeChatServerInstance()
+	weChatV2Server := server.GetWeChatV2ServerInstance()
 	aliPayServer := server.GetAliPayServerInstance()
 	var payUrl string
+	var zfbPay string
+	var wxData interface{}
 	var notifyUrl string
 	if data.PayType == 1 { //微信支付
 		//查询当前用户openid
@@ -143,7 +145,8 @@ func (shopOrder ShopOrderController) GetOrderSn(c *gin.Context) {
 			payUrl = chinaumsServer.ShopOrderWxH5Pay(shopMchidInfo, shopOrderModel.RsSn, shopOrderModel.PayMoney, notifyUrl, memberOpenId)
 		} else {
 			notifyUrl = "http://127.0.0.1/api/notify/shopOrder"
-			weChatServer.WeChatJsPay(shopOrderModel.RsSn, shopOrderModel.PayMoney, notifyUrl, "订单支付", memberOpenId)
+			clientIP := c.ClientIP()
+			wxData = weChatV2Server.WeChatV2JsPay(shopOrderModel.RsSn, shopOrderModel.PayMoney, notifyUrl, "订单支付", memberOpenId, clientIP)
 		}
 
 	} else { //支付宝支付
@@ -152,8 +155,13 @@ func (shopOrder ShopOrderController) GetOrderSn(c *gin.Context) {
 		} else {
 			notifyUrl = "http://127.0.0.1/api/notify/shopOrder"
 			returnUrl := "http://127.0.0.1/api/notify/shopOrder"
-			payUrl = aliPayServer.GetAliWapPay(shopOrderModel.RsSn, shopOrderModel.PayMoney, "订单支付", notifyUrl, returnUrl)
+			zfbPay = aliPayServer.GetAliWapPay(shopOrderModel.RsSn, shopOrderModel.PayMoney, "订单支付", notifyUrl, returnUrl)
 		}
 	}
-	shopOrder.responseSuccess(c, payUrl)
+	result := make(map[string]interface{})
+	result["is_bank"] = isBank
+	result["wx_pay"] = wxData
+	result["zfb_pay"] = zfbPay
+	result["pay_url"] = payUrl
+	shopOrder.responseSuccess(c, result)
 }

+ 3 - 0
go.mod

@@ -29,6 +29,9 @@ require (
 	github.com/gabriel-vasile/mimetype v1.4.13 // indirect
 	github.com/gin-contrib/sse v1.1.0 // indirect
 	github.com/go-pay/crypto v0.0.1 // indirect
+	github.com/go-pay/errgroup v0.0.3 // indirect
+	github.com/go-pay/smap v0.0.2 // indirect
+	github.com/go-pay/util v0.0.4 // indirect
 	github.com/go-pay/xlog v0.0.3 // indirect
 	github.com/go-pay/xtime v0.0.2 // indirect
 	github.com/go-sql-driver/mysql v1.9.3 // indirect

+ 4 - 0
go.sum

@@ -31,8 +31,12 @@ github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
 github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
 github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY=
 github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes=
+github.com/go-pay/errgroup v0.0.3 h1:DB4s8e8oWYDyETKQ1y1riMJ7y29zE1uIsMCSjEOFSbU=
+github.com/go-pay/errgroup v0.0.3/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8=
 github.com/go-pay/gopay v1.5.115 h1:8WjWftPChKCiVt5Qz2xLqXeUdidsR+y9/R2S/7Q9szc=
 github.com/go-pay/gopay v1.5.115/go.mod h1:p48xvWeepPolZuakAjCeucWynWwW7msoXsqahcoJpKE=
+github.com/go-pay/smap v0.0.2 h1:kKflYor5T5FgZltPFBMTFfjJvqYMHr5VnIFSEyhVTcA=
+github.com/go-pay/smap v0.0.2/go.mod h1:HW9oAo0okuyDYsbpbj5fJFxnNj/BZorRGFw26SxrNWw=
 github.com/go-pay/util v0.0.4 h1:TuwSU9o3Qd7m9v1PbzFuIA/8uO9FJnA6P7neG/NwPyk=
 github.com/go-pay/util v0.0.4/go.mod h1:Tsdhs8Ib9J9b4+NKNO1PHh5hWHhlg98PthsX0ckq6PM=
 github.com/go-pay/xlog v0.0.3 h1:avyMhCL/JgBHreoGx/am/kHxfs1udDOAeVqbmzP/Yes=

+ 22 - 0
pkg/utils/utils.go

@@ -6,6 +6,8 @@ import (
 	"fmt"
 	"math/big"
 	"net/url"
+	"os"
+	"path/filepath"
 	"regexp"
 	"strconv"
 )
@@ -43,3 +45,23 @@ func Md5Sign(str string) string {
 func UrlEncode(str string) string {
 	return url.QueryEscape(str)
 }
+
+// LoadPrivateKeyWithPath  通过私钥的文件路径内容加载私钥
+func LoadPrivateKeyWithPath(path string) (privateKey string) {
+	absPath, _ := filepath.Abs(path)
+	privateKeyBytes, err := os.ReadFile(absPath)
+	if err != nil {
+		return ""
+	}
+	return string(privateKeyBytes)
+}
+
+// LoadPublicKeyWithPath 通过公钥的文件路径加载公钥
+func LoadPublicKeyWithPath(path string) (publicKey string) {
+	absPath, _ := filepath.Abs(path)
+	publicKeyBytes, err := os.ReadFile(absPath)
+	if err != nil {
+		return ""
+	}
+	return string(publicKeyBytes)
+}

+ 4 - 26
server/aliPayServer.go

@@ -7,8 +7,7 @@ import (
 	"github.com/shopspring/decimal"
 	"github.com/spf13/viper"
 	"go.uber.org/zap"
-	"os"
-	"path/filepath"
+	"go_zh/pkg/utils"
 	"sync"
 )
 
@@ -24,7 +23,6 @@ type AliPayServer struct {
 var (
 	aliPayServerInstance *AliPayServer
 	aliPayServerOnce     sync.Once
-	aliCtx               context.Context
 )
 
 // GetAliPayServerInstance 获取单例实例
@@ -36,9 +34,9 @@ func GetAliPayServerInstance() *AliPayServer {
 			AppId:          viper.GetString("aliPay.appId"),
 			PrivateKey:     viper.GetString("aliPay.privateKey"),
 		}
-		privateKeyStr := loadPrivateKeyWithPath(aliPayServerInstance.PrivateKeyFile)
+		privateKeyStr := utils.LoadPrivateKeyWithPath(aliPayServerInstance.PrivateKeyFile)
 		aliPayServerInstance.privateKeyInfo = privateKeyStr
-		aliPayPublicKeyStr := loadPublicKeyWithPath(aliPayServerInstance.CertFile)
+		aliPayPublicKeyStr := utils.LoadPublicKeyWithPath(aliPayServerInstance.CertFile)
 		aliPayServerInstance.PublicKeyInfo = aliPayPublicKeyStr
 	})
 	return aliPayServerInstance
@@ -67,29 +65,9 @@ func (ali *AliPayServer) GetAliWapPay(orderSn string, payMoney decimal.Decimal,
 		Set("out_trade_no", orderSn).
 		Set("total_amount", payMoney)
 
-	aliRsp, err := client.TradeWapPay(aliCtx, bm)
+	aliRsp, err := client.TradeWapPay(context.Background(), bm)
 	if err != nil {
 		zap.L().Error("支付宝手机网站支付请求失败:", zap.Error(err))
 	}
 	return aliRsp
 }
-
-// loadPrivateKeyWithPath 通过私钥的文件路径内容加载私钥
-func loadPrivateKeyWithPath(path string) (privateKey string) {
-	absPath, _ := filepath.Abs(path)
-	privateKeyBytes, err := os.ReadFile(absPath)
-	if err != nil {
-		return ""
-	}
-	return string(privateKeyBytes)
-}
-
-// loadPublicKeyWithPath 通过公钥的文件路径加载公钥
-func loadPublicKeyWithPath(path string) (publicKey string) {
-	absPath, _ := filepath.Abs(path)
-	publicKeyBytes, err := os.ReadFile(absPath)
-	if err != nil {
-		return ""
-	}
-	return string(publicKeyBytes)
-}

+ 93 - 0
server/weChatServer.go

@@ -0,0 +1,93 @@
+package server
+
+import (
+	"context"
+	"fmt"
+	"github.com/go-pay/gopay"
+	"github.com/go-pay/gopay/wechat/v3"
+	"github.com/shopspring/decimal"
+	"github.com/spf13/viper"
+	"go.uber.org/zap"
+	"go_zh/pkg/utils"
+	"sync"
+)
+
+type WeChatServer struct {
+	MchId                string
+	PrivateKey           string
+	CertFile             string //公钥证书地址
+	PrivateKeyFile       string //私钥证书地址
+	CertificateSerialNo  string
+	WechatPayPublicKeyId string
+	AppId                string //appid
+	AppSecret            string
+	AppletId             string
+	AppletKey            string
+	PublicKeyInfo        string
+	PrivateKeyFileInfo   string
+}
+
+var (
+	weChatServerInstance *WeChatServer
+	weChatServerOnce     sync.Once
+)
+
+// GetWeChatServerInstance 获取单例实例
+func GetWeChatServerInstance() *WeChatServer {
+	weChatServerOnce.Do(func() {
+		weChatServerInstance = &WeChatServer{
+			MchId:                viper.GetString("weChat.mchId"),
+			PrivateKey:           viper.GetString("weChat.privateKey"),
+			CertFile:             viper.GetString("weChat.certFile"),
+			PrivateKeyFile:       viper.GetString("weChat.keyFile"),
+			CertificateSerialNo:  viper.GetString("weChat.certificateSerialNo"),
+			WechatPayPublicKeyId: viper.GetString("weChat.wechatPayPublicKeyId"),
+			AppId:                viper.GetString("weChat.appId"),
+			AppSecret:            viper.GetString("weChat.appSecret"),
+			AppletId:             viper.GetString("weChat.appletId"),
+			AppletKey:            viper.GetString("weChat.appletKey"),
+		}
+		weChatPublicKeyStr := utils.LoadPublicKeyWithPath(weChatServerInstance.CertFile)
+		weChatServerInstance.PublicKeyInfo = weChatPublicKeyStr
+
+		weChatPrivateKeyStr := utils.LoadPrivateKeyWithPath(weChatServerInstance.PrivateKeyFile)
+		weChatServerInstance.PrivateKeyFileInfo = weChatPrivateKeyStr
+	})
+	return weChatServerInstance
+}
+
+// GetAliWapPay 获取支付宝手机网站支付
+func (weChat *WeChatServer) WeChatJsPay(orderSn string, payMoney decimal.Decimal, notifyUrl string, boby string, openId string) string {
+	// NewClientV3 初始化微信客户端 v3
+	// mchid:商户ID 或者服务商模式的 sp_mchid
+	// serialNo:商户证书的证书序列号
+	// apiV3Key:apiV3Key,商户平台获取
+	// privateKey:私钥 apiclient_key.pem 读取后的内容
+	client, err := wechat.NewClientV3(weChat.MchId, weChat.CertificateSerialNo, weChat.PrivateKey, weChat.PrivateKeyFileInfo)
+	if err != nil {
+		zap.L().Error("微信支付NewClient failed", zap.Error(err))
+	}
+	// 微信支付公钥自动同步验签(新微信支付用户推荐)
+	err = client.AutoVerifySignByPublicKey([]byte(weChat.PublicKeyInfo), weChat.WechatPayPublicKeyId)
+	if err != nil {
+		zap.L().Error("微信支付同步验签err", zap.Error(err))
+		return ""
+	}
+
+	bm := make(gopay.BodyMap)
+	bm.Set("appid", weChat.AppId).
+		Set("description", boby).
+		Set("out_trade_no", orderSn).
+		Set("notify_url", notifyUrl).
+		SetBodyMap("amount", func(bm gopay.BodyMap) {
+			bm.Set("total", payMoney.Mul(decimal.NewFromInt(100)).IntPart()).
+				Set("currency", "CNY")
+		}).
+		SetBodyMap("payer", func(bm gopay.BodyMap) {
+			bm.Set("openid", openId)
+		})
+
+	wxRsp, err := client.V3TransactionJsapi(context.Background(), bm)
+	fmt.Println(wxRsp)
+	return ""
+}

+ 112 - 0
server/weChatV2Server.go

@@ -0,0 +1,112 @@
+package server
+
+import (
+	"context"
+	"fmt"
+	"github.com/go-pay/gopay"
+	"github.com/go-pay/gopay/wechat"
+	"github.com/shopspring/decimal"
+	"github.com/spf13/viper"
+	"go_zh/pkg/utils"
+	"strconv"
+	"sync"
+	"time"
+)
+
+type WeChatV2Server struct {
+	MchId              string
+	PrivateKey         string
+	CertFile           string //公钥证书地址
+	PrivateKeyFile     string //私钥证书地址
+	AppId              string //appid
+	AppSecret          string
+	AppletId           string
+	AppletKey          string
+	PublicKeyInfo      string
+	PrivateKeyFileInfo string
+}
+
+var (
+	weChatV2ServerInstance *WeChatV2Server
+	weChatV2ServerOnce     sync.Once
+)
+
+// GetWeChatV2ServerInstance 获取单例实例
+func GetWeChatV2ServerInstance() *WeChatV2Server {
+	weChatV2ServerOnce.Do(func() {
+		weChatV2ServerInstance = &WeChatV2Server{
+			MchId:          viper.GetString("weChat.mchId"),
+			PrivateKey:     viper.GetString("weChat.privateKey"),
+			CertFile:       viper.GetString("weChat.certFile"),
+			PrivateKeyFile: viper.GetString("weChat.keyFile"),
+			AppId:          viper.GetString("weChat.appId"),
+			AppSecret:      viper.GetString("weChat.appSecret"),
+			AppletId:       viper.GetString("weChat.appletId"),
+			AppletKey:      viper.GetString("weChat.appletKey"),
+		}
+		weChatPublicKeyStr := utils.LoadPublicKeyWithPath(weChatV2ServerInstance.CertFile)
+		weChatV2ServerInstance.PublicKeyInfo = weChatPublicKeyStr
+
+		weChatPrivateKeyStr := utils.LoadPrivateKeyWithPath(weChatV2ServerInstance.PrivateKeyFile)
+		weChatV2ServerInstance.PrivateKeyFileInfo = weChatPrivateKeyStr
+	})
+	return weChatV2ServerInstance
+}
+
+type JsApiV2Data struct {
+	AppId     string `json:"appId"`     //员工ID
+	NonceStr  string `json:"nonceStr"`  //当前登录token
+	Package   string `json:"package"`   //当前登录token
+	SignType  string `json:"signType"`  //当前登录token
+	TimeStamp string `json:"timeStamp"` //当前登录token
+	PaySign   string `json:"paySign"`   //当前登录token
+}
+
+// WeChatV2JsPay 获取
+func (weChatV2 *WeChatV2Server) WeChatV2JsPay(orderSn string, payMoney decimal.Decimal, notifyUrl string, boby string, openId string, clientIP string) JsApiV2Data {
+	// 初始化微信客户端
+	//    appId:应用ID
+	//    mchId:商户ID
+	//    apiKey:API秘钥值
+	//    isProd:是否是正式环境
+	client := wechat.NewClient(weChatV2.AppId, weChatV2.MchId, weChatV2.PrivateKey, true)
+
+	bm := make(gopay.BodyMap)
+	bm.Set("nonce_str", utils.Md5Sign(orderSn)).
+		Set("spbill_create_ip", clientIP).
+		Set("appid", weChatV2.AppId).
+		Set("mch_id", weChatV2.MchId).
+		Set("body", boby).
+		Set("out_trade_no", orderSn).
+		Set("notify_url", notifyUrl).
+		Set("trade_type", "JSAPI").
+		Set("total_fee", payMoney.Mul(decimal.NewFromInt(100)).IntPart()).
+		Set("openid", openId)
+
+	wxRsp, err := client.UnifiedOrder(context.Background(), bm)
+	if err != nil {
+		fmt.Println("err:", err)
+	}
+	// ====微信小程序 paySign====
+	timeStamp := strconv.FormatInt(time.Now().Unix(), 10)
+	packages := "prepay_id=" + wxRsp.PrepayId // 此处的 wxRsp.PrepayId ,统一下单成功后得到
+	// 获取微信小程序支付的 paySign
+	//    appId:AppID
+	//    nonceStr:随机字符串
+	//    packages:统一下单成功后拼接得到的值
+	//    signType:签名方式,务必与统一下单时用的签名方式一致
+	//    timeStamp:时间
+	//    apiKey:API秘钥值
+	paySign := wechat.GetMiniPaySign(weChatV2.AppId, wxRsp.NonceStr, packages, wechat.SignType_MD5, timeStamp, weChatV2.PrivateKey)
+
+	data := JsApiV2Data{
+		AppId:     weChatV2.AppId,
+		NonceStr:  utils.Md5Sign(timeStamp),
+		Package:   "prepay_id=" + wxRsp.PrepayId,
+		SignType:  "MD5",
+		TimeStamp: timeStamp,
+		PaySign:   paySign,
+	}
+
+	return data
+}