代码拉取完成,页面将自动刷新
package qq
import (
"context"
"crypto/hmac"
"crypto/md5"
"crypto/sha256"
"crypto/tls"
"encoding/xml"
"errors"
"fmt"
"hash"
"strings"
"sync"
"gitee.com/ujq/gopay"
"gitee.com/ujq/gopay/pkg/xhttp"
"gitee.com/ujq/gopay/pkg/xlog"
)
type Client struct {
MchId string
ApiKey string
IsProd bool
DebugSwitch gopay.DebugSwitch
logger xlog.XLogger
mu sync.RWMutex
sha256Hash hash.Hash
md5Hash hash.Hash
hc *xhttp.Client
tlsHc *xhttp.Client
}
// 初始化QQ客户端(正式环境)
// mchId:商户ID
// ApiKey:API秘钥值
func NewClient(mchId, apiKey string) (client *Client) {
logger := xlog.NewLogger()
logger.SetLevel(xlog.DebugLevel)
return &Client{
MchId: mchId,
ApiKey: apiKey,
DebugSwitch: gopay.DebugOff,
logger: logger,
sha256Hash: hmac.New(sha256.New, []byte(apiKey)),
md5Hash: md5.New(),
hc: xhttp.NewClient(),
tlsHc: xhttp.NewClient(),
}
}
// SetBodySize 设置http response body size(MB)
func (q *Client) SetBodySize(sizeMB int) {
if sizeMB > 0 {
q.hc.SetBodySize(sizeMB)
}
}
// SetHttpClient 设置自定义的xhttp.Client
func (q *Client) SetHttpClient(client *xhttp.Client) {
if client != nil {
q.hc = client
}
}
// SetTLSHttpClient 设置自定义的xhttp.Client
func (q *Client) SetTLSHttpClient(client *xhttp.Client) {
if client != nil {
q.tlsHc = client
}
}
func (q *Client) SetLogger(logger xlog.XLogger) {
if logger != nil {
q.logger = logger
}
}
// 向QQ发送Post请求,对于本库未提供的QQ API,可自行实现,通过此方法发送请求
// bm:请求参数的BodyMap
// url:完整url地址,例如:https://qpay.qq.com/cgi-bin/pay/qpay_unified_order.cgi
// tlsConfig:tls配置,如无需证书请求,传nil
func (q *Client) PostQQAPISelf(ctx context.Context, bm gopay.BodyMap, url string, tlsConfig *tls.Config) (bs []byte, err error) {
if bm.GetString("mch_id") == gopay.NULL {
bm.Set("mch_id", q.MchId)
}
if bm.GetString("fee_type") == gopay.NULL {
bm.Set("fee_type", "CNY")
}
if bm.GetString("sign") == gopay.NULL {
sign := GetReleaseSign(q.ApiKey, bm.GetString("sign_type"), bm)
bm.Set("sign", sign)
}
req := GenerateXml(bm)
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Request: %s", req)
}
httpClient := xhttp.NewClient()
if q.IsProd && tlsConfig != nil {
httpClient.SetTLSConfig(tlsConfig)
}
res, bs, err := httpClient.Req(xhttp.TypeXML).Post(url).SendString(req).EndBytes(ctx)
if err != nil {
return nil, err
}
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Response: %d, %s", res.StatusCode, string(bs))
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
if strings.Contains(string(bs), "HTML") {
return nil, errors.New(string(bs))
}
return bs, nil
}
// 提交付款码支付
// 文档地址:https://qpay.qq.com/buss/wiki/1/1122
func (q *Client) MicroPay(ctx context.Context, bm gopay.BodyMap) (qqRsp *MicroPayResponse, err error) {
err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "device_info", "auth_code")
if err != nil {
return nil, err
}
bm.Set("trade_type", TradeType_MicroPay)
bs, err := q.doQQPost(ctx, bm, microPay)
if err != nil {
return nil, err
}
qqRsp = new(MicroPayResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 撤销订单
// 文档地址:https://qpay.qq.com/buss/wiki/1/1125
func (q *Client) Reverse(ctx context.Context, bm gopay.BodyMap) (qqRsp *ReverseResponse, err error) {
err = bm.CheckEmptyError("sub_mch_id", "nonce_str", "out_trade_no", "op_user_id", "op_user_passwd")
if err != nil {
return nil, err
}
bs, err := q.doQQPost(ctx, bm, reverse)
if err != nil {
return nil, err
}
qqRsp = new(ReverseResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 统一下单
// 文档地址:https://qpay.qq.com/buss/wiki/38/1203
func (q *Client) UnifiedOrder(ctx context.Context, bm gopay.BodyMap) (qqRsp *UnifiedOrderResponse, err error) {
err = bm.CheckEmptyError("nonce_str", "body", "out_trade_no", "total_fee", "spbill_create_ip", "trade_type", "notify_url")
if err != nil {
return nil, err
}
bs, err := q.doQQPost(ctx, bm, unifiedOrder)
if err != nil {
return nil, err
}
qqRsp = new(UnifiedOrderResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 订单查询
// 文档地址:https://qpay.qq.com/buss/wiki/38/1205
func (q *Client) OrderQuery(ctx context.Context, bm gopay.BodyMap) (qqRsp *OrderQueryResponse, err error) {
err = bm.CheckEmptyError("nonce_str")
if err != nil {
return nil, err
}
if bm.GetString("out_trade_no") == gopay.NULL && bm.GetString("transaction_id") == gopay.NULL {
return nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time")
}
bs, err := q.doQQPost(ctx, bm, orderQuery)
if err != nil {
return nil, err
}
qqRsp = new(OrderQueryResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 关闭订单
// 文档地址:https://qpay.qq.com/buss/wiki/38/1206
func (q *Client) CloseOrder(ctx context.Context, bm gopay.BodyMap) (qqRsp *CloseOrderResponse, err error) {
err = bm.CheckEmptyError("nonce_str", "out_trade_no")
if err != nil {
return nil, err
}
bs, err := q.doQQPost(ctx, bm, orderClose)
if err != nil {
return nil, err
}
qqRsp = new(CloseOrderResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 申请退款
// 注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 nil,否则,3证书Path均不可空
// 文档地址:https://qpay.qq.com/buss/wiki/38/1207
func (q *Client) Refund(ctx context.Context, bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath any) (qqRsp *RefundResponse, err error) {
if err = checkCertFilePathOrContent(certFilePath, keyFilePath, pkcs12FilePath); err != nil {
return nil, err
}
err = bm.CheckEmptyError("nonce_str", "out_refund_no", "refund_fee", "op_user_id", "op_user_passwd")
if err != nil {
return nil, err
}
if bm.GetString("out_trade_no") == gopay.NULL && bm.GetString("transaction_id") == gopay.NULL {
return nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time")
}
bs, err := q.doQQPostTLS(ctx, bm, refund)
if err != nil {
return nil, err
}
qqRsp = new(RefundResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 退款查询
// 文档地址:https://qpay.qq.com/buss/wiki/38/1208
func (q *Client) RefundQuery(ctx context.Context, bm gopay.BodyMap) (qqRsp *RefundQueryResponse, err error) {
err = bm.CheckEmptyError("nonce_str")
if err != nil {
return nil, err
}
if bm.GetString("refund_id") == gopay.NULL && bm.GetString("out_refund_no") == gopay.NULL && bm.GetString("transaction_id") == gopay.NULL && bm.GetString("out_trade_no") == gopay.NULL {
return nil, errors.New("refund_id, out_refund_no, out_trade_no, transaction_id are not allowed to be null at the same time")
}
bs, err := q.doQQPost(ctx, bm, refundQuery)
if err != nil {
return nil, err
}
qqRsp = new(RefundQueryResponse)
if err = xml.Unmarshal(bs, qqRsp); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
return qqRsp, nil
}
// 交易账单
// 文档地址:https://qpay.qq.com/buss/wiki/38/1209
func (q *Client) StatementDown(ctx context.Context, bm gopay.BodyMap) (qqRsp string, err error) {
err = bm.CheckEmptyError("nonce_str", "bill_date", "bill_type")
if err != nil {
return gopay.NULL, err
}
billType := bm.GetString("bill_type")
if billType != "ALL" && billType != "SUCCESS" && billType != "REFUND" && billType != "RECHAR" {
return gopay.NULL, errors.New("bill_type error, please reference: https://qpay.qq.com/buss/wiki/38/1209")
}
bs, err := q.doQQPost(ctx, bm, statementDown)
if err != nil {
return gopay.NULL, err
}
return string(bs), nil
}
// 资金账单
// 文档地址:https://qpay.qq.com/buss/wiki/38/3089
func (q *Client) AccRoll(ctx context.Context, bm gopay.BodyMap) (qqRsp string, err error) {
err = bm.CheckEmptyError("nonce_str", "bill_date", "acc_type")
if err != nil {
return gopay.NULL, err
}
accType := bm.GetString("acc_type")
if accType != "CASH" && accType != "MARKETING" {
return gopay.NULL, errors.New("acc_type error, please reference: https://qpay.qq.com/buss/wiki/38/3089")
}
bs, err := q.doQQPost(ctx, bm, accRoll)
if err != nil {
return gopay.NULL, err
}
return string(bs), nil
}
// 向QQ发送请求
func (q *Client) doQQPost(ctx context.Context, bm gopay.BodyMap, url string) (bs []byte, err error) {
if bm.GetString("mch_id") == gopay.NULL {
bm.Set("mch_id", q.MchId)
}
if bm.GetString("fee_type") == gopay.NULL {
bm.Set("fee_type", "CNY")
}
if bm.GetString("sign") == gopay.NULL {
sign := q.getReleaseSign(q.ApiKey, bm.GetString("sign_type"), bm)
bm.Set("sign", sign)
}
req := GenerateXml(bm)
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Request: %s", req)
}
res, bs, err := q.hc.Req(xhttp.TypeXML).Post(url).SendString(req).EndBytes(ctx)
if err != nil {
return nil, err
}
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Response: %d, %s", res.StatusCode, string(bs))
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
if strings.Contains(string(bs), "HTML") {
return nil, errors.New(string(bs))
}
return bs, nil
}
// 向QQ发送请求 TLS
func (q *Client) doQQPostTLS(ctx context.Context, bm gopay.BodyMap, url string) (bs []byte, err error) {
if bm.GetString("mch_id") == gopay.NULL {
bm.Set("mch_id", q.MchId)
}
if bm.GetString("fee_type") == gopay.NULL {
bm.Set("fee_type", "CNY")
}
if bm.GetString("sign") == gopay.NULL {
sign := q.getReleaseSign(q.ApiKey, bm.GetString("sign_type"), bm)
bm.Set("sign", sign)
}
req := GenerateXml(bm)
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Request: %s", req)
}
res, bs, err := q.tlsHc.Req(xhttp.TypeXML).Post(url).SendString(req).EndBytes(ctx)
if err != nil {
return nil, err
}
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Response: %d, %s", res.StatusCode, string(bs))
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
if strings.Contains(string(bs), "HTML") {
return nil, errors.New(string(bs))
}
return bs, nil
}
// Get请求、正式
func (q *Client) doQQGet(ctx context.Context, bm gopay.BodyMap, url, signType string) (bs []byte, err error) {
if bm.GetString("mch_id") == gopay.NULL {
bm.Set("mch_id", q.MchId)
}
bm.Remove("sign")
sign := q.getReleaseSign(q.ApiKey, signType, bm)
bm.Set("sign", sign)
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Request: %s", bm.JsonBody())
}
param := bm.EncodeURLParams()
uri := url + "?" + param
res, bs, err := q.hc.Req(xhttp.TypeXML).Get(uri).EndBytes(ctx)
if err != nil {
return nil, err
}
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Response: %d, %s", res.StatusCode, string(bs))
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
if strings.Contains(string(bs), "HTML") || strings.Contains(string(bs), "html") {
return nil, errors.New(string(bs))
}
return bs, nil
}
func (q *Client) doQQRed(ctx context.Context, bm gopay.BodyMap, url string) (bs []byte, err error) {
if bm.GetString("mch_id") == gopay.NULL {
bm.Set("mch_id", q.MchId)
}
if bm.GetString("sign") == gopay.NULL {
sign := GetReleaseSign(q.ApiKey, SignType_MD5, bm)
bm.Set("sign", sign)
}
req := GenerateXml(bm)
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Request: %s", req)
}
res, bs, err := q.tlsHc.Req(xhttp.TypeXML).Post(url).SendString(req).EndBytes(ctx)
if err != nil {
return nil, err
}
if q.DebugSwitch == gopay.DebugOn {
q.logger.Debugf("QQ_Response: %d, %s", res.StatusCode, string(bs))
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
}
if strings.Contains(string(bs), "HTML") {
return nil, errors.New(string(bs))
}
return bs, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。