From b67f241d61843ae5d381254863b854410b7f9f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=85=89=E6=98=A5?= Date: Fri, 17 Jun 2022 11:32:09 +0800 Subject: [PATCH] - init --- .drone.yml | 17 ++ .gitignore | 9 + README.md | 25 ++ certificates.go | 47 +++ const.go | 63 ++++ go.mod | 32 ++ go.sum | 276 ++++++++++++++++++ pay.jsapi.go | 48 +++ pay.partner.transactions.id.go | 80 +++++ pay.partner.transactions.jsapi.go | 42 +++ ...partner.transactions.out-trade-no.close.go | 32 ++ pay.partner.transactions.out-trade-no.go | 82 ++++++ pgsql.go | 26 ++ refund.domestic.refunds.go | 78 +++++ refund.domestic.refunds.out_refund_no.go | 77 +++++ sign.go | 164 +++++++++++ wechatpayopen.go | 91 ++++++ 17 files changed, 1189 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 certificates.go create mode 100644 const.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 pay.jsapi.go create mode 100644 pay.partner.transactions.id.go create mode 100644 pay.partner.transactions.jsapi.go create mode 100644 pay.partner.transactions.out-trade-no.close.go create mode 100644 pay.partner.transactions.out-trade-no.go create mode 100644 pgsql.go create mode 100644 refund.domestic.refunds.go create mode 100644 refund.domestic.refunds.out_refund_no.go create mode 100644 sign.go create mode 100644 wechatpayopen.go 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 +}