初始化项目,复制原投票系统后台代码

This commit is contained in:
2024-12-05 14:21:18 +08:00
parent e1db36a1d1
commit 4accd5a78f
115 changed files with 7888 additions and 1 deletions
+103
View File
@@ -0,0 +1,103 @@
package weChat
import (
"context"
"errors"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/option"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"vote-video-api/config"
)
// 创建客户端
func createClient() (*core.Client, error) {
mchId := config.C.Wechat.Pay1281030301.MchId // 商户号
mchCertificateSerialNumber := config.C.Wechat.Pay1281030301.MchCertificateSerialNumber // 商户证书序列号
v3ApiSecret := config.C.Wechat.Pay1281030301.V3ApiSecret // 商户APIv3密钥
privateKeyPath := config.C.Wechat.Pay1281030301.PrivateKey // 商户私钥文件地址
if mchId == "" {
return nil, errors.New("商户号错误")
}
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
return nil, errors.New("微信支付生成失败")
}
ctx := context.Background()
// 使用商户私钥等初始化 client,并使它具有自动定时获取微信支付平台证书的能力
opts := []core.ClientOption{
option.WithWechatPayAutoAuthCipher(mchId, mchCertificateSerialNumber, mchPrivateKey, v3ApiSecret),
}
client, err := core.NewClient(ctx, opts...)
if err != nil {
return nil, errors.New(err.Error())
}
return client, nil
}
// WxPayResult 支付结果
type WxPayResult struct {
OrderStatus int `json:"order_status"` // 订单状态(1:待支付 2:已完成 3:已取消)
PayStatus int `json:"pay_status"` // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款)
PayTime *string `json:"pay_time"` // 支付时间
}
// WxPayRefundResult 退款结果
type WxPayRefundResult struct {
RefundStatus int `json:"refundStatus"` // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)
SuccessTime *string `json:"successTime"` // 退款成功时间
}
// HandlePayStatus 处理支付状态
func HandlePayStatus(t *payments.Transaction) (w *WxPayResult, err error) {
w = &WxPayResult{}
switch *t.TradeState {
case "SUCCESS": // 支付成功
w.OrderStatus = 2
w.PayStatus = 2
w.PayTime = t.SuccessTime
case "CLOSED": // 已关闭
w.PayStatus = 6
case "REVOKED": // 已撤销(付款码支付)
w.PayStatus = 7
case "USERPAYING": // 用户支付中(付款码支付)
w.PayStatus = 3
case "PAYERROR": // 支付失败(其他原因,如银行返回失败)
w.PayStatus = 4
default:
return nil, errors.New("未知支付状态")
}
return w, nil
}
// HandlePayRefundStatus 处理退款状态
func HandlePayRefundStatus(r *refunddomestic.Refund) (w *WxPayRefundResult, err error) {
w = &WxPayRefundResult{}
switch *r.Status {
case "SUCCESS": // 退款成功
w.RefundStatus = 3
if r.SuccessTime != nil {
successTime := r.SuccessTime.Format("2006-01-02 15:04:05")
w.SuccessTime = &successTime
}
case "CLOSED": // 退款关闭
w.RefundStatus = 5
case "PROCESSING": // 退款处理中
w.RefundStatus = 2
case "ABNORMAL": // 退款异常
return nil, errors.New("退款状态错误")
default:
return nil, errors.New("退款状态错误")
}
return w, nil
}
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDR4fiYRUjnLQA+
TPMslDvQIIyZm4ajWFcSXg6yozhdi/O+vuRhwgyeGXsE4SpvuxkK4HPZaocrsjqe
68Y46DyhudzXIBy50IF2UeN5ilj7ydq5TJbBOi1iOF1lgOUNK6DGiOQ+folVNKyN
sjrsA3RHUKHF/JdOfcpSGzvV79FnmjSll/KsdxEc9LCiAa5t7GYA9Hx7AIDG4i8b
03d5EJXvE8BGUZQItE6VIpHwyySTHtGQkPyI6lqMKt2Jlx0TFHu0GkTEmhuA2Z5C
5gOqYWclQO+X2ORgulL9Q3mNcpfy9uBynI/CJDbSvVTkNfwYB0Ny1knu/+in7C6p
IPJYfZpPAgMBAAECggEAWb5tJPcjSC5W10ziAiLUPJdeZ2Q4OupQOPtc/4eJV367
V8maMC7gZE3y61A4bBQtjhgRkVrat5V7OW8JkFXFb0XhJ1+EyPNeGDDFureseuWC
EA+uuqrcsw306a0mw+3uzlXEevByWquuSNx4E2katE/HDLiIHjjtZRReDomAGfLw
vLNZ40RgdNSXmwpVRoHOZnvX+C2Hxh4fKsrxs1HRCjFsSpTxKwsK6/Kdudj4hsaO
Z5glqf4qopPlQIIkToxOe5p7ukwo7vqaqogixx3jOjruOMpzxZ24wU5AxneLcOYr
ZC1659Uwv96TsKpueakWw0lq3TXkv9Qs+wDNI+NC6QKBgQDx4hWXplLYPYwgGgxG
L9Pk+If9aDS9wj78JUaYBXKR5atXmf/62ufahW4IEDfmMugC1bm9MoYC7CmHv1nk
nxckB84B20NOSiaxbkOaJuMHDtnNeXCmL8Gvtb8fTni1AM/g6XEleEw1r40LNZAq
kdyQ1+OUFhRQGOvAe1SRr1rdGwKBgQDeIcauwMPkc3Seh6Fb66FQ4Lgmt1GvvuiG
UJiwgwPFbEaKKczBHBL6hlKoAFweFa2Qw2xJ0H8jwb8RnkkNcAKYUyMc2wi85+Ih
d568pvXMdYl4DGeGdd/sXKf4dGDjcb2AHzNfDuklbCsAojtO3pLWOs5U/RSKe8ps
hhWa8nPO3QKBgQCIxuKU3X1tP+hz4qbcLYFxscQcXIeuYiABrwZrQnFV5Pxtzex9
Krn+zIK61oj1iAXATKD6Ro6XKnoVg/POHtQUEMHCNP2rUKzumj5p9eFdBV3OHgTA
RLMOrARGLLZ/C9WBBiBwIsVdekaUdxZtrAuAcEQFYjLcVCtDrbnVo8YKzwKBgEsg
V0cBMP+RwM5hBsTE45Er/3wwofLzeUb7+Tgxh1P887qEuphRO2X5ifkB7iXKpSIB
xh0M5AMe4tU9mG1wBaCo9YYr2j+xmTxCbbBWM2mMEwtD/rtuIGabS7/u9FnYPQQZ
CVHMBDRA6iZTuAVLp5PG3cPGuGzBw0uC6cm22E4NAoGAKrbg3nFPKtzBJZ8Z0iNH
G2gNotmHXbd4+gs4e3Iz9xsEabm4wCoEibNojNdMkX8zX267ebgfvesGEobROjW9
Intvh39xi3aQ2Q5gvXzKdY0lDzgVvQ7udmLQ4dCUyYnpLr7Yac0asRxLge2esCWV
x2uW2YTiUW3zsbHN/N/vJ5I=
-----END PRIVATE KEY-----
Binary file not shown.
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIELjCCAxagAwIBAgIUfewObFfg3HHwd/AvUkBlZq85vrswDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwMzAxMDExNzE3WhcNMjgwMjI4MDExNzE3WjCBhzETMBEGA1UEAwwK
MTYzNjY0NDI0ODEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTMwMQYDVQQL
DCrljJfkuqzmrKPmrKPnm7jnhaflgaXlurfnp5HmioDmnInpmZDlhazlj7gxCzAJ
BgNVBAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAK7x9ywApasrs+xIt0XxHhYOK4xZeuNEj5o7pzTcZMRkd6CB
1eX2ydlUfDe+FcNS03cwLNtUeAfrZXlhA807b4HuReaFqCrbt87hfIF/lOBpePdN
sSr8Wi6OKfPakuLNZ4RCOdlvgxPMhOf2b9VFQwns8h72H6JrFz7xR+Heundy3KGH
kEoG2qcl7nKyhkUVSRSH4/yzsxsDIwkpXjEiSL87E+A6GKik7jphYc+vfV9NURiA
JwbVWbQMhpj3YLRxgXadLS4xMryB59fYKm+VFMNg/jSZG55Jz2DW02aR2h3KjegY
SGA9qMKojkFVOrZvxKWOvtEWw2JQ/1dRvzYpgw0CAwEAAaOBuTCBtjAJBgNVHRME
AjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDov
L2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUw
REJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJF
MTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IB
AQCBjjBeJTRtnsIISCfoQ7pj70dil5LTBNjpzV9swGOG7sY9dTMbHV0K2BUyRvbD
eY2fuCcr3Qt3L1RykyomKbxD4O6b/ZPLTkiq2m+hq34g2Ig7zUx0a0sNKf/NPAq6
6pVu5/XdIKYXz2OsByBI8aN2hkcUApmM1qlm+gb4FSmTzkdHaFRblhDlKfiRdAw8
T4yliaOFovqhf/S8o5Xa76kQMOb3uJ/oE4KX02kp/ig5wZKj9nay46AyovBXOj+Z
0oaeIPFDXCPzZV7LA4E45zDl43fL8r+9OzaVprRZ9ASO0cgnPsHyS+o/mpMEBOR4
NIwdIaqYRQ/Rh6eZQvrqDZ4r
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCu8fcsAKWrK7Ps
SLdF8R4WDiuMWXrjRI+aO6c03GTEZHeggdXl9snZVHw3vhXDUtN3MCzbVHgH62V5
YQPNO2+B7kXmhagq27fO4XyBf5TgaXj3TbEq/Foujinz2pLizWeEQjnZb4MTzITn
9m/VRUMJ7PIe9h+iaxc+8Ufh3rp3ctyhh5BKBtqnJe5ysoZFFUkUh+P8s7MbAyMJ
KV4xIki/OxPgOhiopO46YWHPr31fTVEYgCcG1Vm0DIaY92C0cYF2nS0uMTK8gefX
2CpvlRTDYP40mRueSc9g1tNmkdodyo3oGEhgPajCqI5BVTq2b8Sljr7RFsNiUP9X
Ub82KYMNAgMBAAECggEAT7uNyGs/FkVjykPV67WZ3bl1lZDOljgQLt4TNd9gubWE
ZA3om9efZULBHnKu3oeoQ0EcoJXd4tYhOHHD1szI5HHhP9AYtffPzSUtpqOsCZ9o
d2XcYlgDDgbTDgXHPkEZdcjtLrFJD0P+Ku5BR/U6OZLZQs0v28ltHc2/0iy91WQt
iB/NSf88dY1L5RVZc7dZfb8nFB2/BsITUXDFJpzdim3AXKvNYzDajs/yuoTp5r/b
2EOjUIGPP55W/8iPwbezgDToF79toum6V7yUpCq5P+QQvnnbJfoc1yXB48zHjmgP
I2P1cDVtFs+V9Edb6qsU3e4CMyBQg5Uykk3TUqZRIQKBgQDnXgGk8XnI1kG9jZMR
wMkra87Dx76vrBdM47n+c2I1xubM9WSeSUxtXGyryTwJ9i6/BUyc3Eyb2wi4Q8AK
1ZIEFhvIUkBJpUgevAksj4eX6z0qDwDc3ZhgAAWOplnOCCyJuOBKmZks4GaQb4Gv
/aSFVQ9jefOc5e9RIDNzkHFkaQKBgQDBkigvmusjad95Jt34TwyE4Dr9z2LzBker
6ebcyQmv4YdKvq0KaaVMrroH7pNu7CSFrj4CGqdS/Gg1BtK8Xaxn+gEWT6RPcsi1
mPwy/7oiK0GodzNI6RW5HDZEHKG2icEb4ycDnHeLfThKmP/gckPifjcJHrP5OX1A
V6qrq4iFBQKBgGdgB1gNVJ65rJHnCckq3Dd8advsCXUwbRC7x0S7hSwF/OWi1xwq
H+3VF/EBbsP8rRJIadzESa5xhUnfa5Trq9wLjMpKhdLh+IFS/r5cOvdT8fYy0e3d
TNHH8LO1+/YkjNHUOtLaIih88xah284ohDPWt5N4z7JQwkb7HkIKTb/RAoGARt51
7Afx8sM+WCLMva5jTPqzXl1hQsyXzO8T4N2RuFz/pXPt8pP/OvX1khXc0I2QSYkj
lq2feRiEJnXbDa/WATNc1ohOBfBmX2YlX56UzRG9Nip+EkGT/HPBwmohIq2Ij+c4
T3AnrGAqDdW6SLhM9k1zZNli1uofW0E9cSCaGOkCgYAI29eelAwUxpbDXJB6vSyr
LBvDV+C+/3OW4AjlJ5lWSzY8oMn21Xzp03MwXOXOSFuR6vTMkJDKJ3zIDHBt9vJ2
evNmfMKzqNdsvNjORb79GAZ0paBF1XGzXvPO9JSUi6oZxNg+pW8oxIzx0xFIgtZL
PxnzFj2IbraxYwuR1CI6rQ==
-----END PRIVATE KEY-----
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEFDCCAvygAwIBAgIUES/M0bnsyCknA6tzY8c9dLav3BowDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMwMzAxMDExNzE2WhcNMjgwMjI4MDExNzE2WjBuMRgwFgYDVQQDDA9U
ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl
bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo
ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvM2YfnzwBvd+B5Ro1
z+LaCVYhxia9/hQwRhi5ag2HmeZZNgMNf4+bC75Nv+t3RW0lklbM9qJSBk03eEBG
75Q33VPGy//WCJZgXlHV+rZ7ejzWtwh9Muhd60HFXhIsM3pwlfWpPo6bkJie9Na0
wMsgg/8UxsNydhEZF6HFLdqY+zqGOjRRCduIyJcFhtrkjNUMIFAOSkHBaGJjmGrm
OXigAnYAsaD1VWLOoblA0HlOs144KQ/5Shj74Ggk2pFo/YDN+i5hazHZo9hZnjmX
G5BV3KDJD0k3Gn/qymhiLlzGsYW79P+BbsmE/M6jKIP2jxcDDdpek0Z6Lk0Cz/au
02UPAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB
kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv
aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4
RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0
MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAJX6C/QMYF0F3IiK9P8tW2DKN8y9vCU20
6Ws8u4bcO3AaiSmsdAJ6I1MyZkUg3dKijtnDbieY2P364IEp48TmI4k6UJwP4+/f
i0NseOm3BmAJ3mBoNmuFul+61opKpeV67AYQuhbehVA4cDPqKb/hP0tEcb6nefIO
mnys5GReLWLFV2XFR1h9QtsohPOEYlpBl6lmKHNoQZdAPtSHq2iVFLbuD7GMKoj7
Br2puJEQgxla1AsNxAYjGttEIO9I30+L5av7SKesSGXvL6G5eOvFrZl0S4EsNVz4
QeL4qmtFEKDi2eaxeyLuWe9TMI/IiI3ngekoDyTwdnUzadumbio+dg==
-----END CERTIFICATE-----
Binary file not shown.
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENzCCAx+gAwIBAgIUEvql9hcIt5W7UzeukVSU4twsqHswDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMxMjE5MDUxNTA4WhcNMjgxMjE3MDUxNTA4WjCBkDETMBEGA1UEAwwK
MTY1OTY2MjkzNjEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTwwOgYDVQQL
DDPmiJDpg73ph5HniZvmrKPmrKPnm7jnhafkupLogZTnvZHljLvpmaLmnInpmZDl
hazlj7gxCzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAPCINl3HwUEM2CzerR5u4GbUnvRJDjVmaEbm
qHlaYRUrrGDIF/Q63QEbcUh+PP+zZ0Grln7hGT5dwPj6e8jwmVvtEqO8mhBQzynH
6MZUONVfxj7L5Pf/+vZtB/PPGKdupZ9tUuodqOrbLycE8xWmSUDPrKEZ9iOEYamz
p0s9fPWwcZM8Vf4Gqx8d9ItnFJjVmF8aIJfsvcGNvKWD44JUzZItcsWz/srzxRAH
2moGIM8eNf1UNm3k6q43dfoRbZryWmVIyw49xybAKzm5e4Q4z8jCwGoQWgs2tSF4
uM0/SxK2j1XRGnVmwRzcdDq/rCPlbqynHXZyUh8Te1540HJKZK8CAwEAAaOBuTCB
tjAJBgNVHRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGH
hoGEaHR0cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0x
QkQ0MjIwRTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0
NzFCNjU0MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3
DQEBCwUAA4IBAQAe4CfX/m0Xz6aI/Studd5pbdb/M7cyTjTwYSoBUNKtEmBwQFCu
CsFOEGuqmhq4ciMkooDWY/6mccgluZtdiYB8PQAIk1+IKBgpmhSuJP6yONecoit0
JBWzFeSLkBxKkq6CfHhb9wW4EK0oPtgE6LjqLMj13fLy+Mxf4Eu4fgSlsdgGLpBt
bXIPbL12bCcP5hJLGRoLbECyx9LaOe1D9RAxwcTx/i+8C1s3IajkkyZnoPasLMs6
cdSIp20QSct0klMgpEobmV+Z59NMKAQC/pbaSRrT/PDHUTDAuwA7W+COALg6H26c
MAGyeSTnwhc9WccmohKPgi3UcXh/Sn84QqSs
-----END CERTIFICATE-----
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDwiDZdx8FBDNgs
3q0ebuBm1J70SQ41ZmhG5qh5WmEVK6xgyBf0Ot0BG3FIfjz/s2dBq5Z+4Rk+XcD4
+nvI8Jlb7RKjvJoQUM8px+jGVDjVX8Y+y+T3//r2bQfzzxinbqWfbVLqHajq2y8n
BPMVpklAz6yhGfYjhGGps6dLPXz1sHGTPFX+BqsfHfSLZxSY1ZhfGiCX7L3Bjbyl
g+OCVM2SLXLFs/7K88UQB9pqBiDPHjX9VDZt5OquN3X6EW2a8lplSMsOPccmwCs5
uXuEOM/IwsBqEFoLNrUheLjNP0sSto9V0Rp1ZsEc3HQ6v6wj5W6spx12clIfE3te
eNBySmSvAgMBAAECggEBAOVndDqjLozFHFRHGGOjKgDJHsUr/BIwFpowmVQMP/V6
DtDLZhU4ItpQew3B4JmbWIrIhSODupjBwC92bqLp3cpP5Gwnj+SpvbtCf57QatgO
nTv9KObizE9FE8WTqhbeL7ZLBT5mhVlhLKqRTOpECy92IlYQNbIQKzk4MAFRpqGH
CqSMtD0ng6DiMDxdQYLgIDCZIKamv0lF3rQymINQ6VZHpdlB0f3Gp/T/yWzn0nmJ
5eOrBhzFKGpevsp00FmvFE7HSqnogC7a3ieLluhfRPyB4QZQjfMorEGKwS8i7I8U
ZLA9FhtsaZpvOeNSZoVh3z0sblKUOvhXvL+jgRQK4PkCgYEA+M0QxM3VXst+deDr
ZkpmhWdPUQykVdSbfOdR8thYDu30QdjKe/T0X75wcqKh+s2GPK2GZsgeU02+EWqv
gVZMgFwGKFCBzDiCs9QwOTO+32OWP62yapFWNba1g4K7Un7MJRsJIj0u0nwjik68
hec5KFYTOYttzk9oOLgXLFM6zm0CgYEA933lgvPITFykd2kGKg1v4oA52PPmq9LT
2dvVkDpIxioaUUHPALwFLhHLT+w/RPwCfP9gqZAQQFAfpMiTezm+ULA54rLHF2Tj
Lm0q3ko98sFW3Ltk1ehw8CdU0p5bii7a7AbuS6V36iTIrC53rhl7N+WL82uMw2w/
a8Gz3fIl3gsCgYA+XsHeZC8iBWddS5YXXX1X4e8bRU0JCzQzWpXLh/qDO5mozBzu
eBiuy8HKqwRqKA2HtoRjzbT0cx+7o//9L1IcN3V/s7bmKCBzzjSMknE99OwcaIG6
f1aaPoRARIyLAKhSgPWINMhBEcejC1vtQWqttu441cAgIP3ighulC/RI/QKBgQDE
lxKqdK8USTqzR4+H8+h+CNDqjsMalXuGwGLiEAoirur8xMODl9adg7D2KXkQeQYY
+Fp2FmNyUrLwGAtehL2yJmm0s8IFyTPUew7kSCDxJbaz2377k4mymet86iFYoGNx
vQeouyWHrfRhIQAcIU2JVyNWFoZX7TJrjBAuKtz9hQKBgGUFIyhQiPYRWDocvuSi
McyzYov2AnNehn24vsXC3SCXT804KPOX8RY4he63zWMGaLfCmSq1fhsJnf2pvffK
W+9ZXlIH6dwgQ9QyDNjKRVCGwTASsWOyqgn1Be0VDuuoXlREyGNQSn2RpvL1SMFH
PW/ePgdfoFkzU5WFJFPnvlU2
-----END PRIVATE KEY-----
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEFDCCAvygAwIBAgIUW1yKacyG0RJ/a2qgaqrxBTHu/pAwDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMxMjE5MDUxNTA3WhcNMjgxMjE3MDUxNTA3WjBuMRgwFgYDVQQDDA9U
ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl
bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGEwJDTjERMA8GA1UEBwwIU2hlblpo
ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQClp0R6MR6NwEkqRJNF
aD1INE0uHoRg1of0D8mTVwoWvADgt9gutpeJ1uTweAtrXkvu9NALxTNaG51tsZa8
2fsJ00/Ry1IYUb6xG7kAuh0h4KLgWUUhoXDOKW0B1K/7g3AhCAylUzhD/ghD1Y8F
hlMtw87oNzfTE51I/2sGZgnPX2IqIEOjxx1F8Ebzfh5shJGN40guMpBItsCWYe7s
zj1zdyERL6zxQN2A7o2QvDeX1EsNIdwoEECv06tWjUEGJioFwb3OKBu5n9jJT+Og
EcJFmRasLRQWsooutsDCO7y5wIKrCY9n52eZgk9QlHkYWyiwUqpnrrJ1DC+ueJei
QptFAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB
kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv
aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4
RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0
MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAhWwscSAE1OBR3AGBOuFHs3vFJ+y37z5/
EoB9FwVu4zXP1dih101zl83iwIdPEHIR2skXcTjHRI2qdOvu7X5JmWOJP+51RGtX
Y+aWXKfhRzRQOUlpNyltlgsGKzbIXLLBzQjMzBNv+n/HX4q9F0TV3SW4zTiMlhD8
+bGGGwuIhziWpK9qvr3RPU1j+0bggHhIre+cNolnh1FepS4Gt964zhx6THtrS/jI
I64UBBh6moBq7zB5QYloBhW464c7GCEEv5/AdcxGhAe+vuL/mkVRNsSRxVPIxPE6
+qoIiNBmQvL/mL+4UKfX6b9h4wrUQUdQP3ljRdpL3a5YTMTUJuoJSQ==
-----END CERTIFICATE-----
+62
View File
@@ -0,0 +1,62 @@
package weChat
import (
"context"
"errors"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/app"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
"vote-video-api/config"
)
// CloseJsapiOrder 关闭jsapi支付订单
func CloseJsapiOrder(outTradeNo string) error {
client, err := createClient()
if err != nil {
return err
}
svc := jsapi.JsapiApiService{Client: client}
req := jsapi.CloseOrderRequest{
OutTradeNo: &outTradeNo,
Mchid: core.String(config.C.Wechat.Pay1281030301.MchId),
}
result, err := svc.CloseOrder(context.TODO(), req)
if err != nil {
return err
}
if result.Response.StatusCode != 204 {
return errors.New("关闭订单失败")
}
return nil
}
// CloseAppOrder 关闭app支付订单
func CloseAppOrder(outTradeNo string) error {
client, err := createClient()
if err != nil {
return err
}
svc := app.AppApiService{Client: client}
req := app.CloseOrderRequest{
OutTradeNo: &outTradeNo,
Mchid: core.String(config.C.Wechat.Pay1281030301.MchId),
}
result, err := svc.CloseOrder(context.TODO(), req)
if err != nil {
return err
}
if result.Response.StatusCode != 204 {
return errors.New("关闭订单失败")
}
return nil
}
+82
View File
@@ -0,0 +1,82 @@
package weChat
import (
"context"
"errors"
"github.com/gin-gonic/gin"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/wechatpay-apiv3/wechatpay-go/utils"
"vote-video-api/config"
)
// PayParseNotify 支付回调通知的验签与解密
func PayParseNotify(c *gin.Context) (notifyReq *notify.Request, t *payments.Transaction, err error) {
mchId := config.C.Wechat.Pay1281030301.MchId // 商户号
mchCertificateSerialNumber := config.C.Wechat.Pay1281030301.MchCertificateSerialNumber // 商户证书序列号
v3ApiSecret := config.C.Wechat.Pay1281030301.V3ApiSecret // 商户APIv3密钥
privateKeyPath := "extend/weChat/certs/" + config.C.Wechat.Pay1281030301.MchId + "/apiclient_key.pem" // 商户私钥文件地址
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
return nil, nil, errors.New("微信支付生成失败")
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(c, mchPrivateKey, mchCertificateSerialNumber, mchId, v3ApiSecret)
if err != nil {
return nil, nil, err
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchId)
// 3. 使用证书访问器初始化 `notify.Handler`
handler, err := notify.NewRSANotifyHandler(v3ApiSecret, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
transaction := new(payments.Transaction)
notifyReq, err = handler.ParseNotifyRequest(context.Background(), c.Request, transaction)
// 如果验签未通过,或者解密失败
if err != nil {
return nil, nil, err
}
return notifyReq, transaction, nil
}
// RefundParseNotify 退款回调通知的验签与解密
func RefundParseNotify(c *gin.Context) (notifyReq *notify.Request, t *refunddomestic.Refund, err error) {
mchId := config.C.Wechat.Pay1281030301.MchId // 商户号
mchCertificateSerialNumber := config.C.Wechat.Pay1281030301.MchCertificateSerialNumber // 商户证书序列号
v3ApiSecret := config.C.Wechat.Pay1281030301.V3ApiSecret // 商户APIv3密钥
privateKeyPath := "extend/weChat/certs/" + config.C.Wechat.Pay1281030301.MchId + "/apiclient_key.pem" // 商户私钥文件地址
// 使用 utils 提供的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
mchPrivateKey, err := utils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
return nil, nil, errors.New("微信支付生成失败")
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(c, mchPrivateKey, mchCertificateSerialNumber, mchId, v3ApiSecret)
if err != nil {
return nil, nil, err
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchId)
// 3. 使用证书访问器初始化 `notify.Handler`
handler, err := notify.NewRSANotifyHandler(v3ApiSecret, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
refund := new(refunddomestic.Refund)
notifyReq, err = handler.ParseNotifyRequest(context.Background(), c.Request, refund)
// 如果验签未通过,或者解密失败
if err != nil {
return nil, nil, err
}
return notifyReq, refund, nil
}
+142
View File
@@ -0,0 +1,142 @@
package weChat
import (
"context"
"errors"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/app"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
)
/**
JSAPI下单
APP下单
*/
// JsapiRequest 请求数据-JSAPI下单
type JsapiRequest struct {
AppId string `json:"appid" comment:"公众号ID"`
MchId string `json:"mchid" comment:"直连商户号"`
Description string `json:"description" comment:"商品描述"`
OutTradeNo string `json:"out_trade_no" comment:"商户订单号"`
NotifyUrl string `json:"notify_url" comment:"回调地址"`
Amount JsapiRequestAmountRequest `json:"amount" comment:"订单金额"`
Payer JsapiRequestPayerRequest `json:"payer" comment:"支付者"`
}
// JsapiRequestAmountRequest 订单金额信息
type JsapiRequestAmountRequest struct {
Total int64 `json:"total" comment:"订单总金额"`
Currency string `json:"currency" comment:"货币类型"`
}
// JsapiRequestPayerRequest 支付者信息
type JsapiRequestPayerRequest struct {
OpenId string `json:"openid" comment:"openid"`
}
// AppRequest 请求数据-APP下单
type AppRequest struct {
AppId string `json:"appid" comment:"公众号ID"`
MchId string `json:"mchid" comment:"直连商户号"`
Description string `json:"description" comment:"商品描述"`
OutTradeNo string `json:"out_trade_no" comment:"商户订单号"`
NotifyUrl string `json:"notify_url" comment:"回调地址"`
Amount AppRequestAmountRequest `json:"amount" comment:"订单金额"`
}
// AppRequestAmountRequest 订单金额信息
type AppRequestAmountRequest struct {
Total int64 `json:"total" comment:"订单总金额"`
Currency string `json:"currency" comment:"货币类型"`
}
// GetJsapiPrepay JSAPI下单-获取jsapi预支付交易会话
func (r JsapiRequest) GetJsapiPrepay() (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) {
client, err := createClient()
if err != nil {
return nil, err
}
svc := jsapi.JsapiApiService{Client: client}
// 得到prepay_id,以及调起支付所需的参数和签名
PrepayRequest := jsapi.PrepayRequest{
Appid: core.String(r.AppId),
Mchid: core.String(r.MchId),
Description: core.String(r.Description),
OutTradeNo: core.String(r.OutTradeNo),
NotifyUrl: core.String(r.NotifyUrl),
Amount: &jsapi.Amount{
Total: core.Int64(r.Amount.Total),
Currency: core.String("CNY"),
},
Payer: &jsapi.Payer{
Openid: core.String(r.Payer.OpenId),
},
}
resp, result, err := svc.PrepayWithRequestPayment(context.Background(),
PrepayRequest,
)
if err != nil {
return nil, err
}
if result.Response.StatusCode != 200 {
return nil, errors.New("发起支付失败")
}
if resp.PrepayId == nil {
return nil, errors.New("发起支付失败")
}
if err != nil {
return nil, err
}
return resp, nil
}
// GetAppPrepay APP下单-获取app预支付交易会话
func (r AppRequest) GetAppPrepay() (prepay *app.PrepayWithRequestPaymentResponse, err error) {
client, err := createClient()
if err != nil {
return nil, err
}
svc := app.AppApiService{Client: client}
// 得到prepay_id,以及调起支付所需的参数和签名
resp, result, err := svc.PrepayWithRequestPayment(context.Background(),
app.PrepayRequest{
Appid: core.String(r.AppId),
Mchid: core.String(r.MchId),
Description: core.String(r.Description),
OutTradeNo: core.String(r.OutTradeNo),
NotifyUrl: core.String(r.NotifyUrl),
Amount: &app.Amount{
Total: core.Int64(r.Amount.Total),
Currency: core.String("CNY"),
},
},
)
if err != nil {
return nil, err
}
if result.Response.StatusCode != 200 {
return nil, errors.New("发起支付失败")
}
if resp.PrepayId == nil {
return nil, errors.New("发起支付失败")
}
if err != nil {
return nil, err
}
return resp, nil
}
+46
View File
@@ -0,0 +1,46 @@
package weChat
import (
"context"
"github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
)
type RefundRequest struct {
TransactionId string `json:"transaction_id" comment:"微信订单号"`
OutTradeNo string `json:"out_trade_no" comment:"商户订单号"`
OutRefundNo string `json:"out_refund_no" comment:"退款订单号"`
Reason string `json:"reason" comment:"退款原因"`
RefundAmount int64 `json:"refund_amount" comment:"退款金额"`
PaymentAmountTotal int64 `json:"payment_amount_total" comment:"支付金额"`
NotifyUrl string `json:"notify_url" comment:"回调地址"`
}
// Refund 退款
func (r RefundRequest) Refund() (*refunddomestic.Refund, error) {
client, err := createClient()
if err != nil {
return nil, err
}
refundRequest := refunddomestic.CreateRequest{
TransactionId: core.String(r.TransactionId),
OutTradeNo: core.String(r.OutTradeNo),
OutRefundNo: core.String(r.OutRefundNo),
Reason: core.String(r.Reason),
NotifyUrl: core.String(r.NotifyUrl),
Amount: &refunddomestic.AmountReq{
Currency: core.String("CNY"),
Refund: core.Int64(r.RefundAmount),
Total: core.Int64(r.PaymentAmountTotal),
},
}
svc := refunddomestic.RefundsApiService{Client: client}
resp, _, err := svc.Create(context.Background(), refundRequest)
if err != nil {
return nil, err
}
return resp, nil
}
+76
View File
@@ -0,0 +1,76 @@
package weChat
import (
"encoding/json"
"errors"
"io"
"net/http"
)
// GetUserInfoResponse 网页授权拉取用户信息返回值
type GetUserInfoResponse struct {
OpenId string `json:"openid" form:"openid" label:"openid"`
Nickname string `json:"nickname" form:"nickname" label:"用户昵称"`
Sex int `json:"sex" form:"sex" label:"性别"` // 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
Province string `json:"province" form:"province" label:"省份"`
City string `json:"city" form:"city" label:"城市"`
Country string `json:"country" form:"country" label:"国家"`
HeadImgUrl string `json:"headimgurl" form:"headimgurl" label:"头像"`
UnionId string `json:"unionid" form:"unionid" label:"unionid"`
Errcode *int `json:"errcode" form:"errcode" label:"errcode"`
Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"`
}
// GetUserInfo 网页授权拉取用户信息
func GetUserInfo(accessToken, openId string) (r *GetUserInfoResponse, err error) {
if accessToken == "" {
return nil, errors.New("授权失败")
}
if openId == "" {
return nil, errors.New("授权失败")
}
// 拼接请求数据
requestUrl := "https://api.weixin.qq.com/sns/userinfo?" +
"access_token=" + accessToken +
"&openid=" + openId +
"&lang=zh_CN"
// 发送GET请求
resp, err := http.Get(requestUrl)
if err != nil {
return nil, err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
if resp.StatusCode != 200 {
return nil, errors.New("请求失败")
}
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var response GetUserInfoResponse
err = json.Unmarshal([]byte(respBody), &response)
if err != nil {
// json解析失败
return nil, err
}
if response.Errcode != nil {
if response.Errmsg != "" {
return nil, errors.New(response.Errmsg)
} else {
return nil, errors.New("请求失败")
}
}
return &response, nil
}
+68
View File
@@ -0,0 +1,68 @@
package weChat
import (
"encoding/json"
"errors"
"io"
"net/http"
"vote-video-api/config"
)
// GetWebAccessTokenResponse 获取网页授权access_token返回值
type GetWebAccessTokenResponse struct {
AccessToken string `json:"access_token" form:"access_token" label:"网页授权接口调用凭证"`
ExpiresIn int `json:"expires_in" form:"expires_in" label:"access_token接口调用凭证超时时间"`
RefreshToken string `json:"refresh_token" form:"refresh_token" label:"用户刷新access_token"`
OpenId string `json:"openid" form:"openid" label:"openid"`
Scope string `json:"scope" form:"scope" label:"scope"`
UnionId string `json:"unionid" form:"unionid" label:"unionid"`
Errcode *int `json:"errcode" form:"errcode" label:"errcode"`
Errmsg string `json:"errmsg" form:"errmsg" label:"errmsg"`
}
// GetWebAccessToken 获取网页授权access_token
func GetWebAccessToken(code string) (r *GetWebAccessTokenResponse, err error) {
// 拼接请求数据
requestUrl := "https://api.weixin.qq.com/sns/oauth2/access_token?" +
"appid=" + config.C.Wechat.AppId +
"&secret=" + config.C.Wechat.AppSecret +
"&code=" + code +
"&grant_type=authorization_code"
// 发送GET请求
resp, err := http.Get(requestUrl)
if err != nil {
return nil, err
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(resp.Body)
if resp.StatusCode != 200 {
return nil, errors.New("请求失败")
}
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var response GetWebAccessTokenResponse
err = json.Unmarshal([]byte(respBody), &response)
if err != nil {
// json解析失败
return nil, err
}
if response.Errcode != nil {
if response.Errmsg != "" {
return nil, errors.New(response.Errmsg)
} else {
return nil, errors.New("请求失败")
}
}
return &response, nil
}