123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- package qq
- import (
- "crypto/tls"
- "crypto/x509"
- "encoding/xml"
- "errors"
- "fmt"
- "strings"
- "sync"
- "github.com/iGoogle-ink/gopay"
- )
- type Client struct {
- MchId string
- ApiKey string
- IsProd bool
- certificate tls.Certificate
- certPool *x509.CertPool
- mu sync.RWMutex
- }
- // 初始化QQ客户端(正式环境)
- // mchId:商户ID
- // ApiKey:API秘钥值
- func NewClient(mchId, apiKey string) (client *Client) {
- if mchId != gopay.NULL && apiKey != gopay.NULL {
- return &Client{
- MchId: mchId,
- ApiKey: apiKey,
- }
- }
- return nil
- }
- // 提交付款码支付
- // 文档地址:https://qpay.qq.com/buss/wiki/1/1122
- func (q *Client) MicroPay(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.doQQ(bm, microPay, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(MicroPayResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 撤销订单
- // 文档地址:https://qpay.qq.com/buss/wiki/1/1125
- func (q *Client) Reverse(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.doQQ(bm, reverse, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(ReverseResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 统一下单
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1203
- func (q *Client) UnifiedOrder(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.doQQ(bm, unifiedOrder, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(UnifiedOrderResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 订单查询
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1205
- func (q *Client) OrderQuery(bm gopay.BodyMap) (qqRsp *OrderQueryResponse, err error) {
- err = bm.CheckEmptyError("nonce_str")
- if err != nil {
- return nil, err
- }
- if bm.Get("out_trade_no") == gopay.NULL && bm.Get("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.doQQ(bm, orderQuery, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(OrderQueryResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 关闭订单
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1206
- func (q *Client) CloseOrder(bm gopay.BodyMap) (qqRsp *CloseOrderResponse, err error) {
- err = bm.CheckEmptyError("nonce_str", "out_trade_no")
- if err != nil {
- return nil, err
- }
- bs, err := q.doQQ(bm, orderClose, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(CloseOrderResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 申请退款
- // 注意:如已使用client.AddCertFilePath()添加过证书,参数certFilePath、keyFilePath、pkcs12FilePath全传空字符串 "",否则,3证书Path均不可空
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1207
- func (q *Client) Refund(bm gopay.BodyMap, certFilePath, keyFilePath, pkcs12FilePath interface{}) (qqRsp *RefundResponse, err error) {
- if err = checkCertFilePath(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.Get("out_trade_no") == gopay.NULL && bm.Get("transaction_id") == gopay.NULL {
- return nil, errors.New("out_trade_no and transaction_id are not allowed to be null at the same time")
- }
- tlsConfig, err := q.addCertConfig(certFilePath, keyFilePath, pkcs12FilePath)
- if err != nil {
- return nil, err
- }
- bs, err := q.doQQ(bm, refund, tlsConfig)
- if err != nil {
- return nil, err
- }
- qqRsp = new(RefundResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 退款查询
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1208
- func (q *Client) RefundQuery(bm gopay.BodyMap) (qqRsp *RefundQueryResponse, err error) {
- err = bm.CheckEmptyError("nonce_str")
- if err != nil {
- return nil, err
- }
- if bm.Get("refund_id") == gopay.NULL && bm.Get("out_refund_no") == gopay.NULL && bm.Get("transaction_id") == gopay.NULL && bm.Get("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.doQQ(bm, refundQuery, nil)
- if err != nil {
- return nil, err
- }
- qqRsp = new(RefundQueryResponse)
- if err = xml.Unmarshal(bs, qqRsp); err != nil {
- return nil, fmt.Errorf("xml.Unmarshal(%s):%w", string(bs), err)
- }
- return qqRsp, nil
- }
- // 交易账单
- // 文档地址:https://qpay.qq.com/buss/wiki/38/1209
- func (q *Client) StatementDown(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.Get("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.doQQ(bm, statementDown, nil)
- if err != nil {
- return gopay.NULL, err
- }
- return string(bs), nil
- }
- // 资金账单
- // 文档地址:https://qpay.qq.com/buss/wiki/38/3089
- func (q *Client) AccRoll(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.Get("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.doQQ(bm, accRoll, nil)
- if err != nil {
- return gopay.NULL, err
- }
- return string(bs), nil
- }
- // 向QQ发送请求
- func (q *Client) doQQ(bm gopay.BodyMap, url string, tlsConfig *tls.Config) (bs []byte, err error) {
- bm.Set("mch_id", q.MchId)
- if bm.Get("fee_type") == gopay.NULL {
- bm.Set("fee_type", "CNY")
- }
- if bm.Get("sign") == gopay.NULL {
- var sign string
- sign = getReleaseSign(q.ApiKey, bm.Get("sign_type"), bm)
- bm.Set("sign", sign)
- }
- httpClient := gopay.NewHttpClient()
- if tlsConfig != nil {
- httpClient.SetTLSConfig(tlsConfig)
- }
- res, bs, errs := httpClient.Type(gopay.TypeXML).Post(url).SendString(generateXml(bm)).EndBytes()
- if len(errs) > 0 {
- return nil, errs[0]
- }
- 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
- }
|