Commit 6536c4f9 by yemin

no message

parent 6bb13dfc
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions">
<TaskOptions isEnabled="true">
<option name="arguments" value="fmt $FilePath$" />
<option name="checkSyntaxErrors" value="true" />
<option name="description" />
<option name="exitCodeBehavior" value="ERROR" />
<option name="fileExtension" value="go" />
<option name="immediateSync" value="false" />
<option name="name" value="go fmt" />
<option name="output" value="$FilePath$" />
<option name="outputFilters">
<array />
</option>
<option name="outputFromStdout" value="false" />
<option name="program" value="$GoExecPath$" />
<option name="runOnExternalChanges" value="false" />
<option name="scopeName" value="Project Files" />
<option name="trackOnlyRoot" value="true" />
<option name="workingDir" value="$ProjectFileDir$" />
<envs>
<env name="GOROOT" value="$GOROOT$" />
<env name="GOPATH" value="$GOPATH$" />
<env name="PATH" value="$GoBinDirs$" />
</envs>
</TaskOptions>
</component>
</project>
\ No newline at end of file
module git.168cad.top/go/wechat-client-sdk module git.168cad.top/go/wechat-client-sdk
go 1.17 go 1.17
require github.com/wechatpay-apiv3/wechatpay-go v0.2.16 // indirect
github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/wechatpay-apiv3/wechatpay-go v0.2.16 h1:7wTQYLxx5PSkzv0Zu/LabGHDVva9VEvH56su/lzRPbE=
github.com/wechatpay-apiv3/wechatpay-go v0.2.16/go.mod h1:Ca9wvI7xFoIWiY163q1jzddarQBS+1NE17OM1ZV24nw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package partnerpay
import (
"bytes"
"context"
"crypto/rsa"
"errors"
"fmt"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/partnerpayments/jsapi"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"log"
"math/big"
"strings"
"time"
)
// WechatPartnerPayClient 服务商支付
type WechatPartnerPayClient struct {
AppId *string `json:"appId,omitempty"` // 微信小程序Id
SpMchId *string `json:"spMchId,omitempty"` // 服务商商户号(如果服务商模式,该值需要填写)
MchId *string `json:"mchId,omitempty"` // 收款商户号(如果是服务商模式,配置特约商户号)
MchSerialNo *string `json:"mchSerialNo,omitempty"` // 收款商户证书序号
MchPrivateKey *string `json:"mchPrivateKey,omitempty"` // 收款商户私钥
ApiKey *string `json:"apiKey,omitempty"` // apiKey
wxClient *core.Client //不需要外界传入
privateKey *rsa.PrivateKey //不需要外界传入
}
// WxJsApiOrder JSAPI下单
func (s *WechatPartnerPayClient) WxJsApiOrder(p *PaymentJsApiParam) *PaymentJsApiResponse {
wxClient := s.getWxPayClient()
svc := jsapi.JsapiApiService{Client: wxClient}
money, b := new(big.Int).SetString(new(big.Float).Mul(new(big.Float).SetFloat64(*p.Money), new(big.Float).SetFloat64(100)).String(), 10)
if !b {
panic(errors.New("金额错误请检查,请检查"))
}
resp, _, err := svc.Prepay(context.Background(),
jsapi.PrepayRequest{
SpAppid: s.AppId,
SpMchid: s.SpMchId,
SubMchid: s.MchId,
Description: p.Title,
OutTradeNo: p.OutTradeNo,
NotifyUrl: p.CallBackUrl,
TimeExpire: core.Time(getTimeExpire(p.TimeExpire)),
Amount: &jsapi.Amount{
Total: core.Int64(money.Int64()),
},
Payer: &jsapi.Payer{
SpOpenid: p.OpenId,
},
},
)
if err != nil {
panic(errors.New(getErrMessage(err.Error())))
}
log.Println(fmt.Sprintf("微信调用结果:%s", resp.String()))
return buildWxMiniPayInfo(*resp.PrepayId, *s.AppId, s.privateKey)
}
func (s *WechatPartnerPayClient) getWxPayClient() *core.Client {
if s.wxClient != nil {
return s.wxClient
}
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
if s.MchPrivateKey == nil {
panic(errors.New("商户私钥未配置,请检查"))
}
mchPrivateKey, err := LoadPrivateKey(*s.MchPrivateKey)
if err != nil {
panic(errors.New("商户私钥配置错误,请检查"))
}
s.privateKey = mchPrivateKey
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(*s.MchId, *s.MchSerialNo, mchPrivateKey, *s.ApiKey),
}
wc, err := core.NewClient(context.Background(), opts...)
if err != nil {
panic(err)
}
s.wxClient = wc
return wc
}
const defaultExpireTime int64 = 30
func getTimeExpire(expireTime *int64) time.Time {
if expireTime == nil || *expireTime == 0 {
t := defaultExpireTime
expireTime = &t
}
return dateAdd(time.Now(), *expireTime*60)
}
func dateAdd(date time.Time, d int64) time.Time {
t := date.Unix()
t += d //增加秒
return time.Unix(t, 0)
}
func getErrMessage(msg string) string {
startStr := "response:["
endStr := "Header"
startIndex := strings.Index(msg, startStr)
endIndex := strings.Index(msg, endStr)
rs := msg
if startIndex >= 0 && endIndex > 0 {
rs = msg[startIndex+len(startStr) : endIndex]
}
return strings.ReplaceAll(rs, "\n", " ")
}
func buildWxMiniPayInfo(prepayId, appId string, privateKey *rsa.PrivateKey) *PaymentJsApiResponse {
nonceStr, err := utils.GenerateNonce()
if err != nil {
panic(err)
}
packageInfo := "prepay_id=" + prepayId
r := &PaymentJsApiResponse{
TimeStamp: core.Int64(time.Now().Unix()),
NonceStr: &nonceStr,
Package: core.String("prepay_id=" + prepayId),
SignType: core.String("RSA"),
}
r.PaySign = rsaSign(appId, *r.TimeStamp, nonceStr, packageInfo, privateKey)
return r
}
func rsaSign(appId string, timeStamp int64, nonceStr, packageInfo string, privateKey *rsa.PrivateKey) *string {
var buf bytes.Buffer
buf.WriteString(fmt.Sprintf("%s\n", appId))
buf.WriteString(fmt.Sprintf("%d\n", timeStamp))
buf.WriteString(fmt.Sprintf("%s\n", nonceStr))
buf.WriteString(fmt.Sprintf("%s\n", packageInfo))
sign, err := utils.SignSHA256WithRSA(buf.String(), privateKey)
if err != nil {
panic(err)
}
return &sign
}
package partnerpay
import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
)
func LoadPrivateKey(privateKeyStr string) (privateKey *rsa.PrivateKey, err error) {
block, _ := pem.Decode([]byte(buildFullPrivateKey(privateKeyStr)))
if block == nil {
return nil, fmt.Errorf("decode private key err")
}
if block.Type != "PRIVATE KEY" {
return nil, fmt.Errorf("the kind of PEM should be PRVATE KEY")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse private key err:%s", err.Error())
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("%s is not rsa private key", privateKeyStr)
}
return privateKey, nil
}
func buildFullPrivateKey(privateKey string) string {
preLine := "-----BEGIN PRIVATE KEY-----\n"
endLine := "\n-----END PRIVATE KEY-----"
return preLine + privateKey + endLine
}
// LoadPublicKey 通过公钥的文本内容加载公钥
func LoadPublicKey(publicKeyStr string) (publicKey *rsa.PublicKey, err error) {
block, _ := pem.Decode([]byte(buildFullPublicKey(publicKeyStr)))
if block == nil {
return nil, fmt.Errorf("decode public key error")
}
if block.Type != "PUBLIC KEY" {
return nil, fmt.Errorf("the kind of PEM should be PUBLIC KEY")
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parse public key err:%s", err.Error())
}
publicKey, ok := key.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("%s is not rsa public key", publicKeyStr)
}
return publicKey, nil
}
func buildFullPublicKey(publicKey string) string {
preLine := "-----BEGIN PUBLIC KEY-----\n"
endLine := "\n-----END PUBLIC KEY-----"
return preLine + publicKey + endLine
}
package partnerpay
type PaymentParams struct {
Money *float64 `json:"money,omitempty"` // 交易金额(元),必须
Title *string `json:"title,omitempty"` // 交易标题/商品描述,必须
OutTradeNo *string `json:"outTradeNo,omitempty"` // 交易订单号,必须
TimeExpire *int64 `json:"timeExpire,omitempty"` // 超时时长,即代表是多少分钟后订单超时无效(分钟为单位)
CallBackUrl *string `json:"callBackUrl,omitempty"` // 回调地址,必须
}
type PaymentJsApiParam struct {
PaymentParams
OpenId *string `json:"openId"`
}
package partnerpay
type PaymentJsApiResponse struct {
TimeStamp *int64 `json:"timeStamp"` //时间戳
NonceStr *string `json:"nonceStr"` //随机字符串,不长于32位
Package *string `json:"package"` //订单详情扩展字符串,小程序下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
SignType *string `json:"signType"` //签名方式,签名类型,默认为RSA,仅支持RSA
PaySign *string `json:"paySign"` //签名,签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
}
...@@ -8,20 +8,23 @@ import ( ...@@ -8,20 +8,23 @@ import (
) )
func TestCode2Session(t *testing.T) { func TestCode2Session(t *testing.T) {
r,err:= minipro.Code2Session("033DEr0w36huoZ2Ga22w3wXeTR1DEr0g","","") c := minipro.NewMiniClient("","")
r,err:= c.Code2Session("033DEr0w36huoZ2Ga22w3wXeTR1DEr0g")
log.Println(err) log.Println(err)
log.Println(fmt.Sprintf("%+v\n", *r)) log.Println(fmt.Sprintf("%+v\n", *r))
} }
func TestNewAccessToken(t *testing.T) { func TestNewAccessToken(t *testing.T) {
r,err := minipro.NewAccessToken("","") c := minipro.NewMiniClient("","")
r,err := c.NewAccessToken()
log.Println(err) log.Println(err)
log.Println(fmt.Sprintf("%+v\n", *r)) log.Println(fmt.Sprintf("%+v\n", *r))
} }
func TestGetUserPhoneNumber(t *testing.T) { func TestGetUserPhoneNumber(t *testing.T) {
c := minipro.NewMiniClient("","")
accessToken := "61_uqRWPGxxveyZnFoqCsddx9uY5w6uTZtHckodV8sS80OKtEBKJHM1udwD8ImlOa8UXnO_o3A4Qi3cwNtBvoC_fXSFnmXzJaD1cw-dMwaRGoz7XxTLhxvAeA_Bou4TWqPAmXnmmTknNwe9mU3_PVPiAGAZFZ" accessToken := "61_uqRWPGxxveyZnFoqCsddx9uY5w6uTZtHckodV8sS80OKtEBKJHM1udwD8ImlOa8UXnO_o3A4Qi3cwNtBvoC_fXSFnmXzJaD1cw-dMwaRGoz7XxTLhxvAeA_Bou4TWqPAmXnmmTknNwe9mU3_PVPiAGAZFZ"
r,err := minipro.GetUserPhoneNumber("fa46d5412a1c7053de2f930a043fa92b4d253eb83ce4e23d0fe0304bef65ded1", accessToken) r,err := c.GetUserPhoneNumber("fa46d5412a1c7053de2f930a043fa92b4d253eb83ce4e23d0fe0304bef65ded1", accessToken)
log.Println(err) log.Println(err)
log.Println(fmt.Sprintf("%+v\n", *r)) log.Println(fmt.Sprintf("%+v\n", *r))
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment