Commit 1bd9d1c6 authored by haoyanbin's avatar haoyanbin

1

parent e3d817fc
package utils
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"strings"
"time"
)
func SerializeJson(data interface{}) []byte {
reply, _ := json.Marshal(data)
return reply
}
func UnserislizeJson(str string, data interface{}) {
_ = json.Unmarshal([]byte(str), data)
}
func ChangeData(dataBefore interface{}, dataAfter interface{}) {
UnserislizeJson(string(SerializeJson(dataBefore)), dataAfter)
}
func RequesBody(reqBody io.ReadCloser) []byte {
var bodyBytes []byte
bodyBytes, _ = ioutil.ReadAll(reqBody)
return bodyBytes
}
// 发送POST请求
// url: 请求地址
// data: POST请求提交的数据
// contentType: 请求体格式,如:application/json
// content: 请求放回的内容
func Post(url string, data interface{}, token string) string {
// 超时时间:10秒
client := &http.Client{Timeout: 10 * time.Second}
jsonStr, _ := json.Marshal(data)
fmt.Println(string(jsonStr))
reqest, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if token != "" {
reqest.Header.Add("Authorization", "Bearer "+token)
}
reqest.Header.Add("Content-Type", "application/json")
reqest.Header.Add("Connection", "keep-alive")
if err != nil {
panic(err)
}
resp, _ := client.Do(reqest)
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
return string(result)
}
func Get(path string, params map[string]string) (string, error) {
var client = http.Client{
Timeout: 10 * time.Second,
}
defer func() {
if err := recover(); err != nil {
fmt.Println("http get err: ", err)
fmt.Println("client: ", client)
return
}
}()
param := "?"
for key, val := range params {
param += key + "=" + url.QueryEscape(val) + "&"
}
param = strings.Trim(param, "&")
fmt.Println(path + param)
resp, err := client.Get(path + param)
resp.Header.Set("Content-Type", "application/json; charset=utf-8")
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
return string(body), nil
}
func PostWithFormData(method, url string, postData *map[string]string, token string) string {
body := new(bytes.Buffer)
w := multipart.NewWriter(body)
for k, v := range *postData {
w.WriteField(k, v)
}
w.Close()
req, _ := http.NewRequest(method, url, body)
req.Header.Set("Content-Type", w.FormDataContentType())
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
data, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(resp.StatusCode)
fmt.Printf("%s", data)
return string(data)
}
package wx
import "errors"
// CommonError 微信返回错误信息
type CommonError struct {
ErrCode int `json:"errcode"` // 错误码
ErrMSG string `json:"errmsg"` // 错误描述
}
// GetResponseError 获取微信服务器错返回误信息
func (err *CommonError) GetResponseError() error {
if err.ErrCode != 0 {
return errors.New(err.ErrMSG)
}
return nil
}
// CommonResult 微信返回错误信息
type CommonResult struct {
ResultCode int `json:"resultcode"` // 错误码
ResultMsg string `json:"resultmsg"` // 错误描述
}
// GetResponseError 获取微信服务器错返回误信息
func (err *CommonResult) GetResponseError() error {
if err.ResultCode != 0 {
return errors.New(err.ResultMsg)
}
return nil
}
package wx
import (
"encoding/base64"
"github.com/medivhzhan/weapp/v2"
"io/ioutil"
)
func GetUnlimited(token string, scene string, page string, width int, colorR string, colorG string, colorB string) string {
getter := weapp.UnlimitedQRCode{
Scene: scene,
Page: page,
Width: width,
AutoColor: true,
LineColor: weapp.Color{colorR, colorG, colorB},
IsHyaline: true,
}
resp, res, err := getter.Get(token)
if err != nil {
// 处理一般错误信息
return err.Error()
}
if err := res.GetResponseError(); err != nil {
// 处理微信返回错误信息
return err.Error()
}
defer resp.Body.Close()
content, err := ioutil.ReadAll(resp.Body)
sEnc := base64.StdEncoding.EncodeToString(content)
// 处理图片内容
return sEnc
}
package wx
import (
"encoding/xml"
"fmt"
"strconv"
)
const (
kRefund = "/secapi/pay/refund"
kRefundSandbox = "/pay/refund"
kRefundQuery = "/pay/refundquery"
)
// Refund https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6
func (this *Client) Refund(param RefundParam) (result *RefundRsp, err error) {
var api = kRefundSandbox
if this.isProduction {
api = kRefund
}
if err = this.doRequestWithTLS("POST", this.BuildAPI(api), param, &result); err != nil {
return nil, err
}
return result, err
}
// RefundQuery https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_5&index=7
func (this *Client) RefundQuery(param RefundQueryParam) (result *RefundQueryRsp, err error) {
body, err := this.doRequestWithClient(this.Client, "POST", this.BuildAPI(kRefundQuery), param, &result)
if err != nil {
return nil, err
}
if result != nil {
var infoMap = make(XMLMap)
xml.Unmarshal(body, &infoMap)
for i := 0; i < result.RefundCount; i++ {
var info = &RefundInfo{}
info.OutRefundNo = infoMap.Get(fmt.Sprintf("out_refund_no_%d", i))
info.RefundAccount = infoMap.Get(fmt.Sprintf("refund_account_%d", i))
info.RefundChannel = infoMap.Get(fmt.Sprintf("refund_channel_%d", i))
info.RefundFee, _ = strconv.Atoi(infoMap.Get(fmt.Sprintf("refund_fee_%d", i)))
info.RefundId = infoMap.Get(fmt.Sprintf("refund_id_%d", i))
info.RefundRecvAccount = infoMap.Get(fmt.Sprintf("refund_recv_accout_%d", i))
info.RefundStatus = infoMap.Get(fmt.Sprintf("refund_status_%d", i))
info.RefundSuccessTime = infoMap.Get(fmt.Sprintf("refund_success_time_%d", i))
result.RefundInfos = append(result.RefundInfos, info)
}
}
return result, err
}
package wx
import (
"fmt"
"net/url"
)
// RefundParam https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_4&index=6
type RefundParam struct {
NotifyURL string // 否 异步接收微信支付退款结果通知的回调地址,通知URL必须为外网可访问的url,不允许带参数, 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效。
SignType string // 否 签名类型,默认为MD5,支持HMAC-SHA256和MD5。
TransactionId string // 微信生成的订单号,在支付通知中有返回
OutTradeNo string // 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|*@ ,且在同一个商户号下唯一。
OutRefundNo string // 是 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
TotalFee int64 // 是 订单总金额,单位为分,只能为整数,详见支付金额
RefundFee int64 // 是 退款总金额,订单总金额,单位为分,只能为整数,详见支付金额
RefundFeeType string // 否 退款货币类型,需与支付一致,或者不填。符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型
RefundDesc string // 否 若商户传入,会在下发给用户的退款消息中体现退款原因, 注意:若订单退款金额≤1元,且属于部分退款,则不会在退款消息中体现退款原因
RefundAccount string // 否 仅针对老资金流商户使用: REFUND_SOURCE_UNSETTLED_FUNDS---未结算资金退款(默认使用未结算资金退款), REFUND_SOURCE_RECHARGE_FUNDS---可用余额退款
}
func (this RefundParam) Params() url.Values {
var m = make(url.Values)
m.Set("notify_url", this.NotifyURL)
if len(this.SignType) == 0 {
this.SignType = SignTypeMD5
}
m.Set("sign_type", this.SignType)
if len(this.TransactionId) > 0 {
m.Set("transaction_id", this.TransactionId)
}
if len(this.OutTradeNo) > 0 {
m.Set("out_trade_no", this.OutTradeNo)
}
m.Set("out_refund_no", this.OutRefundNo)
m.Set("total_fee", fmt.Sprintf("%d", this.TotalFee))
m.Set("refund_fee", fmt.Sprintf("%d", this.RefundFee))
if len(this.RefundFeeType) > 0 {
m.Set("refund_fee_type", this.RefundFeeType)
}
if len(this.RefundDesc) > 0 {
m.Set("refund_desc", this.RefundDesc)
}
if len(this.RefundAccount) > 0 {
m.Set("refund_account", this.RefundAccount)
}
return m
}
type RefundRsp struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
ResultCode string `xml:"result_code"`
ErrCode string `xml:"err_code"`
ErrCodeDes string `xml:"err_code_des"`
AppId string `xml:"appid"`
MCHId string `xml:"mch_id"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
TransactionId string `xml:"transaction_id"`
OutTradeNo string `xml:"out_trade_no"`
OutRefundNo string `xml:"out_refund_no"`
RefundId string `xml:"refund_id"`
RefundFee int `xml:"refund_fee"`
SettlementRefundFee int `xml:"settlement_refund_fee"`
TotalFee int `xml:"total_fee"`
SettlementTotalFee int `xml:"settlement_total_fee"`
FeeType string `xml:"fee_type"`
CashFee int `xml:"cash_fee"`
CashFeeType string `xml:"cash_fee_type"`
CashRefundFee int `xml:"cash_refund_fee"`
CouponRefundFee int `xml:"coupon_refund_fee"`
CouponRefundCount int `xml:"coupon_refund_count"`
}
// RefundQueryParam https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_5&index=7
type RefundQueryParam struct {
TransactionId string
OutTradeNo string
OutRefundNo string
RefundId string
Offset int
}
func (this RefundQueryParam) Params() url.Values {
var m = make(url.Values)
if this.TransactionId != "" {
m.Set("transaction_id", this.TransactionId)
}
if this.OutTradeNo != "" {
m.Set("out_trade_no", this.OutTradeNo)
}
if this.OutRefundNo != "" {
m.Set("out_refund_no", this.OutRefundNo)
}
if this.RefundId != "" {
m.Set("refund_id", this.RefundId)
}
m.Set("offset", fmt.Sprintf("%d", this.Offset))
return m
}
type RefundQueryRsp struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
ResultCode string `xml:"result_code"`
ErrCode string `xml:"err_code"`
ErrCodeDes string `xml:"err_code_des"`
AppId string `xml:"appid"`
MCHId string `xml:"mch_id"`
NonceStr string `xml:"nonce_str"`
Sign string `xml:"sign"`
TransactionId string `xml:"transaction_id"`
OutTradeNo string `xml:"out_trade_no"`
TotalRefundCount int `xml:"total_refund_count"`
TotalFee int `xml:"total_fee"`
FeeType string `xml:"fee_type"`
CashFee int `xml:"cash_fee"`
CashFeeType string `xml:"cash_fee_type"`
SettlementTotalFee int `xml:"settlement_total_fee"`
RefundCount int `xml:"refund_count"`
RefundFee int `xml:"refund_fee"`
RefundInfos []*RefundInfo `xml:"-"`
}
type RefundInfo struct {
OutRefundNo string
RefundAccount string
RefundChannel string
RefundFee int
RefundId string
RefundRecvAccount string
RefundStatus string
RefundSuccessTime string
}
package wx
import (
"bytes"
"encoding/json"
"io"
"math/rand"
"mime/multipart"
"net/http"
"net/url"
"os"
"time"
)
// tokenAPI 获取带 token 的 API 地址
func tokenAPI(api, token string) (string, error) {
queries := requestQueries{
"access_token": token,
}
return encodeURL(api, queries)
}
// encodeURL add and encode parameters.
func encodeURL(api string, params requestQueries) (string, error) {
url, err := url.Parse(api)
if err != nil {
return "", err
}
query := url.Query()
for k, v := range params {
query.Set(k, v)
}
url.RawQuery = query.Encode()
return url.String(), nil
}
// randomString random string generator
//
// ln length of return string
func randomString(ln int) string {
letters := []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, ln)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := range b {
b[i] = letters[r.Intn(len(letters))]
}
return string(b)
}
// postJSON perform a HTTP/POST request with json body
func postJSON(url string, params interface{}, response interface{}) error {
resp, err := postJSONWithBody(url, params)
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(response)
}
func getJSON(url string, response interface{}) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(response)
}
// postJSONWithBody return with http body.
func postJSONWithBody(url string, params interface{}) (*http.Response, error) {
b := &bytes.Buffer{}
if params != nil {
enc := json.NewEncoder(b)
enc.SetEscapeHTML(false)
err := enc.Encode(params)
if err != nil {
return nil, err
}
}
return http.Post(url, "application/json; charset=utf-8", b)
}
func postFormByFile(url, field, filename string, response interface{}) error {
// Add your media file
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
return postForm(url, field, filename, file, response)
}
func postForm(url, field, filename string, reader io.Reader, response interface{}) error {
// Prepare a form that you will submit to that URL.
buf := new(bytes.Buffer)
w := multipart.NewWriter(buf)
fw, err := w.CreateFormFile(field, filename)
if err != nil {
return err
}
if _, err = io.Copy(fw, reader); err != nil {
return err
}
// Don't forget to close the multipart writer.
// If you don't close it, your request will be missing the terminating boundary.
w.Close()
// Now that you have a form, you can submit it to your handler.
req, err := http.NewRequest("POST", url, buf)
if err != nil {
return err
}
// Don't forget to set the content type, this will contain the boundary.
req.Header.Set("Content-Type", w.FormDataContentType())
// Submit the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return json.NewDecoder(resp.Body).Decode(response)
}
package wx
const (
// baseURL 微信请求基础URL
baseURL = "https://api.weixin.qq.com"
)
// POST 参数
type requestParams map[string]interface{}
// URL 参数
type requestQueries map[string]string
package wx
import (
"errors"
"fmt"
"github.com/medivhzhan/weapp/v2"
)
const (
apiLogin = "/sns/jscode2session"
apiGetAccessToken = "/cgi-bin/token"
apiGetPaidUnionID = "/wxa/getpaidunionid"
)
// LoginResponse 返回给用户的数据
type LoginResponse struct {
CommonError
OpenID string `json:"openid"`
SessionKey string `json:"session_key"`
// 用户在开放平台的唯一标识符 只在关注公众号的前提下
UnionID string `json:"unionid"`
Mobile string `json:"mobile"`
}
// Login 登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。
//
// appID 小程序 appID
// secret 小程序的 app secret
// code 小程序登录时获取的 code
func GetWxLoginRes(appID, secret, code, encryptedDate, iv string) (*LoginResponse, error) {
api := baseURL + apiLogin
loginResponse, err := getWxLoginRes(appID, secret, code, api)
if err != nil {
fmt.Println(111)
return nil, err
}
if loginResponse.CommonError.ErrCode != 0 {
fmt.Println(222)
fmt.Println(loginResponse.CommonError.ErrCode)
return nil, errors.New("code过期:" + loginResponse.CommonError.ErrMSG)
}
mobile, err := weapp.DecryptMobile(loginResponse.SessionKey, encryptedDate, iv)
if err != nil {
fmt.Println(333)
return nil, err
}
loginResponse.Mobile = mobile.PhoneNumber
return loginResponse, nil
}
func GetOpenId(appID, secret, code string) (string, error) {
api := baseURL + apiLogin
queries := requestQueries{
"appid": appID,
"secret": secret,
"js_code": code,
"grant_type": "authorization_code",
}
url, err := encodeURL(api, queries)
if err != nil {
return "", err
}
res := new(LoginResponse)
if err := getJSON(url, res); err != nil {
return "", err
}
return res.OpenID, nil
}
func getWxLoginRes(appID, secret, code, api string) (*LoginResponse, error) {
queries := requestQueries{
"appid": appID,
"secret": secret,
"js_code": code,
"grant_type": "authorization_code",
}
url, err := encodeURL(api, queries)
if err != nil {
return nil, err
}
res := new(LoginResponse)
if err := getJSON(url, res); err != nil {
return nil, err
}
return res, nil
}
// TokenResponse 获取 access_token 成功返回数据
type TokenResponse struct {
CommonError
AccessToken string `json:"access_token"` // 获取到的凭证
ExpiresIn uint `json:"expires_in"` // 凭证有效时间,单位:秒。目前是7200秒之内的值。
}
// GetAccessToken 获取小程序全局唯一后台接口调用凭据(access_token)。
// 调调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存,注意缓存。
func GetAccessToken(appID, secret string) (*TokenResponse, error) {
api := baseURL + apiGetAccessToken
return getAccessToken(appID, secret, api)
}
func getAccessToken(appID, secret, api string) (*TokenResponse, error) {
queries := requestQueries{
"appid": appID,
"secret": secret,
"grant_type": "client_credential",
}
url, err := encodeURL(api, queries)
if err != nil {
return nil, err
}
res := new(TokenResponse)
if err := getJSON(url, res); err != nil {
return nil, err
}
return res, nil
}
// GetPaidUnionIDResponse response data
type GetPaidUnionIDResponse struct {
CommonError
UnionID string `json:"unionid"`
}
// GetPaidUnionID 用户支付完成后,通过微信支付订单号(transaction_id)获取该用户的 UnionId,
func GetPaidUnionID(accessToken, openID, transactionID string) (*GetPaidUnionIDResponse, error) {
api := baseURL + apiGetPaidUnionID
return getPaidUnionID(accessToken, openID, transactionID, api)
}
func getPaidUnionID(accessToken, openID, transactionID, api string) (*GetPaidUnionIDResponse, error) {
queries := requestQueries{
"openid": openID,
"access_token": accessToken,
"transaction_id": transactionID,
}
return getPaidUnionIDRequest(api, queries)
}
// GetPaidUnionIDWithMCH 用户支付完成后,通过微信支付商户订单号和微信支付商户号(out_trade_no 及 mch_id)获取该用户的 UnionId,
func GetPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID string) (*GetPaidUnionIDResponse, error) {
api := baseURL + apiGetPaidUnionID
return getPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID, api)
}
func getPaidUnionIDWithMCH(accessToken, openID, outTradeNo, mchID, api string) (*GetPaidUnionIDResponse, error) {
queries := requestQueries{
"openid": openID,
"mch_id": mchID,
"out_trade_no": outTradeNo,
"access_token": accessToken,
}
return getPaidUnionIDRequest(api, queries)
}
func getPaidUnionIDRequest(api string, queries requestQueries) (*GetPaidUnionIDResponse, error) {
url, err := encodeURL(api, queries)
if err != nil {
return nil, err
}
res := new(GetPaidUnionIDResponse)
if err := getJSON(url, res); err != nil {
return nil, err
}
return res, nil
}
package wx
import (
"bytes"
"crypto/md5"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"encoding/xml"
"errors"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"sort"
"strings"
"time"
"golang.org/x/crypto/pkcs12"
)
type Client struct {
appId string //微信支付申请对应的公众号的APPID
apiKey string //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
mchId string //微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
Client *http.Client
tlsClient *http.Client
apiDomain string
NotifyURL string
isProduction bool
}
func New(appId, apiKey, mchId string, isProduction bool) (client *Client) {
client = &Client{}
client.appId = appId
client.mchId = mchId
client.apiKey = apiKey
client.Client = http.DefaultClient
client.isProduction = isProduction
if isProduction {
client.apiDomain = kProductionURL
} else {
client.apiDomain = kSandboxURL
}
return client
}
func initTLSClient(cert []byte, password string) (tlsClient *http.Client, err error) {
if len(cert) > 0 {
cert, err := pkcs12ToPem(cert, password)
if err != nil {
return nil, err
}
config := &tls.Config{
Certificates: []tls.Certificate{cert},
}
transport := &http.Transport{
TLSClientConfig: config,
DisableCompression: true,
}
tlsClient = &http.Client{Transport: transport}
}
return tlsClient, err
}
func (this *Client) LoadCert(path string) (err error) {
if len(path) == 0 {
return ErrNotFoundCertFile
}
cert, err := ioutil.ReadFile(path)
if err != nil {
return err
}
tlsClient, err := initTLSClient(cert, this.mchId)
if err != nil {
return err
}
this.tlsClient = tlsClient
return nil
}
func (this *Client) LoadCertFromBase64(cert string) (err error) {
if len(cert) == 0 {
return ErrNotFoundCertFile
}
p12, err := base64.StdEncoding.DecodeString(cert)
if err != nil {
return err
}
tlsClient, err := initTLSClient(p12, this.mchId)
if err != nil {
return err
}
this.tlsClient = tlsClient
return nil
}
func (this *Client) URLValues(param Param, key string) (value url.Values, err error) {
var p = param.Params()
if appId := p.Get("appid"); appId == "" {
p.Set("appid", this.appId)
}
p.Set("mch_id", this.mchId)
p.Set("nonce_str", GetNonceStr())
if _, ok := p["notify_url"]; ok == false {
if len(this.NotifyURL) > 0 {
p.Set("notify_url", this.NotifyURL)
}
}
var sign = SignMD5(p, key)
p.Set("sign", sign)
return p, nil
}
func (this *Client) doRequest(method, url string, param Param, result interface{}) (err error) {
_, err = this.doRequestWithClient(this.Client, method, url, param, result)
return err
}
func (this *Client) doRequestWithTLS(method, url string, param Param, result interface{}) (err error) {
if this.tlsClient == nil {
return ErrNotFoundTLSClient
}
_, err = this.doRequestWithClient(this.tlsClient, method, url, param, result)
return err
}
func (this *Client) doRequestWithClient(client *http.Client, method, url string, param Param, result interface{}) (body []byte, err error) {
key, err := this.getKey()
if err != nil {
return nil, err
}
p, err := this.URLValues(param, key)
if err != nil {
return nil, err
}
req, err := http.NewRequest(method, url, strings.NewReader(URLValueToXML(p)))
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/xml")
req.Header.Set("Content-Type", "application/xml;charset=utf-8")
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if _, err := VerifyResponseData(data, key); err != nil {
return nil, err
}
err = xml.Unmarshal(data, result)
return data, err
}
func (this *Client) DoRequest(method, url string, param Param, result interface{}) (err error) {
return this.doRequest(method, url, param, result)
}
func (this *Client) getKey() (key string, err error) {
if this.isProduction == false {
key, err = this.getSignKey(this.apiKey)
if err != nil {
return "", err
}
} else {
key = this.apiKey
}
return key, err
}
func (this *Client) SignMD5(param url.Values) (sign string) {
return SignMD5(param, this.apiKey)
}
func (this *Client) getSignKey(apiKey string) (key string, err error) {
var p = make(url.Values)
p.Set("mch_id", this.mchId)
p.Set("nonce_str", GetNonceStr())
var sign = SignMD5(p, apiKey)
p.Set("sign", sign)
req, err := http.NewRequest("POST", "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey", strings.NewReader(URLValueToXML(p)))
if err != nil {
return "", err
}
req.Header.Set("Accept", "application/xml")
req.Header.Set("Content-Type", "application/xml;charset=utf-8")
resp, err := this.Client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return "", err
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var signKey *GetSignKeyRsp
if err = xml.Unmarshal(data, &signKey); err != nil {
return "", err
}
return signKey.SandboxSignKey, nil
}
func (this *Client) BuildAPI(paths ...string) string {
var path = this.apiDomain
for _, p := range paths {
p = strings.TrimSpace(p)
if len(p) > 0 {
if strings.HasSuffix(path, "/") {
path = path + p
} else {
if strings.HasPrefix(p, "/") {
path = path + p
} else {
path = path + "/" + p
}
}
}
}
return path
}
func URLValueToXML(m url.Values) string {
var xmlBuffer = &bytes.Buffer{}
xmlBuffer.WriteString("<xml>")
for key := range m {
var value = m.Get(key)
if key == "total_fee" || key == "refund_fee" || key == "execute_time" {
xmlBuffer.WriteString("<" + key + ">" + value + "</" + key + ">")
} else {
xmlBuffer.WriteString("<" + key + "><![CDATA[" + value + "]]></" + key + ">")
}
}
xmlBuffer.WriteString("</xml>")
return xmlBuffer.String()
}
func SignMD5(param url.Values, key string) (sign string) {
var pList = make([]string, 0, 0)
for key := range param {
var value = param.Get(key)
if len(value) > 0 {
pList = append(pList, key+"="+value)
}
}
sort.Strings(pList)
if key != "" {
pList = append(pList, "key="+key)
}
var src = strings.Join(pList, "&")
md5Ctx := md5.New()
md5Ctx.Write([]byte(src))
cipherStr := md5Ctx.Sum(nil)
sign = strings.ToUpper(hex.EncodeToString(cipherStr))
return sign
}
func VerifyResponseData(data []byte, key string) (ok bool, err error) {
var param = make(XMLMap)
err = xml.Unmarshal(data, &param)
if err != nil {
return false, err
}
return VerifyResponseValues(url.Values(param), key)
}
func VerifyResponseValues(param url.Values, key string) (bool, error) {
// 处理错误信息
var code = param.Get("return_code")
if code != ReturnCodeSuccess {
var msg = param.Get("return_msg")
if msg == "" {
msg = param.Get("retmsg")
}
return false, errors.New(msg)
}
code = param.Get("result_code")
if code != ReturnCodeSuccess {
var msg = param.Get("err_code_des")
return false, errors.New(msg)
}
// 验证签名
var sign = param.Get("sign")
delete(param, "sign")
if sign == "" {
return false, errors.New("签名验证失败")
}
var sign2 = SignMD5(param, key)
if sign == sign2 {
return true, nil
}
return false, errors.New("签名验证失败")
}
func GetNonceStr() (nonceStr string) {
chars := "abcdefghijklmnopqrstuvwxyz0123456789"
var r = rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < 32; i++ {
idx := r.Intn(len(chars) - 1)
nonceStr += chars[idx : idx+1]
}
return nonceStr
}
func pkcs12ToPem(p12 []byte, password string) (cert tls.Certificate, err error) {
blocks, err := pkcs12.ToPEM(p12, password)
if err != nil {
return cert, err
}
var pemData []byte
for _, b := range blocks {
pemData = append(pemData, pem.EncodeToMemory(b)...)
}
cert, err = tls.X509KeyPair(pemData, pemData)
return cert, err
}
package wx
import (
"encoding/xml"
"errors"
"io"
"net/url"
)
const (
kSandboxURL = "https://api.mch.weixin.qq.com/sandboxnew"
kProductionURL = "https://api.mch.weixin.qq.com"
)
const (
ReturnCodeFail = "FAIL"
ReturnCodeSuccess = "SUCCESS"
)
const (
SignTypeMD5 = "MD5"
)
var (
ErrNotFoundCertFile = errors.New("wxpay: not found cert file")
ErrNotFoundTLSClient = errors.New("wxpay: not found tls client")
)
type Param interface {
// 返回参数列表
Params() url.Values
}
type XMLMap url.Values
type xmlMapEntry struct {
XMLName xml.Name
Value string `xml:",chardata"`
}
func (m XMLMap) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
for {
var e xmlMapEntry
err := d.Decode(&e)
if err == io.EOF {
break
} else if err != nil {
return err
}
(m)[e.XMLName.Local] = []string{e.Value}
}
return nil
}
func (v XMLMap) Get(key string) string {
if v == nil {
return ""
}
vs := v[key]
if len(vs) == 0 {
return ""
}
return vs[0]
}
func (v XMLMap) Set(key, value string) {
v[key] = []string{value}
}
func (v XMLMap) Add(key, value string) {
v[key] = append(v[key], value)
}
func (v XMLMap) Del(key string) {
delete(v, key)
}
type GetSignKeyParam struct {
MchId string
}
func (this *GetSignKeyParam) Params() url.Values {
var m = make(url.Values)
m.Set("mch_id", this.MchId)
return m
}
type GetSignKeyRsp struct {
ReturnCode string `xml:"return_code"`
ReturnMsg string `xml:"return_msg"`
SandboxSignKey string `xml:"sandbox_signkey"`
}
...@@ -30,4 +30,5 @@ require ( ...@@ -30,4 +30,5 @@ require (
gorm.io/driver/postgres v1.0.6-0.20201208020313-1ed927cfab53 gorm.io/driver/postgres v1.0.6-0.20201208020313-1ed927cfab53
gorm.io/driver/sqlite v1.1.5-0.20201206014648-c84401fbe3ba gorm.io/driver/sqlite v1.1.5-0.20201206014648-c84401fbe3ba
gorm.io/gorm v1.21.11 gorm.io/gorm v1.21.11
github.com/medivhzhan/weapp/v2 v2.4.2
) )
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