commit b67f241d61843ae5d381254863b854410b7f9f13 Author: 李光春 Date: Fri Jun 17 11:32:09 2022 +0800 - init diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..c56c479 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,17 @@ +kind: pipeline +type: docker +name: clone + +steps: + - name: Test + image: golang:1.18 + commands: + - go env -w GO111MODULE=on + - go env -w GOPROXY=https://goproxy.cn,direct + - go test -v ./... + - name: Benchmark + image: golang:1.18 + commands: + - go env -w GO111MODULE=on + - go env -w GOPROXY=https://goproxy.cn,direct + - go test -bench=. -benchmem \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9ea21f --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.env +.git +.svn +.idea +.vscode +*.log +goinit.sh +gomod.sh +*_test.go \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..11d74ea --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +

+Golang WeChatPayOpen +

+ +📦 Golang WeChatPayOpen + +[comment]: <> (go) +[![godoc](https://pkg.go.dev/badge/go.dtapp.net/wechatpayopen?status.svg)](https://pkg.go.dev/go.dtapp.net/wechatpayopen) +[![goproxy.cn](https://goproxy.cn/stats/go.dtapp.net/wechatpayopen/badges/download-count.svg)](https://goproxy.cn/stats/go.dtapp.net/wechatpayopen) +[![goreportcard.com](https://goreportcard.com/badge/go.dtapp.net/wechatpayopen)](https://goreportcard.com/report/go.dtapp.net/wechatpayopen) +[![deps.dev](https://img.shields.io/badge/deps-go-red.svg)](https://deps.dev/go/go.dtapp.net/wechatpayopen) + +#### 安装使用 + +```go +go get -v -u go.dtapp.net/wechatpayopen +``` + +#### 导入 + +```go +import ( + "go.dtapp.net/wechatpayopen" +) +``` \ No newline at end of file diff --git a/certificates.go b/certificates.go new file mode 100644 index 0000000..fc203d3 --- /dev/null +++ b/certificates.go @@ -0,0 +1,47 @@ +package wechatpayopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "net/http" + "time" +) + +type CertificatesResponse struct { + Data []struct { + EffectiveTime time.Time `json:"effective_time"` // 过期时间 + EncryptCertificate struct { + Algorithm string `json:"algorithm"` + AssociatedData string `json:"associated_data"` + Ciphertext string `json:"ciphertext"` + Nonce string `json:"nonce"` + } `json:"encrypt_certificate"` // 加密证书 + ExpireTime time.Time `json:"expire_time"` // 有效时间 + SerialNo string `json:"serial_no"` // 序列号 + } `json:"data"` +} + +type CertificatesResult struct { + Result CertificatesResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewCertificatesResult(result CertificatesResponse, body []byte, http gorequest.Response, err error) *CertificatesResult { + return &CertificatesResult{Result: result, Body: body, Http: http, Err: err} +} + +// Certificates 获取平台证书列表 +// https://pay.weixin.qq.com/wiki/doc/apiv3/apis/wechatpay5_1.shtml +func (app *App) Certificates() *CertificatesResult { + // 请求 + request, err := app.request("https://api.mch.weixin.qq.com/v3/certificates", map[string]interface{}{}, http.MethodGet) + if err != nil { + return NewCertificatesResult(CertificatesResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response CertificatesResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewCertificatesResult(response, request.ResponseBody, request, err) +} diff --git a/const.go b/const.go new file mode 100644 index 0000000..1677eb1 --- /dev/null +++ b/const.go @@ -0,0 +1,63 @@ +package wechatpayopen + +import "time" + +// 微信支付 API 地址 +const ( + WechatPayAPIServer = "https://api.mch.weixin.qq.com" // 微信支付 API 地址 + WechatPayAPIServerBackup = "https://api2.mch.weixin.qq.com" // 微信支付 API 备份地址 +) + +// SDK 相关信息 +const ( + Version = "0.2.11" // SDK 版本 + UserAgentFormat = "WechatPay-Go/%s (%s) GO/%s" // UserAgent中的信息 +) + +// HTTP 请求报文 Header 相关常量 +const ( + Authorization = "Authorization" // Header 中的 Authorization 字段 + Accept = "Accept" // Header 中的 Accept 字段 + ContentType = "Content-Type" // Header 中的 ContentType 字段 + ContentLength = "Content-Length" // Header 中的 ContentLength 字段 + UserAgent = "User-Agent" // Header 中的 UserAgent 字段 +) + +// 常用 ContentType +const ( + ApplicationJSON = "application/json" + ImageJPG = "image/jpg" + ImagePNG = "image/png" + VideoMP4 = "video/mp4" +) + +// 请求报文签名相关常量 +const ( + SignatureMessageFormat = "%s\n%s\n%d\n%s\n%s\n" // 数字签名原文格式 + // HeaderAuthorizationFormat 请求头中的 Authorization 拼接格式 + HeaderAuthorizationFormat = "%s mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"" +) + +// HTTP 应答报文 Header 相关常量 +const ( + WechatPayTimestamp = "Wechatpay-Timestamp" // 微信支付回包时间戳 + WechatPayNonce = "Wechatpay-Nonce" // 微信支付回包随机字符串 + WechatPaySignature = "Wechatpay-Signature" // 微信支付回包签名信息 + WechatPaySerial = "Wechatpay-Serial" // 微信支付回包平台序列号 + RequestID = "Request-Id" // 微信支付回包请求ID +) + +// 时间相关常量 +const ( + FiveMinute = 5 * 60 // 回包校验最长时间(秒) + DefaultTimeout = 30 * time.Second // HTTP 请求默认超时时间 +) + +func getAuthorizationType() string { + return "WECHATPAY2-" + algorithm() +} + +// 返回使用的签名算法:SHA256-RSA2048 +func algorithm() string { + return "SHA256-RSA2048" +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e921ba7 --- /dev/null +++ b/go.mod @@ -0,0 +1,32 @@ +module go.dtapp.net/wechatpayopen + +go 1.18 + +require ( + go.dtapp.net/gojson v1.0.0 + go.dtapp.net/golog v1.0.14 + go.dtapp.net/gorandom v1.0.0 + go.dtapp.net/gorequest v1.0.20 + gorm.io/datatypes v1.0.6 + gorm.io/gorm v1.23.6 +) + +require ( + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect + github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect + github.com/ulikunitz/xz v0.5.10 // indirect + go.dtapp.net/goip v1.0.18 // indirect + go.dtapp.net/gostring v1.0.3 // indirect + go.dtapp.net/gotime v1.0.2 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.21.0 // indirect + golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d // indirect + golang.org/x/text v0.3.7 // indirect + gorm.io/driver/mysql v1.3.4 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..435977d --- /dev/null +++ b/go.sum @@ -0,0 +1,276 @@ +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= +github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA= +github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= +github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= +github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw= +github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0= +github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda h1:h+YpzUB/bGVJcLqW+d5GghcCmE/A25KbzjXvWJQi/+o= +github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda/go.mod h1:MSotTrCv1PwoR8QgU1JurEx+lNNbtr25I+m0zbLyAGw= +github.com/saracen/go7z-fixtures v0.0.0-20190623165746-aa6b8fba1d2f h1:PF9WV5j/x6MT+x/sauUHd4objCvJbZb0wdxZkHSdd5A= +github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f h1:1cJITU3JUI8qNS5T0BlXwANsVdyoJQHQ4hvOxbunPCw= +github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f/go.mod h1:LyBTue+RWeyIfN3ZJ4wVxvDuvlGJtDgCLgCb6HCPgps= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= +github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.dtapp.net/goip v1.0.18 h1:wm+6Agx9LeChtUS44gLsYWVOYTReKxni53m6jgzTstI= +go.dtapp.net/goip v1.0.18/go.mod h1:Y2KCQK4NW0cDJyynSCG/Yj4yHugH+Ayu0KNjdl43wYc= +go.dtapp.net/gojson v1.0.0 h1:jmRjeWChRyv2tKEByHvnW3kXh1jUcL8B7VurV0Zbygc= +go.dtapp.net/gojson v1.0.0/go.mod h1:TkkpTNxHBKxul0e7gC5MrL1K4ICFB9mQ7wHzjBah3/k= +go.dtapp.net/golog v1.0.14 h1:LlJyb/wfCZUoDevzBvymuCp8ympuGJxybjeRb8f3EGU= +go.dtapp.net/golog v1.0.14/go.mod h1:gTt8U/iiZuZ0BhV7Ic84U0Phep6OOYCnVGL7DE1dOo4= +go.dtapp.net/gorandom v1.0.0 h1:55cXi0SE/nk+Z8z9Gut8uz0pF612R0L51LRi25a6Pfk= +go.dtapp.net/gorandom v1.0.0/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8= +go.dtapp.net/gorequest v1.0.20 h1:rddlXqcabDQNUb+iBvJthpNyD0Bv1yLbYftK+CSwSMA= +go.dtapp.net/gorequest v1.0.20/go.mod h1:EwOfdfxsWPszOWrphCWHTN4DbYtU6fyQ/fuWQyQwSnk= +go.dtapp.net/gostring v1.0.3 h1:KSOq4D77/g5yZN/bqWfZ0kOOaPr/P1240vg03+XdENI= +go.dtapp.net/gostring v1.0.3/go.mod h1:+ggrOvgQDQturi1QGsXEpyRN/ZPoRDaqhMujIk5lrgQ= +go.dtapp.net/gotime v1.0.2 h1:CFIJHQXC/4t9bsJhk2cLhjHd6rpdPcJXr8BcHKHDuQo= +go.dtapp.net/gotime v1.0.2/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= +go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d h1:Zu/JngovGLVi6t2J3nmAf3AoTDwuzw85YZ3b9o4yU7s= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/datatypes v1.0.6 h1:3cqbakp1DIgC+P7wyODb5k+lSjW8g3mjkg/BIsmhjlE= +gorm.io/datatypes v1.0.6/go.mod h1:Gh/Xd/iUWWybMEk8CzYCK/swqlni2r+ROeM1HGIM0ck= +gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= +gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q= +gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE= +gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU= +gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ= +gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk= +gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= +gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI= +gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= +gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.2/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0= +gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/pay.jsapi.go b/pay.jsapi.go new file mode 100644 index 0000000..98add24 --- /dev/null +++ b/pay.jsapi.go @@ -0,0 +1,48 @@ +package wechatpayopen + +import ( + "fmt" + "go.dtapp.net/gorandom" + "time" +) + +// GetJsApi 入参 +type GetJsApi struct { + Package string `json:"package"` +} + +// GetJsApiResult 返回参数 +type GetJsApiResult struct { + AppId string `json:"app_id"` // 应用ID + TimeStamp string `json:"time_stamp"` // 时间戳 + NonceStr string `json:"nonce_str"` // 随机字符串 + Package string `json:"package"` // 订单详情扩展字符串 + SignType string `json:"sign_type"` // 签名方式 + PaySign string `json:"pay_sign"` // 签名 +} + +// GetJsApi JSAPI调起支付API https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml +func (app *App) GetJsApi(param GetJsApi) (result GetJsApiResult, err error) { + + // sign params + timeStamp := time.Now().Unix() + nonce := gorandom.Alphanumeric(32) + + result.AppId = app.subAppid + result.TimeStamp = fmt.Sprintf("%v", timeStamp) // 时间戳 + result.NonceStr = nonce // 随机字符串 + result.Package = param.Package // 订单详情扩展字符串 + + // 签名 + message := fmt.Sprintf("%s\n%s\n%s\n%s\n", app.subAppid, fmt.Sprintf("%v", timeStamp), nonce, param.Package) + + signBytes, err := app.signPKCS1v15(message, []byte(app.mchSslKey)) + if err != nil { + return result, err + } + + sign := app.base64EncodeStr(signBytes) + result.PaySign = sign // 签名 + result.SignType = "RSA" // 签名方式 + return result, nil +} diff --git a/pay.partner.transactions.id.go b/pay.partner.transactions.id.go new file mode 100644 index 0000000..bde41bc --- /dev/null +++ b/pay.partner.transactions.id.go @@ -0,0 +1,80 @@ +package wechatpayopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type PayPartnerTransactionsIdResponse struct { + Appid string `json:"appid"` + Mchid string `json:"mchid"` + OutTradeNo string `json:"out_trade_no"` + TransactionId string `json:"transaction_id,omitempty"` + TradeType string `json:"trade_type,omitempty"` + TradeState string `json:"trade_state"` + TradeStateDesc string `json:"trade_state_desc"` + BankType string `json:"bank_type,omitempty"` + Attach string `json:"attach,omitempty"` + SuccessTime string `json:"success_time,omitempty"` + Payer struct { + Openid string `json:"openid"` + } `json:"payer"` + Amount struct { + Total int `json:"total,omitempty"` + PayerTotal int `json:"payer_total,omitempty"` + Currency string `json:"currency,omitempty"` + PayerCurrency string `json:"payer_currency,omitempty"` + } `json:"amount,omitempty"` + SceneInfo struct { + DeviceId string `json:"device_id,omitempty"` + } + PromotionDetail []struct { + CouponId string `json:"coupon_id"` + Name string `json:"name,omitempty"` + Scope string `json:"scope,omitempty"` + Type string `json:"type,omitempty"` + Amount int `json:"amount"` + StockId string `json:"stock_id,omitempty"` + WechatpayContribute int `json:"wechatpay_contribute,omitempty"` + MerchantContribute int `json:"merchant_contribute,omitempty"` + OtherContribute int `json:"other_contribute,omitempty"` + Currency string `json:"currency,omitempty"` + GoodsDetail []struct { + GoodsId string `json:"goods_id"` + Quantity int `json:"quantity"` + UnitPrice int `json:"unit_price"` + DiscountAmount int `json:"discount_amount"` + GoodsRemark string `json:"goods_remark,omitempty"` + } `json:"goods_detail"` + } +} + +type PayPartnerTransactionsIdResult struct { + Result PayPartnerTransactionsIdResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewPayPartnerTransactionsIdResult(result PayPartnerTransactionsIdResponse, body []byte, http gorequest.Response, err error) *PayPartnerTransactionsIdResult { + return &PayPartnerTransactionsIdResult{Result: result, Body: body, Http: http, Err: err} +} + +// PayPartnerTransactionsId 微信支付订单号查询 +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_2.shtml +func (app *App) PayPartnerTransactionsId(transactionId string) *PayPartnerTransactionsIdResult { + // 参数 + params := gorequest.NewParams() + // 请求 + // 请求 + request, err := app.request(fmt.Sprintf("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/%s?sp_mchid=%s&sub_mchid=%s", transactionId, app.spMchId, app.subMchId), params, http.MethodGet) + if err != nil { + return NewPayPartnerTransactionsIdResult(PayPartnerTransactionsIdResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response PayPartnerTransactionsIdResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewPayPartnerTransactionsIdResult(response, request.ResponseBody, request, err) +} diff --git a/pay.partner.transactions.jsapi.go b/pay.partner.transactions.jsapi.go new file mode 100644 index 0000000..59bd31f --- /dev/null +++ b/pay.partner.transactions.jsapi.go @@ -0,0 +1,42 @@ +package wechatpayopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "net/http" +) + +type PayPartnerTransactionsJsapiResponse struct { + PrepayId string `json:"prepay_id"` +} + +type PayPartnerTransactionsJsapiResult struct { + Result PayPartnerTransactionsJsapiResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewPayPartnerTransactionsJsapiResult(result PayPartnerTransactionsJsapiResponse, body []byte, http gorequest.Response, err error) *PayPartnerTransactionsJsapiResult { + return &PayPartnerTransactionsJsapiResult{Result: result, Body: body, Http: http, Err: err} +} + +// PayPartnerTransactionsJsapi JSAPI下单 +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_1.shtml +func (app *App) PayPartnerTransactionsJsapi(notMustParams ...gorequest.Params) *PayPartnerTransactionsJsapiResult { + // 参数 + params := gorequest.NewParamsWith(notMustParams...) + params.Set("sp_appid", app.spAppid) // 服务商应用ID + params.Set("sp_mchid", app.spMchId) // 服务商户号 + params.Set("sub_appid", app.subAppid) // 子商户应用ID + params.Set("sub_mchid", app.subMchId) // 子商户号 + // 请求 + request, err := app.request("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi", params, http.MethodPost) + if err != nil { + return NewPayPartnerTransactionsJsapiResult(PayPartnerTransactionsJsapiResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response PayPartnerTransactionsJsapiResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewPayPartnerTransactionsJsapiResult(response, request.ResponseBody, request, err) +} diff --git a/pay.partner.transactions.out-trade-no.close.go b/pay.partner.transactions.out-trade-no.close.go new file mode 100644 index 0000000..00a3352 --- /dev/null +++ b/pay.partner.transactions.out-trade-no.close.go @@ -0,0 +1,32 @@ +package wechatpayopen + +import ( + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type PayPartnerTransactionsOutTradeNoCloseResult struct { + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewPayPartnerTransactionsOutTradeNoCloseResult(body []byte, http gorequest.Response, err error) *PayPartnerTransactionsOutTradeNoCloseResult { + return &PayPartnerTransactionsOutTradeNoCloseResult{Body: body, Http: http, Err: err} +} + +// PayPartnerTransactionsOutTradeNoClose 关闭订单API +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_3.shtml +func (app *App) PayPartnerTransactionsOutTradeNoClose(outTradeNo string) *PayPartnerTransactionsOutTradeNoCloseResult { + // 参数 + params := gorequest.NewParams() + params.Set("sp_mchid", app.spMchId) // 服务商户号 + params.Set("sub_mchid", app.subMchId) // 子商户号 + // 请求 + request, err := app.request(fmt.Sprintf("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/%s/close", outTradeNo), params, http.MethodPost) + if err != nil { + return NewPayPartnerTransactionsOutTradeNoCloseResult(request.ResponseBody, request, err) + } + return NewPayPartnerTransactionsOutTradeNoCloseResult(request.ResponseBody, request, err) +} diff --git a/pay.partner.transactions.out-trade-no.go b/pay.partner.transactions.out-trade-no.go new file mode 100644 index 0000000..f5b5798 --- /dev/null +++ b/pay.partner.transactions.out-trade-no.go @@ -0,0 +1,82 @@ +package wechatpayopen + +import ( + "encoding/json" + "fmt" + "go.dtapp.net/gorequest" + "net/http" +) + +type PayPartnerTransactionsOutTradeNoResponse struct { + SpAppid string `json:"sp_appid"` // 服务商应用ID + SpMchid string `json:"sp_mchid"` // 服务商户号 + SubAppid string `json:"sub_appid"` // 子商户应用ID + SubMchid string `json:"sub_mchid"` // 子商户号 + OutTradeNo string `json:"out_trade_no"` // 商户订单号 + TransactionId string `json:"transaction_id"` // 微信支付订单号 + TradeType string `json:"trade_type"` // 交易类型 + TradeState string `json:"trade_state"` // 交易状态 + TradeStateDesc string `json:"trade_state_desc"` // 交易状态描述 + BankType string `json:"bank_type"` // 付款银行 + Attach string `json:"attach"` // 附加数据 + SuccessTime string `json:"success_time"` // 支付完成时间 + Payer struct { + SpOpenid string `json:"sp_openid"` // 用户服务标识 + SubOpenid string `json:"sub_openid"` // 用户子标识 + } `json:"payer"` // 支付者 + Amount struct { + Total int `json:"total"` // 总金额 + PayerTotal int `json:"payer_total"` // 用户支付金额 + Currency string `json:"currency"` // 货币类型 + PayerCurrency string `json:"payer_currency"` // 用户支付币种 + } `json:"amount"` // 订单金额 + SceneInfo struct { + DeviceId string `json:"device_id"` // 商户端设备号 + } `json:"scene_info"` // 场景信息 + PromotionDetail []struct { + CouponId string `json:"coupon_id"` // 券ID + Name string `json:"name"` // 优惠名称 + Scope string `json:"scope"` // 优惠范围 + Type string `json:"type"` // 优惠类型 + Amount int `json:"amount"` // 优惠券面额 + StockId string `json:"stock_id"` // 活动ID + WechatpayContribute int `json:"wechatpay_contribute"` // 微信出资 + MerchantContribute int `json:"merchant_contribute"` // 商户出资 + OtherContribute int `json:"other_contribute"` // 其他出资 + Currency string `json:"currency"` // 优惠币种 + GoodsDetail []struct { + GoodsId string `json:"goods_id"` // 商品编码 + Quantity int `json:"quantity"` // 商品数量 + UnitPrice int `json:"unit_price"` // 商品单价 + DiscountAmount int `json:"discount_amount"` // 商品优惠金额 + GoodsRemark string `json:"goods_remark"` // 商品备注 + } `json:"goods_detail"` // 单品列表 + } `json:"promotion_detail"` // 优惠功能 +} + +type PayPartnerTransactionsOutTradeNoResult struct { + Result PayPartnerTransactionsOutTradeNoResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewPayPartnerTransactionsOutTradeNoResult(result PayPartnerTransactionsOutTradeNoResponse, body []byte, http gorequest.Response, err error) *PayPartnerTransactionsOutTradeNoResult { + return &PayPartnerTransactionsOutTradeNoResult{Result: result, Body: body, Http: http, Err: err} +} + +// PayPartnerTransactionsOutTradeNo 商户订单号查询 +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_2.shtml +func (app *App) PayPartnerTransactionsOutTradeNo(outTradeNo string) *PayPartnerTransactionsOutTradeNoResult { + // 参数 + params := gorequest.NewParams() + // 请求 + request, err := app.request(fmt.Sprintf("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/%s?sp_mchid=%s&sub_mchid=%s", outTradeNo, app.spMchId, app.subMchId), params, http.MethodGet) + if err != nil { + return NewPayPartnerTransactionsOutTradeNoResult(PayPartnerTransactionsOutTradeNoResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response PayPartnerTransactionsOutTradeNoResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewPayPartnerTransactionsOutTradeNoResult(response, request.ResponseBody, request, err) +} diff --git a/pgsql.go b/pgsql.go new file mode 100644 index 0000000..55d175c --- /dev/null +++ b/pgsql.go @@ -0,0 +1,26 @@ +package wechatpayopen + +import ( + "go.dtapp.net/gojson" + "go.dtapp.net/golog" + "go.dtapp.net/gorequest" + "gorm.io/datatypes" +) + +// 记录日志 +func (app *App) postgresqlLog(request gorequest.Response) { + app.log.Record(golog.ApiPostgresqlLog{ + RequestTime: golog.TimeString{Time: request.RequestTime}, //【请求】时间 + RequestUri: request.RequestUri, //【请求】链接 + RequestUrl: gorequest.UriParse(request.RequestUri).Url, //【请求】链接 + RequestApi: gorequest.UriParse(request.RequestUri).Path, //【请求】接口 + RequestMethod: request.RequestMethod, //【请求】方式 + RequestParams: datatypes.JSON(gojson.JsonEncodeNoError(request.RequestParams)), //【请求】参数 + RequestHeader: datatypes.JSON(gojson.JsonEncodeNoError(request.RequestHeader)), //【返回】头部 + ResponseHeader: datatypes.JSON(gojson.JsonEncodeNoError(request.ResponseHeader)), //【返回】头部 + ResponseStatusCode: request.ResponseStatusCode, //【返回】状态码 + ResponseBody: request.ResponseBody, //【返回】内容 + ResponseContentLength: request.ResponseContentLength, //【返回】大小 + ResponseTime: golog.TimeString{Time: request.ResponseTime}, //【返回】时间 + }) +} diff --git a/refund.domestic.refunds.go b/refund.domestic.refunds.go new file mode 100644 index 0000000..6095c97 --- /dev/null +++ b/refund.domestic.refunds.go @@ -0,0 +1,78 @@ +package wechatpayopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "net/http" + "time" +) + +type RefundDomesticRefundsResponse struct { + RefundId string `json:"refund_id"` + OutRefundNo string `json:"out_refund_no"` + TransactionId string `json:"transaction_id"` + OutTradeNo string `json:"out_trade_no"` + Channel string `json:"channel"` + UserReceivedAccount string `json:"user_received_account"` + SuccessTime time.Time `json:"success_time"` + CreateTime time.Time `json:"create_time"` + Status string `json:"status"` + FundsAccount string `json:"funds_account"` + Amount struct { + Total int `json:"total"` + Refund int `json:"refund"` + From []struct { + Account string `json:"account"` + Amount int `json:"amount"` + } `json:"from"` + PayerTotal int `json:"payer_total"` + PayerRefund int `json:"payer_refund"` + SettlementRefund int `json:"settlement_refund"` + SettlementTotal int `json:"settlement_total"` + DiscountRefund int `json:"discount_refund"` + Currency string `json:"currency"` + } `json:"amount"` + PromotionDetail []struct { + PromotionId string `json:"promotion_id"` + Scope string `json:"scope"` + Type string `json:"type"` + Amount int `json:"amount"` + RefundAmount int `json:"refund_amount"` + GoodsDetail struct { + MerchantGoodsId string `json:"merchant_goods_id"` + WechatpayGoodsId string `json:"wechatpay_goods_id"` + GoodsName string `json:"goods_name"` + UnitPrice int `json:"unit_price"` + RefundAmount int `json:"refund_amount"` + RefundQuantity int `json:"refund_quantity"` + } `json:"goods_detail"` + } `json:"promotion_detail"` +} + +type RefundDomesticRefundsResult struct { + Result RefundDomesticRefundsResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewRefundDomesticRefundsResult(result RefundDomesticRefundsResponse, body []byte, http gorequest.Response, err error) *RefundDomesticRefundsResult { + return &RefundDomesticRefundsResult{Result: result, Body: body, Http: http, Err: err} +} + +// RefundDomesticRefunds 申请退款API +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_9.shtml +func (app *App) RefundDomesticRefunds(notMustParams ...gorequest.Params) *RefundDomesticRefundsResult { + // 参数 + params := gorequest.NewParamsWith(notMustParams...) + params.Set("sub_mchid", app.subMchId) // 子商户号 + // 请求 + request, err := app.request("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds", params, http.MethodPost) + if err != nil { + return NewRefundDomesticRefundsResult(RefundDomesticRefundsResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response RefundDomesticRefundsResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewRefundDomesticRefundsResult(response, request.ResponseBody, request, err) +} diff --git a/refund.domestic.refunds.out_refund_no.go b/refund.domestic.refunds.out_refund_no.go new file mode 100644 index 0000000..ed495f1 --- /dev/null +++ b/refund.domestic.refunds.out_refund_no.go @@ -0,0 +1,77 @@ +package wechatpayopen + +import ( + "encoding/json" + "go.dtapp.net/gorequest" + "net/http" + "time" +) + +type RefundDomesticRefundsOutRefundNoResponse struct { + RefundId string `json:"refund_id"` + OutRefundNo string `json:"out_refund_no"` + TransactionId string `json:"transaction_id"` + OutTradeNo string `json:"out_trade_no"` + Channel string `json:"channel"` + UserReceivedAccount string `json:"user_received_account"` + SuccessTime string `json:"success_time"` + CreateTime time.Time `json:"create_time"` + Status string `json:"status"` + FundsAccount string `json:"funds_account"` + Amount struct { + Total int `json:"total"` + Refund int `json:"refund"` + From []struct { + Account string `json:"account"` + Amount int `json:"amount"` + } `json:"from"` + PayerTotal int `json:"payer_total"` + PayerRefund int `json:"payer_refund"` + SettlementRefund int `json:"settlement_refund"` + SettlementTotal int `json:"settlement_total"` + DiscountRefund int `json:"discount_refund"` + Currency string `json:"currency"` + } `json:"amount"` + PromotionDetail []struct { + PromotionId string `json:"promotion_id"` + Scope string `json:"scope"` + Type string `json:"type"` + Amount int `json:"amount"` + RefundAmount int `json:"refund_amount"` + GoodsDetail struct { + MerchantGoodsId string `json:"merchant_goods_id"` + WechatpayGoodsId string `json:"wechatpay_goods_id"` + GoodsName string `json:"goods_name"` + UnitPrice int `json:"unit_price"` + RefundAmount int `json:"refund_amount"` + RefundQuantity int `json:"refund_quantity"` + } `json:"goods_detail"` + } `json:"promotion_detail"` +} + +type RefundDomesticRefundsOutRefundNoResult struct { + Result RefundDomesticRefundsOutRefundNoResponse // 结果 + Body []byte // 内容 + Http gorequest.Response // 请求 + Err error // 错误 +} + +func NewRefundDomesticRefundsOutRefundNoResult(result RefundDomesticRefundsOutRefundNoResponse, body []byte, http gorequest.Response, err error) *RefundDomesticRefundsOutRefundNoResult { + return &RefundDomesticRefundsOutRefundNoResult{Result: result, Body: body, Http: http, Err: err} +} + +// RefundDomesticRefundsOutRefundNo 查询单笔退款API +// https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_9.shtml +func (app *App) RefundDomesticRefundsOutRefundNo(outRefundNo string) *RefundDomesticRefundsOutRefundNoResult { + // 参数 + params := gorequest.NewParams() + // 请求 + request, err := app.request("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/"+outRefundNo+"?sub_mchid="+app.subMchId, params, http.MethodGet) + if err != nil { + return NewRefundDomesticRefundsOutRefundNoResult(RefundDomesticRefundsOutRefundNoResponse{}, request.ResponseBody, request, err) + } + // 定义 + var response RefundDomesticRefundsOutRefundNoResponse + err = json.Unmarshal(request.ResponseBody, &response) + return NewRefundDomesticRefundsOutRefundNoResult(response, request.ResponseBody, request, err) +} diff --git a/sign.go b/sign.go new file mode 100644 index 0000000..4d32900 --- /dev/null +++ b/sign.go @@ -0,0 +1,164 @@ +package wechatpayopen + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/json" + "encoding/pem" + "errors" + "fmt" + "go.dtapp.net/gorandom" + "net/url" + "time" +) + +// 对消息的散列值进行数字签名 +func (app *App) signPKCS1v15(msg string, privateKey []byte) ([]byte, error) { + + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, errors.New("private key decode error") + } + + pri, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, errors.New("parse private key error") + } + + key, ok := pri.(*rsa.PrivateKey) + if ok == false { + return nil, errors.New("private key format error") + } + + sign, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, app.haSha256(msg)) + if err != nil { + return nil, errors.New("sign error") + } + + return sign, nil +} + +// base编码 +func (app *App) base64EncodeStr(src []byte) string { + return base64.StdEncoding.EncodeToString(src) +} + +// sha256加密 +func (app *App) haSha256(str string) []byte { + h := sha256.New() + h.Write([]byte(str)) + return h.Sum(nil) +} + +// 生成身份认证信息 +// https://wechatpay-api.gitbook.io/wechatpay-api-v3/qian-ming-zhi-nan-1/qian-ming-sheng-cheng +func (app *App) authorization(method string, paramMap map[string]interface{}, rawUrl string) (token string, err error) { + + // 请求报文主体 + var signBody string + if len(paramMap) != 0 { + paramJsonBytes, err := json.Marshal(paramMap) + if err != nil { + return token, err + } + signBody = string(paramJsonBytes) + } + + // URL + urlPart, err := url.Parse(rawUrl) + if err != nil { + return token, err + } + canonicalUrl := urlPart.RequestURI() + + // 请求时间戳 + timestamp := time.Now().Unix() + + // 请求随机串 + nonce := gorandom.Alphanumeric(32) + + // 构造签名串 + message := fmt.Sprintf(SignatureMessageFormat, method, canonicalUrl, timestamp, nonce, signBody) + + sign, err := app.signSHA256WithRSA(message, app.getRsa([]byte(app.mchSslKey))) + + if err != nil { + return token, err + } + + authorization := fmt.Sprintf( + HeaderAuthorizationFormat, getAuthorizationType(), + app.spMchId, nonce, timestamp, app.mchSslSerialNo, sign, + ) + + return authorization, nil +} + +// 报文解密 +func (app *App) decryptGCM(aesKey, nonceV, ciphertextV, additionalDataV string) ([]byte, error) { + key := []byte(aesKey) + nonce := []byte(nonceV) + additionalData := []byte(additionalDataV) + ciphertext, err := base64.StdEncoding.DecodeString(ciphertextV) + if err != nil { + return nil, err + } + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + plaintext, err := aesGCM.Open(nil, nonce, ciphertext, additionalData) + if err != nil { + return nil, err + } + return plaintext, err +} + +// 对消息的散列值进行数字签名 +func (app *App) getRsa(privateKey []byte) *rsa.PrivateKey { + + block, _ := pem.Decode(privateKey) + if block == nil { + return nil + } + + pri, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil + } + + key, ok := pri.(*rsa.PrivateKey) + if ok == false { + return key + } + + return key +} + +// 通过私钥对字符串以 SHA256WithRSA 算法生成签名信息 +func (app *App) signSHA256WithRSA(source string, privateKey *rsa.PrivateKey) (signature string, err error) { + if privateKey == nil { + return "", fmt.Errorf("private key should not be nil") + } + h := crypto.Hash.New(crypto.SHA256) + _, err = h.Write([]byte(source)) + if err != nil { + return "", nil + } + hashed := h.Sum(nil) + signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed) + if err != nil { + return "", err + } + return base64.StdEncoding.EncodeToString(signatureByte), nil +} diff --git a/wechatpayopen.go b/wechatpayopen.go new file mode 100644 index 0000000..8286c8e --- /dev/null +++ b/wechatpayopen.go @@ -0,0 +1,91 @@ +package wechatpayopen + +import ( + "go.dtapp.net/golog" + "go.dtapp.net/gorequest" + "gorm.io/gorm" +) + +// App 微信支付服务器 +type App struct { + spAppid string // 服务商应用ID + spMchId string // 服务商户号 + subAppid string // 子商户应用ID + subMchId string // 子商户号 + apiV2 string // APIv2密钥 + apiV3 string // APIv3密钥 + serialNo string // 序列号 + mchSslSerialNo string // pem 证书号 + mchSslCer string // pem 内容 + mchSslKey string // pem key 内容 + pgsql *gorm.DB // pgsql数据库 + client *gorequest.App // 请求客户端 + log *golog.Api // 日志服务 + logTableName string // 日志表名 + logStatus bool // 日志状态 +} + +// NewApp 实例化 +func NewApp(spAppid, spMchId, subAppid, subMchId, apiV2, apiV3, serialNo, mchSslSerialNo, mchSslCer, mchSslKey string, pgsql *gorm.DB) *App { + app := &App{spAppid: spAppid, spMchId: spMchId, subAppid: subAppid, subMchId: subMchId, apiV2: apiV2, apiV3: apiV3, serialNo: serialNo, mchSslSerialNo: mchSslSerialNo, mchSslCer: mchSslCer, mchSslKey: mchSslKey} + app.client = gorequest.NewHttp() + if pgsql != nil { + app.pgsql = pgsql + app.logStatus = true + app.logTableName = "wechatpayopen" + app.log = golog.NewApi(&golog.ApiConfig{ + Db: pgsql, + TableName: app.logTableName, + }) + } + return app +} + +// NewAppConfig 实例化 +func (app *App) NewAppConfig(subAppid, subMchId string) *App { + app.subAppid = subAppid + app.subMchId = subMchId + return app +} + +func (app *App) request(url string, params map[string]interface{}, method string) (resp gorequest.Response, err error) { + + // 认证 + authorization, err := app.authorization(method, params, url) + if err != nil { + return gorequest.Response{}, err + } + + // 创建请求 + client := app.client + + // 设置请求地址 + client.SetUri(url) + + // 设置方式 + client.SetMethod(method) + + // 设置JSON格式 + client.SetContentTypeJson() + + // 设置参数 + client.SetParams(params) + + // 设置头部 + client.SetHeader("Authorization", authorization) + client.SetHeader("Accept", "application/json") + client.SetHeader("Accept-Language", "zh-CN") + + // 发起请求 + request, err := client.Request() + if err != nil { + return gorequest.Response{}, err + } + + // 日志 + if app.logStatus == true { + go app.postgresqlLog(request) + } + + return request, err +}