增加vendor

This commit is contained in:
hoteas
2022-05-24 13:49:25 +08:00
parent 77753a123f
commit b638d8bd65
742 changed files with 509206 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 提交申请单API
// 注意:本接口会提交一些敏感信息,需调用 client.V3EncryptText() 进行加密
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_1.shtml
func (c *ClientV3) V3Apply4SubSubmit(ctx context.Context, bm gopay.BodyMap) (*Apply4SubSubmitRsp, error) {
if err := bm.CheckEmptyError("business_code", "contact_info", "subject_info", "business_info", "settlement_info", "bank_account_info"); err != nil {
return nil, err
}
authorization, err := c.authorization(MethodPost, v3Apply4SubSubmit, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3Apply4SubSubmit, authorization)
if err != nil {
return nil, err
}
wxRsp := &Apply4SubSubmitRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Apply4SubSubmit)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 通过业务申请编号查询申请状态API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_2.shtml
func (c *ClientV3) V3Apply4SubQueryByBusinessCode(ctx context.Context, businessCode string) (*Apply4SubQueryRsp, error) {
uri := fmt.Sprintf(v3Apply4SubQueryByBusinessCode, businessCode)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &Apply4SubQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Apply4SubQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 通过申请单号查询申请状态API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_2.shtml
func (c *ClientV3) V3Apply4SubQueryByApplyId(ctx context.Context, applyId string) (*Apply4SubQueryRsp, error) {
uri := fmt.Sprintf(v3Apply4SubQueryByApplyId, applyId)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &Apply4SubQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Apply4SubQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 修改结算账号 API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_3.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_1_4.shtml
func (c *ClientV3) V3Apply4SubModifySettlement(ctx context.Context, bm gopay.BodyMap) (*EmptyRsp, error) {
if err := bm.CheckEmptyError("sub_mchid", "account_type", "account_bank", "account_number"); err != nil {
return nil, err
}
postUrl := fmt.Sprintf(v3Apply4SubModifySettlement, bm["sub_mchid"])
bm.Remove("sub_mchid")
authorization, err := c.authorization(MethodPost, postUrl, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, postUrl, authorization)
if err != nil {
return nil, err
}
wxRsp := &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询结算账户 API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter11_1_4.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_1_5.shtml
func (c *ClientV3) V3Apply4SubQuerySettlement(ctx context.Context, subMchId string) (*Apply4SubQuerySettlementRsp, error) {
uri := fmt.Sprintf(v3Apply4SubQuerySettlement, subMchId)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &Apply4SubQuerySettlementRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Apply4SubQuerySettlement)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+177
View File
@@ -0,0 +1,177 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 获取对私银行卡号开户银行
// 注意:accountNo 需此方法加密:client.V3EncryptText()
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_1.shtml
func (c *ClientV3) V3BankSearchBank(ctx context.Context, accountNo string) (wxRsp *BankSearchBankRsp, err error) {
uri := v3BankSearchBank + "?account_number=" + url.QueryEscape(accountNo)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchBankRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchBank)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询支持个人业务的银行列表
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_2.shtml
func (c *ClientV3) V3BankSearchPersonalList(ctx context.Context, limit, offset int) (wxRsp *BankSearchPersonalListRsp, err error) {
if limit == 0 {
limit = 20
}
uri := v3BankSearchPersonalList + "?limit=" + util.Int2String(limit) + "&offset=" + util.Int2String(offset)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchPersonalListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchList)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询支持对公业务的银行列表
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_3.shtml
func (c *ClientV3) V3BankSearchCorporateList(ctx context.Context, limit, offset int) (wxRsp *BankSearchCorporateListRsp, err error) {
if limit == 0 {
limit = 20
}
uri := v3BankSearchCorporateList + "?limit=" + util.Int2String(limit) + "&offset=" + util.Int2String(offset)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchCorporateListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchList)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询省份列表
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_4.shtml
func (c *ClientV3) V3BankSearchProvinceList(ctx context.Context) (wxRsp *BankSearchProvinceListRsp, err error) {
authorization, err := c.authorization(MethodGet, v3BankSearchProvinceList, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, v3BankSearchProvinceList, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchProvinceListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchProvince)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询城市列表
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_5.shtml
func (c *ClientV3) V3BankSearchCityList(ctx context.Context, provinceCode int) (wxRsp *BankSearchCityListRsp, err error) {
url := fmt.Sprintf(v3BankSearchCityList, provinceCode)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchCityListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchCity)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询支行列表
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/Offline/apis/chapter11_2_6.shtml
func (c *ClientV3) V3BankSearchBranchList(ctx context.Context, bankAliasCode string, cityCode, limit, offset int) (wxRsp *BankSearchBranchListRsp, err error) {
if limit == 0 {
limit = 20
}
uri := fmt.Sprintf(v3BankSearchBranchList, bankAliasCode) + "?city_code=" + util.Int2String(cityCode) + "&limit=" + util.Int2String(limit) + "&offset=" + util.Int2String(offset)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BankSearchBranchListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BankSearchBranch)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+188
View File
@@ -0,0 +1,188 @@
package wechat
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 申请交易账单API
// 注意:如 bill_date 为空,默认查前一天的
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_6.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_6.shtml
func (c *ClientV3) V3BillTradeBill(ctx context.Context, bm gopay.BodyMap) (wxRsp *BillRsp, err error) {
if bm != nil {
if bm.GetString("bill_date") == util.NULL {
now := time.Now()
yesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.Local).Format(util.DateLayout)
bm.Set("bill_date", yesterday)
}
}
uri := v3TradeBill + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BillRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TradeBill)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请资金账单API
// 注意:如 bill_date 为空,默认查前一天的
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_7.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_7.shtml
func (c *ClientV3) V3BillFundFlowBill(ctx context.Context, bm gopay.BodyMap) (wxRsp *BillRsp, err error) {
if bm != nil {
if bm.GetString("bill_date") == util.NULL {
now := time.Now()
yesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.Local).Format(util.DateLayout)
bm.Set("bill_date", yesterday)
}
}
uri := v3FundFlowBill + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BillRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TradeBill)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请特约商户资金账单API
// 注意:如 bill_date 为空,默认查前一天的
// Code = 0 is success
// 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter7_2.shtml
func (c *ClientV3) V3BillEcommerceFundFlowBill(ctx context.Context, bm gopay.BodyMap) (wxRsp *EcommerceFundFlowBillRsp, err error) {
if bm != nil {
if bm.GetString("bill_date") == util.NULL {
now := time.Now()
yesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.Local).Format(util.DateLayout)
bm.Set("bill_date", yesterday)
}
if bm.GetString("account_type") == util.NULL {
bm.Set("account_type", "ALL")
}
if bm.GetString("algorithm") == util.NULL {
bm.Set("algorithm", "AEAD_AES_256_GCM")
}
}
uri := v3EcommerceFundFlowBill + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceFundFlowBillRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(DownloadBill)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请单个子商户资金账单API
// 注意:如 bill_date 为空,默认查前一天的
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_12.shtml
func (c *ClientV3) V3BillSubFundFlowBill(ctx context.Context, bm gopay.BodyMap) (wxRsp *BillRsp, err error) {
if bm != nil {
if bm.GetString("bill_date") == util.NULL {
now := time.Now()
yesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.Local).Format(util.DateLayout)
bm.Set("bill_date", yesterday)
}
}
uri := v3SubFundFlowBill + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BillRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TradeBill)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 下载账单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_8.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_8.shtml
func (c *ClientV3) V3BillDownLoadBill(ctx context.Context, downloadUrl string) (fileBytes []byte, err error) {
if downloadUrl == gopay.NULL {
return nil, errors.New("invalid download url")
}
split := strings.Split(downloadUrl, ".com")
if len(split) != 2 {
return nil, errors.New("invalid download url")
}
authorization, err := c.authorization(MethodGet, split[1], nil)
if err != nil {
return nil, err
}
res, _, bs, err := c.doProdGet(ctx, split[1], authorization)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, errors.New(string(bs))
}
return bs, nil
}
+59
View File
@@ -0,0 +1,59 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 商圈积分同步
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_6_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_6_2.shtml
func (c *ClientV3) V3BusinessPointsSync(ctx context.Context, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusinessPointsSync, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusinessPointsSync, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商圈积分授权查询
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_6_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_6_4.shtml
func (c *ClientV3) V3BusinessAuthPointsQuery(ctx context.Context, appid, openid string) (*BusinessAuthPointsQueryRsp, error) {
uri := fmt.Sprintf(v3BusinessAuthPointsQuery, openid) + "?appid=" + appid
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &BusinessAuthPointsQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusinessAuthPointsQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+284
View File
@@ -0,0 +1,284 @@
package wechat
import (
"context"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"runtime"
"sort"
"sync"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/aes"
"github.com/go-pay/gopay/pkg/errgroup"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xhttp"
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/pkg/xpem"
"github.com/go-pay/gopay/pkg/xtime"
)
// 获取微信平台证书公钥(获取后自行保存使用,如需定期刷新功能,自行实现)
// 注意事项
// 如果自行实现验证平台签名逻辑的话,需要注意以下事项:
// - 程序实现定期更新平台证书的逻辑,不要硬编码验证应答消息签名的平台证书
// - 定期调用该接口,间隔时间小于12小时
// - 加密请求消息中的敏感信息时,使用最新的平台证书(即:证书启用时间较晚的证书)
// 文档说明:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml
func GetPlatformCerts(ctx context.Context, mchid, apiV3Key, serialNo, privateKey string) (certs *PlatformCertRsp, err error) {
var (
eg = new(errgroup.Group)
mu sync.Mutex
jb = ""
)
// Prepare
priKey, err := xpem.DecodePrivateKey([]byte(privateKey))
if err != nil {
return nil, err
}
timestamp := time.Now().Unix()
nonceStr := util.RandomString(32)
ts := util.Int642String(timestamp)
_str := MethodGet + "\n" + v3GetCerts + "\n" + ts + "\n" + nonceStr + "\n" + jb + "\n"
// Sign
h := sha256.New()
h.Write([]byte(_str))
result, err := rsa.SignPKCS1v15(rand.Reader, priKey, crypto.SHA256, h.Sum(nil))
if err != nil {
return nil, fmt.Errorf("[%w]: %+v", gopay.SignatureErr, err)
}
sign := base64.StdEncoding.EncodeToString(result)
// Authorization
authorization := Authorization + ` mchid="` + mchid + `",nonce_str="` + nonceStr + `",timestamp="` + ts + `",serial_no="` + serialNo + `",signature="` + sign + `"`
// Request
var url = v3BaseUrlCh + v3GetCerts
httpClient := xhttp.NewClient()
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, serialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err := httpClient.Type(xhttp.TypeJSON).Get(url).EndBytes(ctx)
if err != nil {
return nil, err
}
certs = &PlatformCertRsp{Code: Success}
if res.StatusCode != http.StatusOK {
certs.Code = res.StatusCode
certs.Error = string(bs)
return certs, nil
}
// Parse
certRsp := new(PlatformCert)
if err = json.Unmarshal(bs, certRsp); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s)%+v", string(bs), err)
}
for _, v := range certRsp.Data {
cert := v
if cert.EncryptCertificate != nil {
ec := cert.EncryptCertificate
eg.Go(func(ctx context.Context) error {
cipherBytes, _ := base64.StdEncoding.DecodeString(ec.Ciphertext)
pubKeyBytes, err := aes.GCMDecrypt(cipherBytes, []byte(ec.Nonce), []byte(ec.AssociatedData), []byte(apiV3Key))
if err != nil {
return fmt.Errorf("aes.GCMDecrypt, err:%+v", err)
}
pci := &PlatformCertItem{
EffectiveTime: cert.EffectiveTime,
ExpireTime: cert.ExpireTime,
PublicKey: string(pubKeyBytes),
SerialNo: cert.SerialNo,
}
mu.Lock()
certs.Certs = append(certs.Certs, pci)
mu.Unlock()
return nil
})
}
}
if err = eg.Wait(); err != nil {
return nil, err
}
return certs, nil
}
// 设置 微信支付平台证书 和 证书序列号
// 注意1:如已开启自动验签功能 client.AutoVerifySign(),无需再调用此方法设置
// 注意2:请预先通过 wechat.GetPlatformCerts() 获取 微信平台公钥证书 和 证书序列号
// 部分接口请求参数中敏感信息加密,使用此 微信支付平台公钥 和 证书序列号
func (c *ClientV3) SetPlatformCert(wxPublicKeyContent []byte, wxSerialNo string) (client *ClientV3) {
pubKey, err := xpem.DecodePublicKey(wxPublicKeyContent)
if err != nil {
xlog.Errorf("SetPlatformCert(%s),err:%+v", wxPublicKeyContent, err)
}
if pubKey != nil {
c.wxPublicKey = pubKey
}
c.WxSerialNo = wxSerialNo
return c
}
// 获取 微信平台证书(readonly)
func (c *ClientV3) WxPublicKey() (wxPublicKey *rsa.PublicKey) {
wxPublicKey = c.wxPublicKey
return
}
// 获取并选择最新的有效证书
func (c *ClientV3) GetAndSelectNewestCert() (cert, serialNo string, err error) {
certs, err := c.getPlatformCerts()
if err != nil {
return gopay.NULL, gopay.NULL, err
}
if certs.Code == Success && len(certs.Certs) > 0 {
// only one
if len(certs.Certs) == 1 {
formatExpire := xtime.FormatDateTime(certs.Certs[0].ExpireTime)
expireTime, err := time.ParseInLocation(xtime.TimeLayout, formatExpire, time.Local)
if err != nil {
return gopay.NULL, gopay.NULL, fmt.Errorf("time.ParseInLocation(%s, %s),err:%w", xtime.TimeLayout, formatExpire, err)
}
if time.Now().Unix() >= expireTime.Unix() {
// 过期了
return gopay.NULL, gopay.NULL, fmt.Errorf("wechat platform API cert expired, expired time: %s", formatExpire)
}
return certs.Certs[0].PublicKey, certs.Certs[0].SerialNo, nil
}
// more one
var (
effectiveTs []int
certMap = make(map[int]*PlatformCertItem)
)
for _, v := range certs.Certs {
formatEffective := xtime.FormatDateTime(v.EffectiveTime)
effectiveTime, err := time.ParseInLocation(xtime.TimeLayout, formatEffective, time.Local)
if err != nil {
return gopay.NULL, gopay.NULL, fmt.Errorf("time.ParseInLocation(%s, %s),err:%w", xtime.TimeLayout, formatEffective, err)
}
eu := int(effectiveTime.Unix())
effectiveTs = append(effectiveTs, eu)
certMap[eu] = v
}
sort.Ints(effectiveTs)
// newest cert
newestCert := certMap[effectiveTs[len(effectiveTs)-1]]
formatExpire := xtime.FormatDateTime(newestCert.ExpireTime)
expireTime, err := time.ParseInLocation(xtime.TimeLayout, formatExpire, time.Local)
if err != nil {
return gopay.NULL, gopay.NULL, fmt.Errorf("time.ParseInLocation(%s, %s),err:%w", xtime.TimeLayout, formatExpire, err)
}
if time.Now().Unix() >= expireTime.Unix() {
// 过期了
return gopay.NULL, gopay.NULL, fmt.Errorf("wechat platform API cert expired, expired time: %s", formatExpire)
}
return newestCert.PublicKey, newestCert.SerialNo, nil
}
// failed
return gopay.NULL, gopay.NULL, fmt.Errorf("GetAndSelectNewestCert() failed or certs is nil: %+v", certs)
}
// 推荐直接使用 client.GetAndSelectNewestCert() 方法
// 获取微信平台证书公钥(获取后自行保存使用,如需定期刷新功能,自行实现)
// 注意事项
// 如果自行实现验证平台签名逻辑的话,需要注意以下事项:
// - 程序实现定期更新平台证书的逻辑,不要硬编码验证应答消息签名的平台证书
// - 定期调用该接口,间隔时间小于12小时
// - 加密请求消息中的敏感信息时,使用最新的平台证书(即:证书启用时间较晚的证书)
// 文档说明:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml
func (c *ClientV3) getPlatformCerts() (certs *PlatformCertRsp, err error) {
var (
eg = new(errgroup.Group)
mu sync.Mutex
)
authorization, err := c.authorization(MethodGet, v3GetCerts, nil)
if err != nil {
return nil, err
}
res, _, bs, err := c.doProdGet(c.ctx, v3GetCerts, authorization)
if err != nil {
return nil, err
}
certs = &PlatformCertRsp{Code: Success}
if res.StatusCode != http.StatusOK {
certs.Code = res.StatusCode
certs.Error = string(bs)
return certs, nil
}
certRsp := new(PlatformCert)
if err = json.Unmarshal(bs, certRsp); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s)%+v", string(bs), err)
}
for _, v := range certRsp.Data {
cert := v
if cert.EncryptCertificate != nil {
ec := cert.EncryptCertificate
eg.Go(func(ctx context.Context) error {
pubKey, err := c.decryptCerts(ec.Ciphertext, ec.Nonce, ec.AssociatedData)
if err != nil {
return err
}
pci := &PlatformCertItem{
EffectiveTime: cert.EffectiveTime,
ExpireTime: cert.ExpireTime,
PublicKey: pubKey,
SerialNo: cert.SerialNo,
}
mu.Lock()
certs.Certs = append(certs.Certs, pci)
mu.Unlock()
return nil
})
}
}
if err = eg.Wait(); err != nil {
return nil, err
}
return certs, nil
}
// 解密加密的证书
func (c *ClientV3) decryptCerts(ciphertext, nonce, additional string) (wxCerts string, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), c.ApiV3Key)
if err != nil {
return "", fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
return string(decrypt), nil
}
func (c *ClientV3) autoCheckCertProc() {
defer func() {
if r := recover(); r != nil {
buf := make([]byte, 64<<10)
buf = buf[:runtime.Stack(buf, false)]
xlog.Errorf("autoCheckCertProc: panic recovered: %s\n%s", r, buf)
// 重启
c.autoCheckCertProc()
}
}()
for {
time.Sleep(time.Hour * 12)
wxPk, wxSerialNo, err := c.GetAndSelectNewestCert()
if err != nil {
xlog.Errorf("c.GetAndSelectNewestCert()err:%+v", err)
continue
}
// decode cert
pubKey, err := xpem.DecodePublicKey([]byte(wxPk))
if err != nil {
xlog.Errorf("xpem.DecodePublicKey(%s)err:%+v", wxPk, err)
continue
}
c.wxPublicKey = pubKey
c.WxSerialNo = wxSerialNo
}
}
+284
View File
@@ -0,0 +1,284 @@
package wechat
import (
"context"
"crypto/rsa"
"fmt"
"net/http"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xhttp"
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/pkg/xpem"
)
// ClientV3 微信支付 V3
type ClientV3 struct {
Mchid string
ApiV3Key []byte
SerialNo string
WxSerialNo string
autoSign bool
privateKey *rsa.PrivateKey
wxPublicKey *rsa.PublicKey
ctx context.Context
DebugSwitch gopay.DebugSwitch
}
// NewClientV3 初始化微信客户端 V3
// mchid:商户ID 或者服务商模式的 sp_mchid
// serialNo:商户API证书的证书序列号
// apiV3KeyAPIv3Key,商户平台获取
// privateKey:商户API证书下载后,私钥 apiclient_key.pem 读取后的字符串内容
func NewClientV3(mchid, serialNo, apiV3Key, privateKey string) (client *ClientV3, err error) {
if mchid == util.NULL || serialNo == util.NULL || apiV3Key == util.NULL || privateKey == util.NULL {
return nil, gopay.MissWechatInitParamErr
}
priKey, err := xpem.DecodePrivateKey([]byte(privateKey))
if err != nil {
return nil, err
}
client = &ClientV3{
Mchid: mchid,
SerialNo: serialNo,
ApiV3Key: []byte(apiV3Key),
privateKey: priKey,
ctx: context.Background(),
DebugSwitch: gopay.DebugOff,
}
return client, nil
}
// AutoVerifySign 开启请求完自动验签功能(默认不开启,推荐开启)
// 开启自动验签,自动开启每12小时一次轮询,请求最新证书操作
func (c *ClientV3) AutoVerifySign() (err error) {
wxPk, wxSerialNo, err := c.GetAndSelectNewestCert()
if err != nil {
return err
}
// decode cert
pubKey, err := xpem.DecodePublicKey([]byte(wxPk))
if err != nil {
return err
}
c.wxPublicKey = pubKey
c.WxSerialNo = wxSerialNo
c.autoSign = true
go c.autoCheckCertProc()
return
}
func (c *ClientV3) doProdPostWithHeader(ctx context.Context, headerMap map[string]string, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
for k, v := range headerMap {
httpClient.Header.Add(k, v)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Post(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPost(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Post(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdGet(ctx context.Context, uri, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + uri
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_Url: %s", url)
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Get(url).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPut(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Put(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdDelete(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Delete(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPostFile(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.GetString("meta"))
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeMultipartFormData).Post(url).SendMultipartBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
func (c *ClientV3) doProdPatch(ctx context.Context, bm gopay.BodyMap, path, authorization string) (res *http.Response, si *SignInfo, bs []byte, err error) {
var url = v3BaseUrlCh + path
httpClient := xhttp.NewClient()
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_RequestBody: %s", bm.JsonBody())
xlog.Debugf("Wechat_V3_Authorization: %s", authorization)
}
httpClient.Header.Add(HeaderAuthorization, authorization)
httpClient.Header.Add(HeaderRequestID, fmt.Sprintf("%s-%d", util.RandomString(21), time.Now().Unix()))
httpClient.Header.Add(HeaderSerial, c.WxSerialNo)
httpClient.Header.Add("Accept", "*/*")
res, bs, err = httpClient.Type(xhttp.TypeJSON).Patch(url).SendBodyMap(bm).EndBytes(ctx)
if err != nil {
return nil, nil, nil, err
}
si = &SignInfo{
HeaderTimestamp: res.Header.Get(HeaderTimestamp),
HeaderNonce: res.Header.Get(HeaderNonce),
HeaderSignature: res.Header.Get(HeaderSignature),
HeaderSerial: res.Header.Get(HeaderSerial),
SignBody: string(bs),
}
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_Response: %d > %s", res.StatusCode, string(bs))
xlog.Debugf("Wechat_Headers: %#v", res.Header)
xlog.Debugf("Wechat_SignInfo: %#v", si)
}
return res, si, bs, nil
}
+276
View File
@@ -0,0 +1,276 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 创建投诉通知回调地址API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_2.shtml
func (c *ClientV3) V3ComplaintNotifyUrlCreate(ctx context.Context, url string) (wxRsp *ComplaintNotifyUrlRsp, err error) {
bm := make(gopay.BodyMap)
bm.Set("url", url)
authorization, err := c.authorization(MethodPost, v3ComplaintNotifyUrlCreate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ComplaintNotifyUrlCreate, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintNotifyUrlRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintNotifyUrl)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询投诉通知回调地址API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_3.shtml
func (c *ClientV3) V3ComplaintNotifyUrlQuery(ctx context.Context) (wxRsp *ComplaintNotifyUrlRsp, err error) {
authorization, err := c.authorization(MethodGet, v3ComplaintNotifyUrlQuery, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, v3ComplaintNotifyUrlQuery, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintNotifyUrlRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintNotifyUrl)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 更新投诉通知回调地址API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_4.shtml
func (c *ClientV3) V3ComplaintNotifyUrlUpdate(ctx context.Context, url string) (wxRsp *ComplaintNotifyUrlRsp, err error) {
bm := make(gopay.BodyMap)
bm.Set("url", url)
authorization, err := c.authorization(MethodPut, v3ComplaintNotifyUrlUpdate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPut(ctx, bm, v3ComplaintNotifyUrlUpdate, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintNotifyUrlRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintNotifyUrl)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 删除投诉通知回调地址API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_5.shtml
func (c *ClientV3) V3ComplaintNotifyUrlDelete(ctx context.Context) (wxRsp *EmptyRsp, err error) {
authorization, err := c.authorization(MethodDelete, v3ComplaintNotifyUrlDelete, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdDelete(ctx, nil, v3ComplaintNotifyUrlDelete, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商户上传反馈图片API
// 注意:图片不能超过2MB
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_10.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_10.shtml
func (c *ClientV3) V3ComplaintUploadImage(ctx context.Context, fileName, fileSha256 string, img *util.File) (wxRsp *MediaUploadRsp, err error) {
bmFile := make(gopay.BodyMap)
bmFile.Set("filename", fileName).Set("sha256", fileSha256)
authorization, err := c.authorization(MethodPost, v3ComplaintUploadImage, bmFile)
if err != nil {
return nil, err
}
bm := make(gopay.BodyMap)
bm.SetBodyMap("meta", func(bm gopay.BodyMap) {
bm.Set("filename", fileName).Set("sha256", fileSha256)
}).SetFormFile("file", img)
res, si, bs, err := c.doProdPostFile(ctx, bm, v3ComplaintUploadImage, authorization)
if err != nil {
return nil, err
}
wxRsp = &MediaUploadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MediaUpload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询投诉单列表API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_11.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_11.shtml
func (c *ClientV3) V3ComplaintList(ctx context.Context, bm gopay.BodyMap) (wxRsp *ComplaintListRsp, err error) {
uri := v3ComplaintList + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintList)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询投诉协商历史API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_12.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_12.shtml
func (c *ClientV3) V3ComplaintNegotiationHistory(ctx context.Context, complaintId string, bm gopay.BodyMap) (wxRsp *ComplaintNegotiationHistoryRsp, err error) {
uri := fmt.Sprintf(v3ComplaintNegotiationHistory, complaintId) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintNegotiationHistoryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintNegotiationHistory)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询投诉单详情API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_13.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_13.shtml
func (c *ClientV3) V3ComplaintDetail(ctx context.Context, complaintId string) (wxRsp *ComplaintDetailRsp, err error) {
url := fmt.Sprintf(v3ComplaintDetail, complaintId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ComplaintDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ComplaintDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 提交回复API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_14.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_14.shtml
func (c *ClientV3) V3ComplaintResponse(ctx context.Context, complaintId string, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3ComplaintResponse, complaintId)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 反馈处理完成API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_15.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter10_2_15.shtml
func (c *ClientV3) V3ComplaintComplete(ctx context.Context, complaintId string, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3ComplaintComplete, complaintId)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+281
View File
@@ -0,0 +1,281 @@
package wechat
const (
Success = 0
SignTypeRSA = "RSA"
MethodGet = "GET"
MethodPost = "POST"
MethodPut = "PUT"
MethodDelete = "DELETE"
MethodPATCH = "PATCH"
HeaderAuthorization = "Authorization"
HeaderRequestID = "Request-ID"
HeaderTimestamp = "Wechatpay-Timestamp"
HeaderNonce = "Wechatpay-Nonce"
HeaderSignature = "Wechatpay-Signature"
HeaderSerial = "Wechatpay-Serial"
Authorization = "WECHATPAY2-SHA256-RSA2048"
v3BaseUrlCh = "https://api.mch.weixin.qq.com" // 中国国内
v3GetCerts = "/v3/certificates"
// 基础支付(直连模式)
v3ApiApp = "/v3/pay/transactions/app" // APP 下单
v3ApiJsapi = "/v3/pay/transactions/jsapi" // JSAPI 下单
v3ApiNative = "/v3/pay/transactions/native" // Native 下单
v3ApiH5 = "/v3/pay/transactions/h5" // H5 下单
v3ApiQueryOrderTransactionId = "/v3/pay/transactions/id/%s" // transaction_id 查询订单
v3ApiQueryOrderOutTradeNo = "/v3/pay/transactions/out-trade-no/%s" // out_trade_no 查询订单
v3ApiCloseOrder = "/v3/pay/transactions/out-trade-no/%s/close" // out_trade_no 关闭订单
// 基础支付(服务商模式)
v3ApiPartnerPayApp = "/v3/pay/partner/transactions/app" // partner APP 下单
v3ApiPartnerJsapi = "/v3/pay/partner/transactions/jsapi" // partner JSAPI 下单
v3ApiPartnerNative = "/v3/pay/partner/transactions/native" // partner Native 下单
v3ApiPartnerH5 = "/v3/pay/partner/transactions/h5" // partner H5 下单
v3ApiPartnerQueryOrderTransactionId = "/v3/pay/partner/transactions/id/%s" // partner transaction_id 查询订单
v3ApiPartnerQueryOrderOutTradeNo = "/v3/pay/partner/transactions/out-trade-no/%s" // partner out_trade_no 查询订单
v3ApiPartnerCloseOrder = "/v3/pay/partner/transactions/out-trade-no/%s/close" // partner out_trade_no 关闭订单
// 基础支付(合单支付)
v3CombinePayApp = "/v3/combine-transactions/app"
v3CombinePayH5 = "/v3/combine-transactions/h5"
v3CombinePayJsapi = "/v3/combine-transactions/jsapi"
v3CombineNative = "/v3/combine-transactions/native"
v3CombineQuery = "/v3/combine-transactions/out-trade-no/%s"
v3CombineClose = "/v3/combine-transactions/out-trade-no/%s/close"
// 退款
v3DomesticRefund = "/v3/refund/domestic/refunds" // 申请退款
v3DomesticRefundQuery = "/v3/refund/domestic/refunds/%s" // 查询单笔退款
// 账单
v3TradeBill = "/v3/bill/tradebill" // 申请交易账单 GET
v3FundFlowBill = "/v3/bill/fundflowbill" // 申请资金账单 GET
v3EcommerceFundFlowBill = "/v3/ecommerce/bill/fundflowbill" // 申请特约商户资金账单 GET
v3SubFundFlowBill = "/v3/bill/sub-merchant-fundflowbill" // 申请单个子商户资金账单 GET
// 提现
v3Withdraw = "/v3/ecommerce/fund/withdraw" // 特约商户余额提现 POST
v3WithdrawStatusById = "/v3/ecommerce/fund/withdraw/%s" // withdraw_id 查询特约商户提现状态 GET
v3WithdrawStatusByNo = "/v3/ecommerce/fund/withdraw/out-request-no/%s" // out_request_no 查询特约商户提现状态 GET
v3EcommerceWithdraw = "/v3/merchant/fund/withdraw" // 电商平台预约提现 POST
v3EcommerceWithdrawStatusById = "/v3/merchant/fund/withdraw/withdraw-id/%s" // withdraw_id 电商平台查询预约提现状态 POST
v3EcommerceWithdrawStatusByNo = "/v3/merchant/fund/withdraw/out-request-no/%s" // out_request_no 电商平台查询预约提现状态 POST
v3WithdrawDownloadErrBill = "/v3/merchant/fund/withdraw/bill-type/%s" // bill_type 按日下载提现异常文件 GET
// 微信支付分(免确认模式)
v3ScoreDirectComplete = "/payscore/serviceorder/direct-complete" // 创单结单合并 POST
// 微信支付分(免确认预授权模式)
v3ScorePermission = "/v3/payscore/permissions" // 商户预授权 POST
v3ScorePermissionQuery = "/v3/payscore/permissions/authorization-code/%s" // authorization_code 查询用户授权记录(授权协议号) GET
v3ScorePermissionTerminate = "/v3/payscore/permissions/authorization-code/%s/terminate" // authorization_code 解除用户授权关系(授权协议号) POST
v3ScorePermissionOpenidQuery = "/v3/payscore/permissions/openid/%s" // openid 查询用户授权记录(openid GET
v3ScorePermissionOpenidTerminate = "/v3/payscore/permissions/openid/%s/terminate" // openid 解除用户授权记录(openid POST
// 微信支付分(公共API
v3ScoreOrderCreate = "/v3/payscore/serviceorder" // 创建支付分订单 POST
v3ScoreOrderQuery = "/v3/payscore/serviceorder" // 查询支付分订单 GET
v3ScoreOrderCancel = "/v3/payscore/serviceorder/%s/cancel" // out_trade_no 取消支付分订单 POST
v3ScoreOrderModify = "/v3/payscore/serviceorder/%s/modify" // out_trade_no 修改订单金额 POST
v3ScoreOrderComplete = "/v3/payscore/serviceorder/%s/complete" // out_trade_no 完结支付分订单 POST
v3ScoreOrderPay = "/v3/payscore/serviceorder/%s/pay" // out_trade_no 商户发起催收扣款 POST
v3ScoreOrderSync = "/v3/payscore/serviceorder/%s/sync" // out_trade_no 同步服务订单信息 POST
// 微信先享卡
v3CardPre = "/v3/discount-card/cards" // 预受理领卡请求 POST
v3CardAddUser = "/v3/discount-card/cards/%s/add-user-records" // out_card_code 增加用户记录 POST
v3CardQuery = "/v3/discount-card/cards/%s" // out_card_code 查询先享卡订单 GET
// 支付即服务
v3GuideReg = "/v3/smartguide/guides" // 服务人员注册 POST
v3GuideAssign = "/v3/smartguide/guides/%s/assign" // guide_id 服务人员分配 POST
v3GuideQuery = "/v3/smartguide/guides" // 服务人员查询 GET
v3GuideUpdate = "/v3/smartguide/guides/%s" // guide_id 服务人员信息更新 PATCH
// 智慧商圈
v3BusinessPointsSync = "/v3/businesscircle/points/notify" // 商圈积分同步 POST
v3BusinessAuthPointsQuery = "/v3/businesscircle/user-authorizations/%s" // openid 商圈积分授权查询 GET
// 代金券
v3FavorBatchCreate = "/v3/marketing/favor/coupon-stocks" // 创建代金券批次 POST
v3FavorBatchStart = "/v3/marketing/favor/stocks/%s/start" // stock_id 激活代金券批次 POST
v3FavorBatchGrant = "/v3/marketing/favor/users/%s/coupons" // openid 发放代金券批次 POST
v3FavorBatchPause = "/v3/marketing/favor/stocks/%s/pause" // stock_id 暂停代金券批次 POST
v3FavorBatchRestart = "/v3/marketing/favor/stocks/%s/restart" // stock_id 重启代金券批次 POST
v3FavorBatchList = "/v3/marketing/favor/stocks" // 条件查询批次列表 GET
v3FavorBatchDetail = "/v3/marketing/favor/stocks/%s" // stock_id 查询批次详情 GET
v3FavorDetail = "/v3/marketing/favor/users/%s/coupons/%s" // openid、coupon_id 查询代金券详情 GET
v3FavorMerchant = "/v3/marketing/favor/stocks/%s/merchants" // stock_id 查询代金券可用商户 GET
v3FavorItems = "/v3/marketing/favor/stocks/%s/items" // stock_id 查询代金券可用单品 GET
v3FavorUserCoupons = "/v3/marketing/favor/users/%s/coupons" // openid 根据商户号查用户的券 GET
v3FavorUseFlowDownload = "/v3/marketing/favor/stocks/%s/use-flow" // stock_id 下载批次核销明细 GET
v3FavorRefundFlowDownload = "/v3/marketing/favor/stocks/%s/refund-flow" // stock_id 下载批次退款明细 GET
v3FavorCallbackUrlSet = "/v3/marketing/favor/callbacks" // 设置消息通知地址 POST
v3FavorMediaUploadImage = "/v3/marketing/favor/media/image-upload" // 图片上传(营销专用) POST
// 商家券
v3BusiFavorBatchCreate = "/v3/marketing/busifavor/stocks" // 创建商家券 POST
v3BusiFavorBatchDetail = "/v3/marketing/busifavor/stocks/%s" // stock_id 查询商家券详情 GET
v3BusiFavorUse = "/v3/marketing/busifavor/coupons/use" // 核销用户券 POST
v3BusiFavorUserCoupons = "/v3/marketing/busifavor/users/%s/coupons" // openid 根据过滤条件查询用户券 GET
v3BusiFavorUserCouponDetail = "/v3/marketing/busifavor/users/%s/coupons/%s/appids/%s" // openid、coupon_code、appid 查询用户单张券详情 GET
v3BusiFavorCodeUpload = "/v3/marketing/busifavor/stocks/%s/couponcodes" // stock_id 上传预存code POST
v3BusiFavorCallbackUrlSet = "/v3/marketing/busifavor/callbacks" // 设置商家券事件通知地址 POST
v3BusiFavorCallbackUrl = "/v3/marketing/busifavor/callbacks" // 查询商家券事件通知地址 GET
v3BusiFavorAssociate = "/v3/marketing/busifavor/coupons/associate" // 关联订单信息 POST
v3BusiFavorDisassociate = "/v3/marketing/busifavor/coupons/disassociate" // 取消关联订单信息 POST
v3BusiFavorBatchUpdate = "/v3/marketing/busifavor/stocks/%s/budget" // stock_id 修改批次预算 PATCH
v3BusiFavorInfoUpdate = "/v3/marketing/busifavor/stocks/%s" // stock_id 修改商家券基本信息 PATCH
v3BusiFavorSend = "/v3/marketing/busifavor/coupons/%s/send" // card_id 发放消费卡 POST
v3BusiFavorReturn = "/v3/marketing/busifavor/coupons/return" // 申请退券 POST
v3BusiFavorDeactivate = "/v3/marketing/busifavor/coupons/deactivate" // 使券失效 POST
v3BusiFavorSubsidyPay = "/v3/marketing/busifavor/subsidy/pay-receipts" // 营销补差付款 POST
v3BusiFavorSubsidyPayDetail = "/v3/marketing/busifavor/subsidy/pay-receipts/%s" // subsidy_receipt_id 查询营销补差付款单详情 GET
// 委托营销(合作伙伴)
v3PartnershipsBuild = "/v3/marketing/partnerships/build" // 建立合作关系 POST
v3PartnershipsTerminate = "/v3/marketing/partnerships/terminate" // 终止合作关系 POST
v3PartnershipsList = "/v3/marketing/partnerships" // 查询合作关系列表 GET
// 点金计划(服务商)
v3GoldPlanManage = "/v3/goldplan/merchants/changegoldplanstatus" // 点金计划管理 POST
v3GoldPlanBillManage = "/v3/goldplan/merchants/changecustompagestatus" // 商家小票管理 POST
v3GoldPlanFilterManage = "/v3/goldplan/merchants/set-advertising-industry-filter" // 同业过滤标签管理 POST
v3GoldPlanOpenAdShow = "/v3/goldplan/merchants/open-advertising-show" // 开通广告展示 PATCH
v3GoldPlanCloseAdShow = "/v3/goldplan/merchants/close-advertising-show" // 关闭广告展示 POST
// 消费者投诉2.0
v3ComplaintList = "/v3/merchant-service/complaints-v2" // 查询投诉单列表 GET
v3ComplaintDetail = "/v3/merchant-service/complaints-v2/%s" // 查询投诉单详情 GET
v3ComplaintNegotiationHistory = "/v3/merchant-service/complaints-v2/%s/negotiation-historys" // 查询投诉协商历史 GET
v3ComplaintNotifyUrlCreate = "/v3/merchant-service/complaint-notifications" // 创建投诉通知回调地址 POST
v3ComplaintNotifyUrlQuery = "/v3/merchant-service/complaint-notifications" // 查询投诉通知回调地址 GET
v3ComplaintNotifyUrlUpdate = "/v3/merchant-service/complaint-notifications" // 查询投诉通知回调地址 PUT
v3ComplaintNotifyUrlDelete = "/v3/merchant-service/complaint-notifications" // 删除投诉通知回调地址 DELETE
v3ComplaintResponse = "/v3/merchant-service/complaints-v2/%s/response" // 提交回复 POST
v3ComplaintComplete = "/v3/merchant-service/complaints-v2/%s/complete" // 反馈处理完成 POST
v3ComplaintUploadImage = "/v3/merchant-service/images/upload" // 商户上传反馈图片 POST
// 分账(服务商)
v3ProfitShareOrder = "/v3/profitsharing/orders" // 请求分账 POST
v3ProfitShareQuery = "/v3/profitsharing/orders/%s" // 查询分账结果 GET
v3ProfitShareReturn = "/v3/profitsharing/return-orders" // 请求分账回退 POST
v3ProfitShareReturnResult = "/v3/profitsharing/return-orders/%s" // 查询分账回退结果 GET
v3ProfitShareUnfreeze = "/v3/profitsharing/orders/unfreeze" // 解冻剩余资金 POST
v3ProfitShareUnsplitAmount = "/v3/profitsharing/transactions/%s/amounts" // 查询剩余待分金额 GET
v3ProfitShareAddReceiver = "/v3/profitsharing/receivers/add" // 添加分账接收方 POST
v3ProfitShareDeleteReceiver = "/v3/profitsharing/receivers/delete" // 删除分账接收方 POST
v3ProfitShareMerchantConfigs = "/v3/profitsharing/merchant-configs/%s" // 查询最大分账比例API GET
v3ProfitShareBills = "/v3/profitsharing/bills" // 申请分账账单 GET
// 其他能力
v3MediaUploadImage = "/v3/merchant/media/upload" // 图片上传 POST
v3MediaUploadVideo = "/v3/merchant/media/video_upload" // 视频上传 POST
// 转账
v3Transfer = "/v3/transfer/batches" // 发起批量转账 POST
v3TransferQuery = "/v3/transfer/batches/batch-id/%s" // batch_id 微信批次单号查询批次单 GET
v3TransferDetail = "/v3/transfer/batches/batch-id/%s/details/detail-id/%s" // batch_id、detail_id 微信明细单号查询明细单 GET
v3TransferMerchantQuery = "/v3/transfer/batches/out-batch-no/%s" // out_batch_no 商家批次单号查询批次单 GET
v3TransferMerchantDetail = "/v3/transfer/batches/out-batch-no/%s/details/out-detail-no/%s" // out_batch_no、out_detail_no 商家明细单号查询明细单 GET
v3TransferReceipt = "/v3/transfer/bill-receipt" // 转账电子回单申请受理 POST
v3TransferReceiptQuery = "/v3/transfer/bill-receipt/%s" // out_batch_no 查询转账电子回单 GET
v3TransferDetailReceipt = "/v3/transfer-detail/electronic-receipts" // 转账明细电子回单受理 POST
v3TransferDetailReceiptQuery = "/v3/transfer-detail/electronic-receipts" // 查询转账明细电子回单受理结果 GET
// 转账(服务商)
v3PartnerTransfer = "/v3/partner-transfer/batches" // 发起批量转账 POST
v3PartnerTransferQuery = "/v3/partner-transfer/batches/batch-id/%s" // batch_id 微信批次单号查询批次单 GET
v3PartnerTransferDetail = "/v3/partner-transfer/batches/batch-id/%s/details/detail-id/%s" // batch_id、detail_id 微信明细单号查询明细单 GET
v3PartnerTransferMerchantQuery = "/v3/partner-transfer/batches/out-batch-no/%s" // out_batch_no 商家批次单号查询批次单 GET
v3PartnerTransferMerchantDetail = "/v3/partner-transfer/batches/out-batch-no/%s/details/out-detail-no/%s" // out_batch_no、out_detail_no 商家明细单号查询明细单 GET
// 余额
v3MerchantBalance = "/v3/merchant/fund/balance/%s" // account_type 查询账户实时余额 GET
v3MerchantDayBalance = "/v3/merchant/fund/dayendbalance/%s" // account_type 查询账户日终余额 GET
v3EcommerceBalance = "/v3/ecommerce/fund/balance/%s" // sub_mchid 查询特约商户账户实时余额 GET
v3EcommerceDayBalance = "/v3/ecommerce/fund/enddaybalance/%s" // sub_mchid 查询二级商户账户日终余额 GET
// 来账识别API
v3MerchantIncomeRecord = "/v3/merchantfund/merchant/income-records" // 商户银行来账查询 GET
v3EcommerceIncomeRecord = "/v3/merchantfund/partner/income-records" // 特约商户银行来账查询 GET
// 服务商-特约商户进件
v3Apply4SubSubmit = "/v3/applyment4sub/applyment/" // 提交申请单 POST
v3Apply4SubQueryByBusinessCode = "/v3/applyment4sub/applyment/business_code/%s" // business_code 通过业务申请编号查询申请状态 GET
v3Apply4SubQueryByApplyId = "/v3/applyment4sub/applyment/applyment_id/%s" // applyment_id 通过申请单号查询申请状态 GET
v3Apply4SubModifySettlement = "/v3/apply4sub/sub_merchants/%s/modify-settlement" // sub_mchid 修改结算账号 POST
v3Apply4SubQuerySettlement = "/v3/apply4sub/sub_merchants/%s/settlement" // sub_mchid 查询结算账户 GET
// 电商收付通(商户进件)
v3EcommerceApply = "/v3/ecommerce/applyments/" // 二级商户进件 POST
v3EcommerceApplyQueryById = "/v3/ecommerce/applyments/%d" // applyment_id 通过申请单ID查询申请状态 GET
v3EcommerceApplyQueryByNo = "/v3/ecommerce/applyments/out-request-no/%s" // out_request_no 通过业务申请编号查询申请状态 GET
// 电商收付通(分账)
v3EcommerceProfitShare = "/v3/ecommerce/profitsharing/orders" // 请求分账 POST
v3EcommerceProfitShareQuery = "/v3/ecommerce/profitsharing/orders" // 查询分账结果 GET
v3EcommerceProfitShareReturn = "/v3/ecommerce/profitsharing/returnorders" // 请求分账回退 POST
v3EcommerceProfitShareReturnResult = "/v3/ecommerce/profitsharing/returnorders" // 查询分账回退结果 GET
v3EcommerceProfitShareFinish = "/v3/ecommerce/profitsharing/finish-order" // 完结分账 POST
v3EcommerceProfitShareUnsplitAmount = "/v3/ecommerce/profitsharing/orders/%s/amounts" // transaction_id 查询订单剩余待分金额 GET
v3EcommerceProfitShareAddReceiver = "/v3/ecommerce/profitsharing/receivers/add" // 添加分账接收方 POST
v3EcommerceProfitShareDeleteReceiver = "/v3/ecommerce/profitsharing/receivers/delete" // 删除分账接收方 POST
// 电商收付通(补差)
v3EcommerceSubsidies = "/v3/ecommerce/subsidies/create" // 请求补差 POST
v3EcommerceSubsidiesReturn = "/v3/ecommerce/subsidies/return" // 请求补差回退 POST
v3EcommerceSubsidiesCancel = "/v3/ecommerce/subsidies/cancel" // 取消补差 POST
// 电商收付通(退款)
v3CommerceRefund = "/v3/ecommerce/refunds/apply" // 申请退款 POST
v3CommerceRefundQueryById = "/v3/ecommerce/refunds/id/%s" // refund_id 通过微信支付退款单号查询退款 GET
v3CommerceRefundQueryByNo = "/v3/ecommerce/refunds/out-refund-no/%s" // out_refund_no 通过商户退款单号查询退款 GET
v3CommerceRefundAdvance = "/v3/ecommerce/refunds/%s/return-advance" // refund_id 垫付退款回补 POST
v3CommerceRefundAdvanceResult = "/v3/ecommerce/refunds/%s/return-advance" // refund_id 查询垫付回补结果 GET
// 银行组件(服务商)
v3BankSearchBank = "/v3/capital/capitallhh/banks/search-banks-by-bank-account" // 获取对私银行卡号开户银行 GET
v3BankSearchPersonalList = "/v3/capital/capitallhh/banks/personal-banking" // 查询支持个人业务的银行列表 GET
v3BankSearchCorporateList = "/v3/capital/capitallhh/banks/corporate-banking" // 查询支持对公业务的银行列表 GET
v3BankSearchProvinceList = "/v3/capital/capitallhh/areas/provinces" // 查询省份列表 GET
v3BankSearchCityList = "/v3/capital/capitallhh/areas/provinces/%d/cities" // province_code 查询城市列表 GET
v3BankSearchBranchList = "/v3/capital/capitallhh/banks/%s/branches" // bank_alias_code 查询支行列表 GET
// 特约商户进件申请单状态
ApplyStateEditing = "APPLYMENT_STATE_EDITTING" // 编辑中
ApplyStateAuditing = "APPLYMENT_STATE_AUDITING" // 审核中
ApplyStateRejected = "APPLYMENT_STATE_REJECTED" // 已驳回
ApplyStateToBeConfirmed = "APPLYMENT_STATE_TO_BE_CONFIRMED" // 待账户验证
ApplyStateSigning = "APPLYMENT_STATE_SIGNING" // 开通权限中
ApplyStateFinished = "APPLYMENT_STATE_FINISHED" // 已完成
ApplyStateCanceled = "APPLYMENT_STATE_CANCELED" // 已作废
// 特约商户结算账号类型
ApplySettlementAccountTypeBusiness = "ACCOUNT_TYPE_BUSINESS" // 对公银行账户
ApplySettlementAccountTypePrivate = "ACCOUNT_TYPE_PRIVATE" // 经营者个人银行卡
// 特约商户结算账号汇款验证结果
ApplySettlementVerifying = "VERIFYING" // 系统汇款验证中,商户可发起提现尝试
ApplySettlementVerifySuccess = "VERIFY_SUCCESS" // 系统成功汇款,该账户可正常发起提现
ApplySettlementVerifyFail = "VERIFY_FAIL" // 系统汇款失败,该账户无法发起提现,请检查修改
// 订单号类型,1-微信订单号,2-商户订单号,3-微信侧回跳到商户前端时用于查单的单据查询id(查询支付分订单中会使用)
TransactionId OrderNoType = 1
OutTradeNo OrderNoType = 2
QueryId OrderNoType = 3
// v3 异步通知订单状态
TradeStateSuccess = "SUCCESS" // 支付成功
TradeStateRefund = "REFUND" // 转入退款
TradeStateNoPay = "NOTPAY" // 未支付
TradeStateClosed = "CLOSED" // 已关闭
TradeStateRevoked = "REVOKED" // 已撤销(付款码支付)
TradeStatePaying = "USERPAYING" // 用户支付中(付款码支付)
TradeStatePayError = "PAYERROR" // 支付失败(其他原因,如银行返回失败)
)
+87
View File
@@ -0,0 +1,87 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 预受理领卡请求API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_3_1.shtml
func (c *ClientV3) V3DiscountCardApply(ctx context.Context, bm gopay.BodyMap) (wxRsp *DiscountCardApplyRsp, err error) {
authorization, err := c.authorization(MethodPost, v3CardPre, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CardPre, authorization)
if err != nil {
return nil, err
}
wxRsp = &DiscountCardApplyRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(DiscountCardApply)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 增加用户记录API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_3_2.shtml
func (c *ClientV3) V3DiscountCardAddUser(ctx context.Context, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
if err = bm.CheckEmptyError("out_card_code"); err != nil {
return nil, err
}
uri := fmt.Sprintf(v3CardAddUser, bm.GetString("out_card_code"))
bm.Remove("out_card_code")
authorization, err := c.authorization(MethodPost, uri, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询先享卡订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_3_3.shtml
func (c *ClientV3) V3DiscountCardQuery(ctx context.Context, outCardCode string) (wxRsp *DiscountCardQueryRsp, err error) {
url := fmt.Sprintf(v3CardQuery, outCardCode)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &DiscountCardQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(DiscountCardQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+360
View File
@@ -0,0 +1,360 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 二级商户进件API
// 注意:本接口会提交一些敏感信息,需调用 client.V3EncryptText() 进行加密。部分图片参数,请先调用 client.V3MediaUploadImage() 上传,获取MediaId
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_1_1.shtml
func (c *ClientV3) V3EcommerceApply(ctx context.Context, bm gopay.BodyMap) (*EcommerceApplyRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceApply, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceApply, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceApplyRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceApply)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询申请状态API
// 注意:applyId 和 outRequestNo 二选一
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_1_2.shtml
func (c *ClientV3) V3EcommerceApplyStatus(ctx context.Context, applyId int64, outRequestNo string) (*EcommerceApplyStatusRsp, error) {
if applyId == 0 && outRequestNo == gopay.NULL {
return nil, fmt.Errorf("applyId[%d] and outRequestNo[%s] empty at the same time", applyId, outRequestNo)
}
var url string
if applyId != 0 {
url = fmt.Sprintf(v3EcommerceApplyQueryById, applyId)
} else {
url = fmt.Sprintf(v3EcommerceApplyQueryByNo, outRequestNo)
}
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceApplyStatusRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceApplyStatus)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 请求分账API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_1.shtml
func (c *ClientV3) V3EcommerceProfitShare(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceProfitShare, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceProfitShare, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShare)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询分账结果API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_2.shtml
func (c *ClientV3) V3EcommerceProfitShareQuery(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareQueryRsp, error) {
uri := v3EcommerceProfitShareQuery + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 请求分账回退API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_3.shtml
func (c *ClientV3) V3EcommerceProfitShareReturn(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareReturnRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceProfitShareReturn, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceProfitShareReturn, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareReturnRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareReturn)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询分账回退结果API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_4.shtml
func (c *ClientV3) V3EcommerceProfitShareReturnResult(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareReturnResultRsp, error) {
uri := v3EcommerceProfitShareReturnResult + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareReturnResultRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareReturn)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 完结分账API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_5.shtml
func (c *ClientV3) V3EcommerceProfitShareFinish(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareFinishRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceProfitShareFinish, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceProfitShareFinish, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareFinishRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareFinish)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询订单剩余待分金额API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_9.shtml
func (c *ClientV3) V3EcommerceProfitShareUnsplitAmount(ctx context.Context, transactionId string) (*EcommerceProfitShareUnsplitAmountRsp, error) {
url := fmt.Sprintf(v3EcommerceProfitShareUnsplitAmount, transactionId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareUnsplitAmountRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareUnsplitAmount)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 添加分账接收方API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_7.shtml
func (c *ClientV3) V3EcommerceProfitShareAddReceiver(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareAddReceiverRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceProfitShareAddReceiver, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceProfitShareAddReceiver, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareAddReceiverRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareReceiver)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 删除分账接收方API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_4_8.shtml
func (c *ClientV3) V3EcommerceProfitShareDeleteReceiver(ctx context.Context, bm gopay.BodyMap) (*EcommerceProfitShareDeleteReceiverRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceProfitShareDeleteReceiver, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceProfitShareDeleteReceiver, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceProfitShareDeleteReceiverRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceProfitShareReceiver)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 请求补差API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_1.shtml
func (c *ClientV3) V3EcommerceSubsidies(ctx context.Context, bm gopay.BodyMap) (*EcommerceSubsidiesRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceSubsidies, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceSubsidies, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceSubsidiesRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceSubsidies)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 请求补差回退API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_2.shtml
func (c *ClientV3) V3EcommerceSubsidiesReturn(ctx context.Context, bm gopay.BodyMap) (*EcommerceSubsidiesReturnRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceSubsidiesReturn, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceSubsidiesReturn, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceSubsidiesReturnRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceSubsidiesReturn)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 取消补差API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_5_3.shtml
func (c *ClientV3) V3EcommerceSubsidiesCancel(ctx context.Context, bm gopay.BodyMap) (*EcommerceSubsidiesCancelRsp, error) {
authorization, err := c.authorization(MethodPost, v3EcommerceSubsidiesCancel, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceSubsidiesCancel, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceSubsidiesCancelRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceSubsidiesCancel)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+179
View File
@@ -0,0 +1,179 @@
package wechat
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/aes"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xpem"
)
// 敏感信息加密
func (c *ClientV3) V3EncryptText(text string) (cipherText string, err error) {
if c.wxPublicKey == nil || c.WxSerialNo == "" {
return util.NULL, errors.New("WxPublicKey or WxSerialNo is null")
}
cipherByte, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, c.wxPublicKey, []byte(text), nil)
if err != nil {
return "", fmt.Errorf("rsa.EncryptOAEP%w", err)
}
return base64.StdEncoding.EncodeToString(cipherByte), nil
}
// 敏感信息解密
func (c *ClientV3) V3DecryptText(cipherText string) (text string, err error) {
cipherByte, _ := base64.StdEncoding.DecodeString(cipherText)
textByte, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, c.privateKey, cipherByte, nil)
if err != nil {
return "", fmt.Errorf("rsa.DecryptOAEP%w", err)
}
return string(textByte), nil
}
// 敏感参数信息加密
// wxPublicKeyContent:微信平台证书内容
func V3EncryptText(text string, wxPublicKeyContent []byte) (cipherText string, err error) {
publicKey, err := xpem.DecodePublicKey(wxPublicKeyContent)
if err != nil {
return gopay.NULL, err
}
cipherByte, err := rsa.EncryptOAEP(sha1.New(), rand.Reader, publicKey, []byte(text), nil)
if err != nil {
return "", fmt.Errorf("rsa.EncryptOAEP%w", err)
}
return base64.StdEncoding.EncodeToString(cipherByte), nil
}
// 敏感参数信息解密
// privateKeyContent:私钥 apiclient_key.pem 读取后的字符串内容
func V3DecryptText(cipherText string, privateKeyContent []byte) (text string, err error) {
privateKey, err := xpem.DecodePrivateKey(privateKeyContent)
if err != nil {
return gopay.NULL, err
}
cipherByte, _ := base64.StdEncoding.DecodeString(cipherText)
textByte, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, privateKey, cipherByte, nil)
if err != nil {
return "", fmt.Errorf("rsa.DecryptOAEP%w", err)
}
return string(textByte), nil
}
// 解密 普通支付 回调中的加密信息
func V3DecryptNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密 服务商支付 回调中的加密信息
func V3DecryptPartnerNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptPartnerResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptPartnerResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密 普通退款 回调中的加密信息
func V3DecryptRefundNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptRefundResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptRefundResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密 服务商退款 回调中的加密信息
func V3DecryptPartnerRefundNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptPartnerRefundResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptPartnerRefundResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密 合单支付 回调中的加密信息
func V3DecryptCombineNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptCombineResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptCombineResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密分账动账回调中的加密信息
func V3DecryptProfitShareNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptProfitShareResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptProfitShareResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密 支付分 回调中的加密信息
func V3DecryptScoreNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptScoreResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptScoreResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
// 解密商家券回调中的加密信息
func V3DecryptBusifavorNotifyCipherText(ciphertext, nonce, additional, apiV3Key string) (result *V3DecryptBusifavorResult, err error) {
cipherBytes, _ := base64.StdEncoding.DecodeString(ciphertext)
decrypt, err := aes.GCMDecrypt(cipherBytes, []byte(nonce), []byte(additional), []byte(apiV3Key))
if err != nil {
return nil, fmt.Errorf("aes.GCMDecrypt, err:%w", err)
}
result = &V3DecryptBusifavorResult{}
if err = json.Unmarshal(decrypt, result); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s), err:%w", string(decrypt), err)
}
return result, nil
}
+123
View File
@@ -0,0 +1,123 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 点金计划管理API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_5_1.shtml
func (c *ClientV3) V3GoldPlanManage(ctx context.Context, bm gopay.BodyMap) (wxRsp *GoldPlanManageRsp, err error) {
authorization, err := c.authorization(MethodPost, v3GoldPlanManage, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3GoldPlanManage, authorization)
if err != nil {
return nil, err
}
wxRsp = &GoldPlanManageRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(GoldPlanManage)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商家小票管理API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_5_2.shtml
func (c *ClientV3) V3GoldPlanBillManage(ctx context.Context, bm gopay.BodyMap) (wxRsp *GoldPlanManageRsp, err error) {
authorization, err := c.authorization(MethodPost, v3GoldPlanBillManage, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3GoldPlanBillManage, authorization)
if err != nil {
return nil, err
}
wxRsp = &GoldPlanManageRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(GoldPlanManage)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 同业过滤标签管理API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_5_3.shtml
func (c *ClientV3) V3GoldPlanFilterManage(ctx context.Context, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
authorization, err := c.authorization(MethodPost, v3GoldPlanFilterManage, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3GoldPlanFilterManage, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 开通广告展示API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_5_4.shtml
func (c *ClientV3) V3GoldPlanOpenAdShow(ctx context.Context, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
authorization, err := c.authorization(MethodPATCH, v3GoldPlanOpenAdShow, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPatch(ctx, bm, v3GoldPlanOpenAdShow, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 关闭广告展示API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_5_5.shtml
func (c *ClientV3) V3GoldPlanCloseAdShow(ctx context.Context, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
authorization, err := c.authorization(MethodPATCH, v3GoldPlanCloseAdShow, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3GoldPlanCloseAdShow, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+3
View File
@@ -0,0 +1,3 @@
package wechat
// 支付有礼
+456
View File
@@ -0,0 +1,456 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 创建商家券
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_1.shtml
func (c *ClientV3) V3BusiFavorBatchCreate(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorCreateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorBatchCreate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorBatchCreate, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorCreateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchCreate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询商家券详情
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_2.shtml
func (c *ClientV3) V3BusiFavorBatchDetail(ctx context.Context, stockId string) (wxRsp *BusiFavorBatchDetailRsp, err error) {
uri := fmt.Sprintf(v3BusiFavorBatchDetail, stockId)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorBatchDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorBatchDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 核销用户券
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_3.shtml
func (c *ClientV3) V3BusiFavorUse(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorUseRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorUse, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorUse, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorUseRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorUse)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 根据过滤条件查询用户券
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_4.shtml
func (c *ClientV3) V3BusiFavorUserCoupons(ctx context.Context, openid string, bm gopay.BodyMap) (wxRsp *BusiFavorUserCouponsRsp, err error) {
uri := fmt.Sprintf(v3BusiFavorUserCoupons, openid) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorUserCouponsRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorUserCoupons)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询用户单张券详情
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_5.shtml
func (c *ClientV3) V3BusiFavorUserCouponDetail(ctx context.Context, openid, couponCode, appid string) (wxRsp *BusiFavorUserCouponDetailRsp, err error) {
uri := fmt.Sprintf(v3BusiFavorUserCouponDetail, openid, couponCode, appid)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorUserCouponDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiUserCoupon)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 上传预存code
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_6.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_6.shtml
func (c *ClientV3) V3BusiFavorCodeUpload(ctx context.Context, stockId string, bm gopay.BodyMap) (wxRsp *BusiFavorCodeUploadRsp, err error) {
url := fmt.Sprintf(v3BusiFavorCodeUpload, stockId)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorCodeUploadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorCodeUpload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 设置商家券事件通知地址
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_7.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_7.shtml
func (c *ClientV3) V3BusiFavorCallbackUrlSet(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorCallbackUrlSetRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorCallbackUrlSet, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorCallbackUrlSet, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorCallbackUrlSetRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorCallbackUrlSet)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询商家券事件通知地址
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_8.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_8.shtml
func (c *ClientV3) V3BusiFavorCallbackUrl(ctx context.Context, mchid string) (wxRsp *BusiFavorCallbackUrlRsp, err error) {
uri := v3BusiFavorCallbackUrl + "?mchid=" + mchid
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorCallbackUrlRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorCallbackUrl)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 关联订单信息
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_9.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_9.shtml
func (c *ClientV3) V3BusiFavorAssociate(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorAssociateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorAssociate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorAssociate, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorAssociateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorAssociate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 取消关联订单信息
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_10.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_10.shtml
func (c *ClientV3) V3BusiFavorDisassociate(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorDisassociateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorDisassociate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorDisassociate, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorDisassociateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorDisassociate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 修改批次预算
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_11.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_11.shtml
func (c *ClientV3) V3BusiFavorBatchUpdate(ctx context.Context, stockId string, bm gopay.BodyMap) (wxRsp *BusiFavorBatchUpdateRsp, err error) {
url := fmt.Sprintf(v3BusiFavorBatchUpdate, stockId)
authorization, err := c.authorization(MethodPATCH, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPatch(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorBatchUpdateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorBatchUpdate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 修改商家券基本信息
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_12.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_12.shtml
func (c *ClientV3) V3BusiFavorInfoUpdate(ctx context.Context, stockId string, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3BusiFavorInfoUpdate, stockId)
authorization, err := c.authorization(MethodPATCH, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPatch(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 发放消费卡
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_6_1.shtml
func (c *ClientV3) V3BusiFavorSend(ctx context.Context, cardId string, bm gopay.BodyMap) (wxRsp *BusiFavorSendRsp, err error) {
url := fmt.Sprintf(v3BusiFavorSend, cardId)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorSendRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorSend)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请退券
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_13.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_13.shtml
func (c *ClientV3) V3BusiFavorReturn(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorReturnRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorReturn, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorReturn, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorReturnRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorReturn)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 使券失效
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_14.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_14.shtml
func (c *ClientV3) V3BusiFavorDeactivate(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorDeactivateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorDeactivate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorDeactivate, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorDeactivateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorDeactivate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 营销补差付款
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_16.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_16.shtml
func (c *ClientV3) V3BusiFavorSubsidyPay(ctx context.Context, bm gopay.BodyMap) (wxRsp *BusiFavorSubsidyPayRsp, err error) {
authorization, err := c.authorization(MethodPost, v3BusiFavorSubsidyPay, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3BusiFavorSubsidyPay, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorSubsidyPayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorSubsidyPay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询营销补差付款单详情
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_18.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_2_18.shtml
func (c *ClientV3) V3BusiFavorSubsidyPayDetail(ctx context.Context, subsidyReceiptId string) (wxRsp *BusiFavorSubsidyPayDetailRsp, err error) {
url := fmt.Sprintf(v3BusiFavorSubsidyPayDetail, subsidyReceiptId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &BusiFavorSubsidyPayDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(BusiFavorSubsidyPay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+399
View File
@@ -0,0 +1,399 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 创建代金券批次
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_1.shtml
func (c *ClientV3) V3FavorBatchCreate(ctx context.Context, bm gopay.BodyMap) (wxRsp *FavorBatchCreateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3FavorBatchCreate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3FavorBatchCreate, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchCreateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchCreate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 发放代金券批次
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_2.shtml
func (c *ClientV3) V3FavorBatchGrant(ctx context.Context, openid string, bm gopay.BodyMap) (wxRsp *FavorBatchGrantRsp, err error) {
url := fmt.Sprintf(v3FavorBatchGrant, openid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchGrantRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchGrant)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 激活代金券批次
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_3.shtml
func (c *ClientV3) V3FavorBatchStart(ctx context.Context, stockId, stockCreatorMchid string) (wxRsp *FavorBatchStartRsp, err error) {
url := fmt.Sprintf(v3FavorBatchStart, stockId)
bm := make(gopay.BodyMap)
bm.Set("stock_creator_mchid", stockCreatorMchid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchStartRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchStart)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 条件查询批次列表
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_4.shtml
func (c *ClientV3) V3FavorBatchList(ctx context.Context, bm gopay.BodyMap) (wxRsp *FavorBatchListRsp, err error) {
uri := v3FavorBatchList + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchList)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询批次详情
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_5.shtml
func (c *ClientV3) V3FavorBatchDetail(ctx context.Context, stockId, stockCreatorMchid string) (wxRsp *FavorBatchDetailRsp, err error) {
uri := fmt.Sprintf(v3FavorBatchDetail, stockId) + "?stock_creator_mchid=" + stockCreatorMchid
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatch)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询代金券详情
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_6.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_6.shtml
func (c *ClientV3) V3FavorDetail(ctx context.Context, appid, couponId, openid string) (wxRsp *FavorDetailRsp, err error) {
uri := fmt.Sprintf(v3FavorDetail, openid, couponId) + "?appid=" + appid
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询代金券可用商户
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_7.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_7.shtml
func (c *ClientV3) V3FavorMerchant(ctx context.Context, stockId, stockCreatorMchid string, limit, offset int) (wxRsp *FavorMerchantRsp, err error) {
if limit == 0 {
limit = 20
}
uri := fmt.Sprintf(v3FavorMerchant, stockId) + "?stock_creator_mchid=" + stockCreatorMchid + "&limit=" + util.Int2String(limit) + "&offset=" + util.Int2String(offset)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorMerchantRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorMerchant)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询代金券可用单品
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_8.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_8.shtml
func (c *ClientV3) V3FavorItems(ctx context.Context, stockId, stockCreatorMchid string, limit, offset int) (wxRsp *FavorItemsRsp, err error) {
if limit == 0 {
limit = 20
}
uri := fmt.Sprintf(v3FavorItems, stockId) + "?stock_creator_mchid=" + stockCreatorMchid + "&limit=" + util.Int2String(limit) + "&offset=" + util.Int2String(offset)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorItemsRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorItems)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 根据商户号查用户的券
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_9.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_9.shtml
func (c *ClientV3) V3FavorUserCoupons(ctx context.Context, openid string, bm gopay.BodyMap) (wxRsp *FavorUserCouponsRsp, err error) {
uri := fmt.Sprintf(v3FavorUserCoupons, openid) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorUserCouponsRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorUserCoupons)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 下载批次核销明细
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_10.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_10.shtml
func (c *ClientV3) V3FavorUseFlowDownload(ctx context.Context, stockId string) (wxRsp *FavorUseFlowDownloadRsp, err error) {
url := fmt.Sprintf(v3FavorUseFlowDownload, stockId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorUseFlowDownloadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorFlowDownload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 下载批次退款明细
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_11.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_11.shtml
func (c *ClientV3) V3FavorRefundFlowDownload(ctx context.Context, stockId string) (wxRsp *FavorRefundFlowDownloadRsp, err error) {
url := fmt.Sprintf(v3FavorRefundFlowDownload, stockId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorRefundFlowDownloadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorFlowDownload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 设置消息通知地址
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_12.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_12.shtml
func (c *ClientV3) V3FavorCallbackUrlSet(ctx context.Context, bm gopay.BodyMap) (wxRsp *FavorCallbackUrlSetRsp, err error) {
authorization, err := c.authorization(MethodPost, v3FavorCallbackUrlSet, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3FavorCallbackUrlSet, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorCallbackUrlSetRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorCallbackUrl)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 暂停代金券批次
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_13.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_13.shtml
func (c *ClientV3) V3FavorBatchPause(ctx context.Context, stockId, stockCreatorMchid string) (wxRsp *FavorBatchPauseRsp, err error) {
url := fmt.Sprintf(v3FavorBatchPause, stockId)
bm := make(gopay.BodyMap)
bm.Set("stock_creator_mchid", stockCreatorMchid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchPauseRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchPause)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 重启代金券批次
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_1_14.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_1_14.shtml
func (c *ClientV3) V3FavorBatchRestart(ctx context.Context, stockId, stockCreatorMchid string) (wxRsp *FavorBatchRestartRsp, err error) {
url := fmt.Sprintf(v3FavorBatchRestart, stockId)
bm := make(gopay.BodyMap)
bm.Set("stock_creator_mchid", stockCreatorMchid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &FavorBatchRestartRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(FavorBatchRestart)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+45
View File
@@ -0,0 +1,45 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 图片上传(营销专用)
// 注意:图片不能超过2MB
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_0_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_0_1.shtml
func (c *ClientV3) V3FavorMediaUploadImage(ctx context.Context, fileName, fileSha256 string, img *util.File) (wxRsp *MarketMediaUploadRsp, err error) {
bmFile := make(gopay.BodyMap)
bmFile.Set("filename", fileName).Set("sha256", fileSha256)
authorization, err := c.authorization(MethodPost, v3FavorMediaUploadImage, bmFile)
if err != nil {
return nil, err
}
bm := make(gopay.BodyMap)
bm.SetBodyMap("meta", func(bm gopay.BodyMap) {
bm.Set("filename", fileName).Set("sha256", fileSha256)
}).SetFormFile("file", img)
res, si, bs, err := c.doProdPostFile(ctx, bm, v3FavorMediaUploadImage, authorization)
if err != nil {
return nil, err
}
wxRsp = &MarketMediaUploadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MarketMediaUpload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+89
View File
@@ -0,0 +1,89 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 建立合作关系
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_5_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_5_1.shtml
func (c *ClientV3) V3PartnershipsBuild(ctx context.Context, idempotencyKey string, bm gopay.BodyMap) (wxRsp *PartnershipsBuildRsp, err error) {
authorization, err := c.authorization(MethodPost, v3PartnershipsBuild, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPostWithHeader(ctx, map[string]string{"Idempotency-Key": idempotencyKey}, bm, v3PartnershipsBuild, authorization)
if err != nil {
return nil, err
}
wxRsp = &PartnershipsBuildRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnershipsBuild)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 终止合作关系
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_5_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_5_2.shtml
func (c *ClientV3) V3PartnershipsTerminate(ctx context.Context, idempotencyKey string, bm gopay.BodyMap) (wxRsp *PartnershipsTerminateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3PartnershipsTerminate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPostWithHeader(ctx, map[string]string{"Idempotency-Key": idempotencyKey}, bm, v3PartnershipsTerminate, authorization)
if err != nil {
return nil, err
}
wxRsp = &PartnershipsTerminateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnershipsTerminate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询合作关系列表
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_5_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter9_5_3.shtml
func (c *ClientV3) V3PartnershipsList(ctx context.Context, bm gopay.BodyMap) (wxRsp *PartnershipsListRsp, err error) {
uri := v3PartnershipsList + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &PartnershipsListRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnershipsList)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+79
View File
@@ -0,0 +1,79 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 图片上传API
// 注意:图片不能超过2MB
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter2_1_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter2_1_1.shtml
func (c *ClientV3) V3MediaUploadImage(ctx context.Context, fileName, fileSha256 string, img *util.File) (wxRsp *MediaUploadRsp, err error) {
bmFile := make(gopay.BodyMap)
bmFile.Set("filename", fileName).Set("sha256", fileSha256)
authorization, err := c.authorization(MethodPost, v3MediaUploadImage, bmFile)
if err != nil {
return nil, err
}
bm := make(gopay.BodyMap)
bm.SetBodyMap("meta", func(bm gopay.BodyMap) {
bm.Set("filename", fileName).Set("sha256", fileSha256)
}).SetFormFile("file", img)
res, si, bs, err := c.doProdPostFile(ctx, bm, v3MediaUploadImage, authorization)
if err != nil {
return nil, err
}
wxRsp = &MediaUploadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MediaUpload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 视频上传API
// 注意:媒体视频只支持avi、wmv、mpeg、mp4、mov、mkv、flv、f4v、m4v、rmvb格式,文件大小不能超过5M。
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter2_1_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter2_1_2.shtml
func (c *ClientV3) V3MediaUploadVideo(ctx context.Context, fileName, fileSha256 string, img *util.File) (wxRsp *MediaUploadRsp, err error) {
bmFile := make(gopay.BodyMap)
bmFile.Set("filename", fileName).Set("sha256", fileSha256)
authorization, err := c.authorization(MethodPost, v3MediaUploadVideo, bmFile)
if err != nil {
return nil, err
}
bm := make(gopay.BodyMap)
bm.SetBodyMap("meta", func(bm gopay.BodyMap) {
bm.Set("filename", fileName).Set("sha256", fileSha256)
}).SetFormFile("file", img)
res, si, bs, err := c.doProdPostFile(ctx, bm, v3MediaUploadVideo, authorization)
if err != nil {
return nil, err
}
wxRsp = &MediaUploadRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MediaUpload)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+181
View File
@@ -0,0 +1,181 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 查询特约商户账户实时余额、查询二级商户账户实时余额
// Code = 0 is success
// 注意:服务商时,bm参数传 nil
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter5_1.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_7_1.shtml
func (c *ClientV3) V3EcommerceBalance(ctx context.Context, subMchid string, bm gopay.BodyMap) (*EcommerceBalanceRsp, error) {
url := fmt.Sprintf(v3EcommerceBalance, subMchid) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceBalanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceBalance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询二级商户账户日终余额
// date示例值:2019-08-17
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_7_2.shtml
func (c *ClientV3) V3EcommerceDayBalance(ctx context.Context, subMchid, date string) (*EcommerceBalanceRsp, error) {
uri := fmt.Sprintf(v3EcommerceDayBalance, subMchid) + "?date=" + date
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceBalanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceBalance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询账户实时余额
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter5_2.shtml
// 电商平台:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_7_3.shtml
func (c *ClientV3) V3MerchantBalance(ctx context.Context, accountType string) (*MerchantBalanceRsp, error) {
url := fmt.Sprintf(v3MerchantBalance, accountType)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &MerchantBalanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MerchantBalance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询账户日终余额
// date示例值:2019-08-17
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter5_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter5_3.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_7_4.shtml
func (c *ClientV3) V3MerchantDayBalance(ctx context.Context, accountType, date string) (*MerchantBalanceRsp, error) {
uri := fmt.Sprintf(v3MerchantDayBalance, accountType) + "?date=" + date
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &MerchantBalanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MerchantBalance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 特约商户银行来账查询API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_6.shtml
func (c *ClientV3) V3EcommerceIncomeRecord(ctx context.Context, bm gopay.BodyMap) (*PartnerIncomeRecordRsp, error) {
uri := v3EcommerceIncomeRecord + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &PartnerIncomeRecordRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerIncomeRecord)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商户/服务商银行来账查询API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_7.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_7.shtml
func (c *ClientV3) V3MerchantIncomeRecord(ctx context.Context, bm gopay.BodyMap) (*MerchantIncomeRecordRsp, error) {
uri := v3MerchantIncomeRecord + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &MerchantIncomeRecordRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(MerchantIncomeRecord)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+2743
View File
File diff suppressed because it is too large Load Diff
+328
View File
@@ -0,0 +1,328 @@
package wechat
import (
"crypto/rsa"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/xlog"
)
type Resource struct {
OriginalType string `json:"original_type"`
Algorithm string `json:"algorithm"`
Ciphertext string `json:"ciphertext"`
AssociatedData string `json:"associated_data"`
Nonce string `json:"nonce"`
}
type V3DecryptResult struct {
Appid string `json:"appid"`
Mchid string `json:"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 *Payer `json:"payer"`
Amount *Amount `json:"amount"`
SceneInfo *SceneInfo `json:"scene_info"`
PromotionDetail []*PromotionDetail `json:"promotion_detail"`
}
type V3DecryptPartnerResult struct {
SpAppid string `json:"sp_appid"`
SpMchid string `json:"sp_mchid"`
SubAppid string `json:"sub_appid"`
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 *PartnerPayer `json:"payer"`
Amount *Amount `json:"amount"`
SceneInfo *SceneInfo `json:"scene_info"`
PromotionDetail []*PromotionDetail `json:"promotion_detail"`
}
type V3DecryptRefundResult struct {
Mchid string `json:"mchid"`
OutTradeNo string `json:"out_trade_no"`
TransactionId string `json:"transaction_id"`
OutRefundNo string `json:"out_refund_no"`
RefundId string `json:"refund_id"`
RefundStatus string `json:"refund_status"`
SuccessTime string `json:"success_time"`
UserReceivedAccount string `json:"user_received_account"`
Amount *RefundAmount `json:"amount"`
}
type V3DecryptPartnerRefundResult struct {
SpMchid string `json:"sp_mchid"`
SubMchid string `json:"sub_mchid"`
OutTradeNo string `json:"out_trade_no"`
TransactionId string `json:"transaction_id"`
OutRefundNo string `json:"out_refund_no"`
RefundId string `json:"refund_id"`
RefundStatus string `json:"refund_status"`
SuccessTime string `json:"success_time"`
UserReceivedAccount string `json:"user_received_account"`
Amount *RefundAmount `json:"amount"`
}
type V3DecryptCombineResult struct {
CombineAppid string `json:"combine_appid"`
CombineMchid string `json:"combine_mchid"`
CombineOutTradeNo string `json:"combine_out_trade_no"`
SceneInfo *SceneInfo `json:"scene_info"`
SubOrders []*SubOrders `json:"sub_orders"` // 最多支持子单条数:50
CombinePayerInfo *Payer `json:"combine_payer_info"` // 支付者信息
}
type V3DecryptScoreResult struct {
Appid string `json:"appid"`
Mchid string `json:"mchid"`
OutOrderNo string `json:"out_order_no"`
ServiceId string `json:"service_id"`
Openid string `json:"openid"`
State string `json:"state"`
StateDescription string `json:"state_description"`
TotalAmount int `json:"total_amount"`
ServiceIntroduction string `json:"service_introduction"`
PostPayments []*PostPayments `json:"post_payments"`
PostDiscounts []*PostDiscounts `json:"post_discounts"`
RiskFund *RiskFund `json:"risk_fund"`
TimeRange *TimeRange `json:"time_range"`
Location *Location `json:"location"`
Attach string `json:"attach"`
NotifyUrl string `json:"notify_url"`
OrderId string `json:"order_id"`
NeedCollection bool `json:"need_collection"`
Collection *Collection `json:"collection"`
}
type V3DecryptProfitShareResult struct {
SpMchid string `json:"sp_mchid"` // 服务商商户号
SubMchid string `json:"sub_mchid"` // 子商户号
TransactionId string `json:"transaction_id"` // 微信订单号
OrderId string `json:"order_id"` // 微信分账/回退单号
OutOrderNo string `json:"out_order_no"` // 商户分账/回退单号
Receiver *Receiver `json:"receiver"`
SuccessTime string `json:"success_time"` // 成功时间
}
type Receiver struct {
Type string `json:"type"` // 分账接收方类型
Account string `json:"account"` // 分账接收方账号
Amount int `json:"amount"` // 分账动账金额
Description string `json:"description"` // 分账/回退描述
}
type V3DecryptBusifavorResult struct {
EventType string `json:"event_type"` // 事件类型
CouponCode string `json:"coupon_code"` // 券code
StockId string `json:"stock_id"` // 批次号
SendTime string `json:"send_time"` // 发放时间
Openid string `json:"openid"` // 用户标识
Unionid string `json:"unionid"` // 用户统一标识
SendChannel string `json:"send_channel"` // 发放渠道
SendMerchant string `json:"send_merchant"` // 发券商户号
AttachInfo *BusifavorAttachInfo `json:"attach_info"` // 发券附加信息
}
type BusifavorAttachInfo struct {
TransactionId string `json:"transaction_id"` // 交易订单编号
ActCode string `json:"act_code"` // 支付有礼活动编号/营销馆活动ID
HallCode string `json:"hall_code"` // 营销馆ID
HallBelongMchID int `json:"hall_belong_mch_id"` // 营销馆所属商户号
CardID string `json:"card_id"` // 会员卡ID
Code string `json:"code"` // 会员卡code
ActivityID string `json:"activity_id"` // 会员活动ID
}
type V3NotifyReq struct {
Id string `json:"id"`
CreateTime string `json:"create_time"`
ResourceType string `json:"resource_type"`
EventType string `json:"event_type"`
Summary string `json:"summary"`
Resource *Resource `json:"resource"`
SignInfo *SignInfo `json:"-"`
}
type V3NotifyRsp struct {
Code string `json:"code"`
Message string `json:"message"`
}
// 解析微信回调请求的参数到 V3NotifyReq 结构体
func V3ParseNotify(req *http.Request) (notifyReq *V3NotifyReq, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
if err != nil {
return nil, fmt.Errorf("read request body error:%w", err)
}
si := &SignInfo{
HeaderTimestamp: req.Header.Get(HeaderTimestamp),
HeaderNonce: req.Header.Get(HeaderNonce),
HeaderSignature: req.Header.Get(HeaderSignature),
HeaderSerial: req.Header.Get(HeaderSerial),
SignBody: string(bs),
}
notifyReq = &V3NotifyReq{SignInfo: si}
if err = json.Unmarshal(bs, notifyReq); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s, %+v)%w", string(bs), notifyReq, err)
}
return notifyReq, nil
}
// Deprecated
// 推荐使用 VerifySignByPK()
func (v *V3NotifyReq) VerifySign(wxPkContent string) (err error) {
if v.SignInfo != nil {
return V3VerifySign(v.SignInfo.HeaderTimestamp, v.SignInfo.HeaderNonce, v.SignInfo.SignBody, v.SignInfo.HeaderSignature, wxPkContent)
}
return errors.New("verify notify sign, bug SignInfo is nil")
}
// 异步通知验签
// wxPublicKey:微信平台证书公钥内容,通过 client.WxPublicKey() 获取
func (v *V3NotifyReq) VerifySignByPK(wxPublicKey *rsa.PublicKey) (err error) {
if v.SignInfo != nil {
return V3VerifySignByPK(v.SignInfo.HeaderTimestamp, v.SignInfo.HeaderNonce, v.SignInfo.SignBody, v.SignInfo.HeaderSignature, wxPublicKey)
}
return errors.New("verify notify sign, bug SignInfo is nil")
}
// 解密 普通支付 回调中的加密信息
func (v *V3NotifyReq) DecryptCipherText(apiV3Key string) (result *V3DecryptResult, err error) {
if v.Resource != nil {
result, err = V3DecryptNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密 服务商支付 回调中的加密信息
func (v *V3NotifyReq) DecryptPartnerCipherText(apiV3Key string) (result *V3DecryptPartnerResult, err error) {
if v.Resource != nil {
result, err = V3DecryptPartnerNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密 普通退款 回调中的加密信息
func (v *V3NotifyReq) DecryptRefundCipherText(apiV3Key string) (result *V3DecryptRefundResult, err error) {
if v.Resource != nil {
result, err = V3DecryptRefundNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密 服务商退款 回调中的加密信息
func (v *V3NotifyReq) DecryptPartnerRefundCipherText(apiV3Key string) (result *V3DecryptPartnerRefundResult, err error) {
if v.Resource != nil {
result, err = V3DecryptPartnerRefundNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密 合单支付 回调中的加密信息
func (v *V3NotifyReq) DecryptCombineCipherText(apiV3Key string) (result *V3DecryptCombineResult, err error) {
if v.Resource != nil {
result, err = V3DecryptCombineNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密 支付分 回调中的加密信息
func (v *V3NotifyReq) DecryptScoreCipherText(apiV3Key string) (result *V3DecryptScoreResult, err error) {
if v.Resource != nil {
result, err = V3DecryptScoreNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密分账动账回调中的加密信息
func (v *V3NotifyReq) DecryptProfitShareCipherText(apiV3Key string) (result *V3DecryptProfitShareResult, err error) {
if v.Resource != nil {
result, err = V3DecryptProfitShareNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// 解密商家券回调中的加密信息
func (v *V3NotifyReq) DecryptBusifavorCipherText(apiV3Key string) (result *V3DecryptBusifavorResult, err error) {
if v.Resource != nil {
result, err = V3DecryptBusifavorNotifyCipherText(v.Resource.Ciphertext, v.Resource.Nonce, v.Resource.AssociatedData, apiV3Key)
if err != nil {
bytes, _ := json.Marshal(v)
return nil, fmt.Errorf("V3NotifyReq(%s) decrypt cipher text error(%w)", string(bytes), err)
}
return result, nil
}
return nil, errors.New("notify data Resource is nil")
}
// Deprecated
// 暂时不推荐此方法,请使用 wechat.V3ParseNotify()
// 解析微信回调请求的参数到 gopay.BodyMap
func V3ParseNotifyToBodyMap(req *http.Request) (bm gopay.BodyMap, err error) {
bs, err := ioutil.ReadAll(io.LimitReader(req.Body, int64(3<<20))) // default 3MB change the size you want;
defer req.Body.Close()
if err != nil {
xlog.Error("err:", err)
return
}
bm = make(gopay.BodyMap)
if err = json.Unmarshal(bs, &bm); err != nil {
return nil, fmt.Errorf("json.Unmarshal(%s)%w", string(bs), err)
}
return bm, nil
}
+185
View File
@@ -0,0 +1,185 @@
package wechat
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// APP下单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml
func (c *ClientV3) V3TransactionApp(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("mchid") == util.NULL {
bm.Set("mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiApp, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiApp, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// JSAPI/小程序下单API
// Code = 0 is success
// 商户JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
// 商户小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml
func (c *ClientV3) V3TransactionJsapi(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("mchid") == util.NULL {
bm.Set("mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiJsapi, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiJsapi, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// Native下单API
// Code = 0 is success
// 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml
func (c *ClientV3) V3TransactionNative(ctx context.Context, bm gopay.BodyMap) (wxRsp *NativeRsp, err error) {
if bm.GetString("mchid") == util.NULL {
bm.Set("mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiNative, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiNative, authorization)
if err != nil {
return nil, err
}
wxRsp = &NativeRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Native)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// H5下单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml
func (c *ClientV3) V3TransactionH5(ctx context.Context, bm gopay.BodyMap) (wxRsp *H5Rsp, err error) {
if bm.GetString("mchid") == util.NULL {
bm.Set("mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiH5, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiH5, authorization)
if err != nil {
return nil, err
}
wxRsp = &H5Rsp{Code: Success, SignInfo: si}
wxRsp.Response = new(H5Url)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_2.shtml
func (c *ClientV3) V3TransactionQueryOrder(ctx context.Context, orderNoType OrderNoType, orderNo string) (wxRsp *QueryOrderRsp, err error) {
var uri string
switch orderNoType {
case TransactionId:
uri = fmt.Sprintf(v3ApiQueryOrderTransactionId, orderNo) + "?mchid=" + c.Mchid
case OutTradeNo:
uri = fmt.Sprintf(v3ApiQueryOrderOutTradeNo, orderNo) + "?mchid=" + c.Mchid
default:
return nil, errors.New("unsupported order number type")
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &QueryOrderRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(QueryOrder)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 关闭订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_3.shtml
func (c *ClientV3) V3TransactionCloseOrder(ctx context.Context, tradeNo string) (wxRsp *CloseOrderRsp, err error) {
url := fmt.Sprintf(v3ApiCloseOrder, tradeNo)
bm := make(gopay.BodyMap)
bm.Set("mchid", c.Mchid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &CloseOrderRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+186
View File
@@ -0,0 +1,186 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 合单APP下单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_1.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_1.shtml
func (c *ClientV3) V3CombineTransactionApp(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("combine_mchid") == util.NULL {
bm.Set("combine_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3CombinePayApp, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CombinePayApp, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 合单JSAPI/小程序下单API
// Code = 0 is success
// 商户JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_3.shtml
// 商户小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_4.shtml
// 服务商JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_3.shtml
// 服务商小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_4.shtml
// 电商JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_3.shtml
// 电商小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_4.shtml
func (c *ClientV3) V3CombineTransactionJsapi(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("combine_mchid") == util.NULL {
bm.Set("combine_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3CombinePayJsapi, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CombinePayJsapi, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 合单Native下单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_5.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_5.shtml
func (c *ClientV3) V3CombineTransactionNative(ctx context.Context, bm gopay.BodyMap) (wxRsp *NativeRsp, err error) {
if bm.GetString("combine_mchid") == util.NULL {
bm.Set("combine_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3CombineNative, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CombineNative, authorization)
if err != nil {
return nil, err
}
wxRsp = &NativeRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Native)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 合单H5下单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_2.shtml
func (c *ClientV3) V3CombineTransactionH5(ctx context.Context, bm gopay.BodyMap) (wxRsp *H5Rsp, err error) {
if bm.GetString("combine_mchid") == util.NULL {
bm.Set("combine_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3CombinePayH5, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CombinePayH5, authorization)
if err != nil {
return nil, err
}
wxRsp = &H5Rsp{Code: Success, SignInfo: si}
wxRsp.Response = new(H5Url)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 合单查询订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_11.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_11.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_11.shtml
func (c *ClientV3) V3CombineQueryOrder(ctx context.Context, traderNo string) (wxRsp *CombineQueryOrderRsp, err error) {
uri := fmt.Sprintf(v3CombineQuery, traderNo)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &CombineQueryOrderRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(CombineQueryOrder)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 合单关闭订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter5_1_12.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter5_1_12.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_3_12.shtml
func (c *ClientV3) V3CombineCloseOrder(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *CloseOrderRsp, err error) {
url := fmt.Sprintf(v3CombineClose, tradeNo)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &CloseOrderRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+195
View File
@@ -0,0 +1,195 @@
package wechat
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// (服务商、电商模式)APP下单API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_2_1.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_1.shtml
func (c *ClientV3) V3PartnerTransactionApp(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("sp_mchid") == util.NULL {
bm.Set("sp_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiPartnerPayApp, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiPartnerPayApp, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// (服务商、电商模式)JSAPI/小程序下单API
// Code = 0 is success
// 服务商JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_1.shtml
// 服务商小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_5_1.shtml
// 电商JSAPI文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_2.shtml
// 电商小程序文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_3.shtml
func (c *ClientV3) V3PartnerTransactionJsapi(ctx context.Context, bm gopay.BodyMap) (wxRsp *PrepayRsp, err error) {
if bm.GetString("sp_mchid") == util.NULL {
bm.Set("sp_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiPartnerJsapi, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiPartnerJsapi, authorization)
if err != nil {
return nil, err
}
wxRsp = &PrepayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Prepay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// (服务商、电商模式)Native下单API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_4_1.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_12.shtml
func (c *ClientV3) V3PartnerTransactionNative(ctx context.Context, bm gopay.BodyMap) (wxRsp *NativeRsp, err error) {
if bm.GetString("sp_mchid") == util.NULL {
bm.Set("sp_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiPartnerNative, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiPartnerNative, authorization)
if err != nil {
return nil, err
}
wxRsp = &NativeRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Native)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// (服务商模式)H5下单API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_3_1.shtml
func (c *ClientV3) V3PartnerTransactionH5(ctx context.Context, bm gopay.BodyMap) (wxRsp *H5Rsp, err error) {
if bm.GetString("sp_mchid") == util.NULL {
bm.Set("sp_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, v3ApiPartnerH5, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ApiPartnerH5, authorization)
if err != nil {
return nil, err
}
wxRsp = &H5Rsp{Code: Success, SignInfo: si}
wxRsp.Response = new(H5Url)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// (服务商、电商模式)查询订单API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_2.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_5.shtml
func (c *ClientV3) V3PartnerQueryOrder(ctx context.Context, orderNoType OrderNoType, orderNo string, bm gopay.BodyMap) (wxRsp *PartnerQueryOrderRsp, err error) {
var uri string
if bm.GetString("sp_mchid") == gopay.NULL {
bm.Set("sp_mchid", c.Mchid)
}
switch orderNoType {
case TransactionId:
uri = fmt.Sprintf(v3ApiPartnerQueryOrderTransactionId, orderNo) + "?" + bm.EncodeURLParams()
case OutTradeNo:
uri = fmt.Sprintf(v3ApiPartnerQueryOrderOutTradeNo, orderNo) + "?" + bm.EncodeURLParams()
default:
return nil, errors.New("unsupported order number type")
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &PartnerQueryOrderRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerQueryOrder)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// (服务商、电商模式)关单API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_3.shtml
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_2_6.shtml
func (c *ClientV3) V3PartnerCloseOrder(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *CloseOrderRsp, err error) {
url := fmt.Sprintf(v3ApiPartnerCloseOrder, tradeNo)
if bm.GetString("sp_mchid") == gopay.NULL {
bm.Set("sp_mchid", c.Mchid)
}
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &CloseOrderRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+285
View File
@@ -0,0 +1,285 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 请求分账API
// 微信会在接到请求后立刻返回请求接收结果,分账结果需要自行调用查询接口来获取
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_1.shtml
func (c *ClientV3) V3ProfitShareOrder(ctx context.Context, bm gopay.BodyMap) (*ProfitShareOrderRsp, error) {
authorization, err := c.authorization(MethodPost, v3ProfitShareOrder, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ProfitShareOrder, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareOrderRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareOrder)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询分账结果API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_2.shtml
func (c *ClientV3) V3ProfitShareOrderQuery(ctx context.Context, orderNo string, bm gopay.BodyMap) (*ProfitShareOrderQueryRsp, error) {
uri := fmt.Sprintf(v3ProfitShareQuery, orderNo) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareOrderQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareOrderQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 请求分账回退API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_3.shtml
func (c *ClientV3) V3ProfitShareReturn(ctx context.Context, bm gopay.BodyMap) (*ProfitShareReturnRsp, error) {
authorization, err := c.authorization(MethodPost, v3ProfitShareReturn, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ProfitShareReturn, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareReturnRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareReturn)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询分账回退结果API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_4.shtml
func (c *ClientV3) V3ProfitShareReturnResult(ctx context.Context, returnNo string, bm gopay.BodyMap) (*ProfitShareReturnResultRsp, error) {
uri := fmt.Sprintf(v3ProfitShareReturnResult, returnNo) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareReturnResultRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareReturnResult)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 解冻剩余资金API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_5.shtml
func (c *ClientV3) V3ProfitShareOrderUnfreeze(ctx context.Context, bm gopay.BodyMap) (*ProfitShareOrderUnfreezeRsp, error) {
authorization, err := c.authorization(MethodPost, v3ProfitShareUnfreeze, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ProfitShareUnfreeze, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareOrderUnfreezeRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareOrderUnfreeze)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询剩余待分金额API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_6.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_6.shtml
func (c *ClientV3) V3ProfitShareUnsplitAmount(ctx context.Context, transId string) (*ProfitShareUnsplitAmountRsp, error) {
url := fmt.Sprintf(v3ProfitShareUnsplitAmount, transId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareUnsplitAmountRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareUnsplitAmount)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询最大分账比例API
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_7.shtml
func (c *ClientV3) V3ProfitShareMerchantConfigs(ctx context.Context, subMchId string) (*ProfitShareMerchantConfigsRsp, error) {
uri := fmt.Sprintf(v3ProfitShareMerchantConfigs, subMchId)
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareMerchantConfigsRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareMerchantConfigs)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 新增分账接收方API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_8.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_8.shtml
func (c *ClientV3) V3ProfitShareAddReceiver(ctx context.Context, bm gopay.BodyMap) (*ProfitShareAddReceiverRsp, error) {
authorization, err := c.authorization(MethodPost, v3ProfitShareAddReceiver, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ProfitShareAddReceiver, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareAddReceiverRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareAddReceiver)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 删除分账接收方API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_9.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_9.shtml
func (c *ClientV3) V3ProfitShareDeleteReceiver(ctx context.Context, bm gopay.BodyMap) (*ProfitShareDeleteReceiverRsp, error) {
authorization, err := c.authorization(MethodPost, v3ProfitShareDeleteReceiver, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ProfitShareDeleteReceiver, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareDeleteReceiverRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareDeleteReceiver)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请分账账单
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_1_11.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_1_11.shtml
func (c *ClientV3) V3ProfitShareBills(ctx context.Context, bm gopay.BodyMap) (*ProfitShareBillsRsp, error) {
uri := v3ProfitShareBills + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &ProfitShareBillsRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ProfitShareBills)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+203
View File
@@ -0,0 +1,203 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 申请退款API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_9.shtml
func (c *ClientV3) V3Refund(ctx context.Context, bm gopay.BodyMap) (wxRsp *RefundRsp, err error) {
authorization, err := c.authorization(MethodPost, v3DomesticRefund, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3DomesticRefund, authorization)
if err != nil {
return nil, err
}
wxRsp = &RefundRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(RefundOrderResponse)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询单笔退款API
// 注意:商户查询时,bm 可传 nil;服务商时,传相应query参数
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_10.shtml
func (c *ClientV3) V3RefundQuery(ctx context.Context, outRefundNo string, bm gopay.BodyMap) (wxRsp *RefundQueryRsp, err error) {
uri := fmt.Sprintf(v3DomesticRefundQuery, outRefundNo)
if bm != nil {
uri = fmt.Sprintf(v3DomesticRefundQuery, outRefundNo) + "?" + bm.EncodeURLParams()
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &RefundQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(RefundQueryResponse)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 申请退款API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_1.shtml
func (c *ClientV3) V3EcommerceRefund(ctx context.Context, bm gopay.BodyMap) (wxRsp *EcommerceRefundRsp, err error) {
authorization, err := c.authorization(MethodPost, v3CommerceRefund, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3CommerceRefund, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceRefundRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceRefund)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 通过微信支付退款单号查询退款API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_2.shtml
func (c *ClientV3) V3EcommerceRefundQueryById(ctx context.Context, refundId string, bm gopay.BodyMap) (wxRsp *EcommerceRefundQueryRsp, err error) {
uri := fmt.Sprintf(v3CommerceRefundQueryById, refundId) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceRefundQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceRefundQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 通过商户退款单号查询退款API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_2.shtml
func (c *ClientV3) V3EcommerceRefundQueryByNo(ctx context.Context, outRefundNo string, bm gopay.BodyMap) (wxRsp *EcommerceRefundQueryRsp, err error) {
uri := fmt.Sprintf(v3CommerceRefundQueryByNo, outRefundNo) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceRefundQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceRefundQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 垫付退款回补API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_4.shtml
func (c *ClientV3) V3EcommerceRefundAdvance(ctx context.Context, refundId string, bm gopay.BodyMap) (wxRsp *EcommerceRefundAdvanceRsp, err error) {
url := fmt.Sprintf(v3CommerceRefundAdvance, refundId)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceRefundAdvanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceRefundAdvance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询垫付回补结果API
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_6_5.shtml
func (c *ClientV3) V3EcommerceRefundAdvanceResult(ctx context.Context, refundId string, bm gopay.BodyMap) (wxRsp *EcommerceRefundAdvanceRsp, err error) {
uri := fmt.Sprintf(v3CommerceRefundAdvanceResult, refundId) + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &EcommerceRefundAdvanceRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceRefundAdvance)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+361
View File
@@ -0,0 +1,361 @@
package wechat
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 创单结单合并API
// Code = 0 is success
// 注意:限制条件:【免确认订单模式】,用户已授权状态下,可调用该接口。
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_1.shtml
func (c *ClientV3) V3ScoreDirectComplete(ctx context.Context, bm gopay.BodyMap) (wxRsp *ScoreDirectCompleteRsp, err error) {
authorization, err := c.authorization(MethodPost, v3ScoreDirectComplete, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ScoreDirectComplete, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreDirectCompleteRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreDirectComplete)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商户预授权API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_2.shtml
func (c *ClientV3) V3ScorePermission(ctx context.Context, bm gopay.BodyMap) (wxRsp *ScorePermissionRsp, err error) {
authorization, err := c.authorization(MethodPost, v3ScorePermission, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ScorePermission, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScorePermissionRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScorePermission)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询用户授权记录(授权协议号)API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_3.shtml
func (c *ClientV3) V3ScorePermissionQuery(ctx context.Context, authCode, serviceId string) (wxRsp *ScorePermissionQueryRsp, err error) {
uri := fmt.Sprintf(v3ScorePermissionQuery, authCode) + "?service_id=" + serviceId
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScorePermissionQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScorePermissionQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 解除用户授权关系(授权协议号)API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_4.shtml
func (c *ClientV3) V3ScorePermissionTerminate(ctx context.Context, authCode, serviceId, reason string) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3ScorePermissionTerminate, authCode)
bm := make(gopay.BodyMap)
bm.Set("service_id", serviceId).
Set("reason", reason)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询用户授权记录(openidAPI
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_5.shtml
func (c *ClientV3) V3ScorePermissionOpenidQuery(ctx context.Context, appid, openid, serviceid string) (wxRsp *ScorePermissionOpenidQueryRsp, err error) {
uri := fmt.Sprintf(v3ScorePermissionOpenidQuery, openid) + "?appid=" + appid + "&service_id=" + serviceid
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScorePermissionOpenidQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScorePermissionOpenidQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 解除用户授权关系(openidAPI
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_6.shtml
func (c *ClientV3) V3ScorePermissionOpenidTerminate(ctx context.Context, appid, openid, serviceid, reason string) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3ScorePermissionOpenidTerminate, openid)
bm := make(gopay.BodyMap)
bm.Set("service_id", serviceid).
Set("appid", appid).
Set("reason", reason)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 创建支付分订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_14.shtml
func (c *ClientV3) V3ScoreOrderCreate(ctx context.Context, bm gopay.BodyMap) (wxRsp *ScoreOrderCreateRsp, err error) {
authorization, err := c.authorization(MethodPost, v3ScoreOrderCreate, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3ScoreOrderCreate, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderCreateRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderCreate)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询支付分订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_15.shtml
func (c *ClientV3) V3ScoreOrderQuery(ctx context.Context, orderNoType OrderNoType, appid, orderNo, serviceid string) (wxRsp *ScoreOrderQueryRsp, err error) {
var uri string
switch orderNoType {
case OutTradeNo:
uri = v3ScoreOrderQuery + "?appid=" + appid + "&out_order_no=" + orderNo + "&service_id=" + serviceid
case QueryId:
uri = v3ScoreOrderQuery + "?appid=" + appid + "&query_id=" + orderNo + "&service_id=" + serviceid
default:
return nil, errors.New("unsupported order number type")
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 取消支付分订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_16.shtml
func (c *ClientV3) V3ScoreOrderCancel(ctx context.Context, appid, tradeNo, serviceid, reason string) (wxRsp *ScoreOrderCancelRsp, err error) {
url := fmt.Sprintf(v3ScoreOrderCancel, tradeNo)
bm := make(gopay.BodyMap)
bm.Set("appid", appid).
Set("service_id", serviceid).
Set("reason", reason)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderCancelRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderCancel)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 修改订单金额API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_17.shtml
func (c *ClientV3) V3ScoreOrderModify(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *ScoreOrderModifyRsp, err error) {
url := fmt.Sprintf(v3ScoreOrderModify, tradeNo)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderModifyRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderModify)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 完结支付分订单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_18.shtml
func (c *ClientV3) V3ScoreOrderComplete(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *ScoreOrderCompleteRsp, err error) {
url := fmt.Sprintf(v3ScoreOrderComplete, tradeNo)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderCompleteRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderComplete)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商户发起催收扣款API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_19.shtml
func (c *ClientV3) V3ScoreOrderPay(ctx context.Context, appid, tradeNo, serviceid string) (wxRsp *ScoreOrderPayRsp, err error) {
url := fmt.Sprintf(v3ScoreOrderPay, tradeNo)
bm := make(gopay.BodyMap)
bm.Set("appid", appid).
Set("service_id", serviceid)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderPayRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderPay)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 同步服务订单信息API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_20.shtml
func (c *ClientV3) V3ScoreOrderSync(ctx context.Context, tradeNo string, bm gopay.BodyMap) (wxRsp *ScoreOrderSyncRsp, err error) {
url := fmt.Sprintf(v3ScoreOrderSync, tradeNo)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &ScoreOrderSyncRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(ScoreOrderSync)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+169
View File
@@ -0,0 +1,169 @@
package wechat
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/pkg/xpem"
)
// Deprecated
// 推荐使用 wechat.V3VerifySignByPK()
func V3VerifySign(timestamp, nonce, signBody, sign, wxPubKeyContent string) (err error) {
publicKey, err := xpem.DecodePublicKey([]byte(wxPubKeyContent))
if err != nil {
return err
}
str := timestamp + "\n" + nonce + "\n" + signBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(sign)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}
// 微信V3 版本验签(同步/异步)
// wxPublicKey:微信平台证书公钥内容,通过 client.WxPublicKey() 获取
func V3VerifySignByPK(timestamp, nonce, signBody, sign string, wxPublicKey *rsa.PublicKey) (err error) {
str := timestamp + "\n" + nonce + "\n" + signBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(sign)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(wxPublicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}
// PaySignOfJSAPI 获取 JSAPI paySign
// 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
func (c *ClientV3) PaySignOfJSAPI(appid, prepayid string) (jsapi *JSAPIPayParams, err error) {
ts := util.Int642String(time.Now().Unix())
nonceStr := util.RandomString(32)
pkg := "prepay_id=" + prepayid
_str := appid + "\n" + ts + "\n" + nonceStr + "\n" + pkg + "\n"
sign, err := c.rsaSign(_str)
if err != nil {
return nil, err
}
jsapi = &JSAPIPayParams{
AppId: appid,
TimeStamp: ts,
NonceStr: nonceStr,
Package: pkg,
SignType: SignTypeRSA,
PaySign: sign,
}
return jsapi, nil
}
// PaySignOfApp 获取 App sign
// 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
func (c *ClientV3) PaySignOfApp(appid, prepayid string) (app *AppPayParams, err error) {
ts := util.Int642String(time.Now().Unix())
nonceStr := util.RandomString(32)
_str := appid + "\n" + ts + "\n" + nonceStr + "\n" + prepayid + "\n"
sign, err := c.rsaSign(_str)
if err != nil {
return nil, err
}
app = &AppPayParams{
Appid: appid,
Partnerid: c.Mchid,
Prepayid: prepayid,
Package: "Sign=WXPay",
Noncestr: nonceStr,
Timestamp: ts,
Sign: sign,
}
return app, nil
}
// PaySignOfApplet 获取 小程序 paySign
// 文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
func (c *ClientV3) PaySignOfApplet(appid, prepayid string) (applet *AppletParams, err error) {
jsapi, err := c.PaySignOfJSAPI(appid, prepayid)
if err != nil {
return nil, err
}
applet = &AppletParams{
AppId: jsapi.AppId,
TimeStamp: jsapi.TimeStamp,
NonceStr: jsapi.NonceStr,
Package: jsapi.Package,
SignType: jsapi.SignType,
PaySign: jsapi.PaySign,
}
return applet, nil
}
// v3 鉴权请求Header
func (c *ClientV3) authorization(method, path string, bm gopay.BodyMap) (string, error) {
var (
jb = ""
timestamp = time.Now().Unix()
nonceStr = util.RandomString(32)
)
if bm != nil {
jb = bm.JsonBody()
}
ts := util.Int642String(timestamp)
_str := method + "\n" + path + "\n" + ts + "\n" + nonceStr + "\n" + jb + "\n"
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_SignString:\n%s", _str)
}
sign, err := c.rsaSign(_str)
if err != nil {
return "", err
}
return Authorization + ` mchid="` + c.Mchid + `",nonce_str="` + nonceStr + `",timestamp="` + ts + `",serial_no="` + c.SerialNo + `",signature="` + sign + `"`, nil
}
func (c *ClientV3) rsaSign(str string) (string, error) {
if c.privateKey == nil {
return "", errors.New("privateKey can't be nil")
}
h := sha256.New()
h.Write([]byte(str))
result, err := rsa.SignPKCS1v15(rand.Reader, c.privateKey, crypto.SHA256, h.Sum(nil))
if err != nil {
return util.NULL, fmt.Errorf("[%w]: %+v", gopay.SignatureErr, err)
}
return base64.StdEncoding.EncodeToString(result), nil
}
// 自动同步请求验签
func (c *ClientV3) verifySyncSign(si *SignInfo) (err error) {
if c.autoSign && c.wxPublicKey != nil {
if si != nil {
str := si.HeaderTimestamp + "\n" + si.HeaderNonce + "\n" + si.SignBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(si.HeaderSignature)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(c.wxPublicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}
return errors.New("auto verify sign, bug SignInfo is nil")
}
return nil
}
+117
View File
@@ -0,0 +1,117 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 服务人员注册API
// 注意:入参加密字段数据加密:client.V3EncryptText()
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_4_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_4_1.shtml
func (c *ClientV3) V3SmartGuideReg(ctx context.Context, bm gopay.BodyMap) (wxRsp *SmartGuideRegRsp, err error) {
authorization, err := c.authorization(MethodPost, v3GuideReg, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3GuideReg, authorization)
if err != nil {
return nil, err
}
wxRsp = &SmartGuideRegRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(SmartGuideReg)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 服务人员分配API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_4_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_4_2.shtml
func (c *ClientV3) V3SmartGuideAssign(ctx context.Context, guideId, tradeNo string) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3GuideAssign, guideId)
bm := make(gopay.BodyMap)
bm.Set("out_trade_no", tradeNo)
authorization, err := c.authorization(MethodPost, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 服务人员查询API
// 注意:入参加密字段数据加密:client.V3EncryptText(),返回参数加密字段解密:client.V3DecryptText()
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_4_3.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_4_3.shtml
func (c *ClientV3) V3SmartGuideQuery(ctx context.Context, bm gopay.BodyMap) (wxRsp *SmartGuideQueryRsp, err error) {
if err = bm.CheckEmptyError("store_id"); err != nil {
return nil, err
}
uri := v3GuideQuery + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &SmartGuideQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(SmartGuideQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 服务人员信息更新API
// 注意:入参加密字段数据加密:client.V3EncryptText()
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter8_4_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter8_4_4.shtml
func (c *ClientV3) V3SmartGuideUpdate(ctx context.Context, guideId string, bm gopay.BodyMap) (wxRsp *EmptyRsp, err error) {
url := fmt.Sprintf(v3GuideUpdate, guideId)
authorization, err := c.authorization(MethodPATCH, url, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPatch(ctx, bm, url, authorization)
if err != nil {
return nil, err
}
wxRsp = &EmptyRsp{Code: Success, SignInfo: si}
if res.StatusCode != http.StatusNoContent {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+413
View File
@@ -0,0 +1,413 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"github.com/go-pay/gopay"
)
// 发起批量转账API
// 注意:入参加密字段数据加密:client.V3EncryptText()
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_1.shtml
func (c *ClientV3) V3Transfer(ctx context.Context, bm gopay.BodyMap) (*TransferRsp, error) {
authorization, err := c.authorization(MethodPost, v3Transfer, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3Transfer, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Transfer)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 发起批量转账API(服务商)
// 注意:入参加密字段数据加密:client.V3EncryptText()
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_1.shtml
func (c *ClientV3) V3PartnerTransfer(ctx context.Context, bm gopay.BodyMap) (*TransferRsp, error) {
authorization, err := c.authorization(MethodPost, v3PartnerTransfer, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3PartnerTransfer, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Transfer)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 微信批次单号查询批次单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_2.shtml
func (c *ClientV3) V3TransferQuery(ctx context.Context, batchId string, bm gopay.BodyMap) (*TransferQueryRsp, error) {
url := fmt.Sprintf(v3TransferQuery, batchId)
bm.Remove("batch_id")
uri := url + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 微信批次单号查询批次单API(服务商)
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_2.shtml
func (c *ClientV3) V3PartnerTransferQuery(ctx context.Context, batchId string, bm gopay.BodyMap) (*PartnerTransferQueryRsp, error) {
url := fmt.Sprintf(v3PartnerTransferQuery, batchId)
bm.Remove("batch_id")
uri := url + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &PartnerTransferQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerTransferQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 微信明细单号查询明细单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_3.shtml
func (c *ClientV3) V3TransferDetail(ctx context.Context, batchId, detailId string) (*TransferDetailRsp, error) {
url := fmt.Sprintf(v3TransferDetail, batchId, detailId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferDetailQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 微信明细单号查询明细单API(服务商)
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_3.shtml
func (c *ClientV3) V3PartnerTransferDetail(ctx context.Context, batchId, detailId string) (*PartnerTransferDetailRsp, error) {
url := fmt.Sprintf(v3PartnerTransferDetail, batchId, detailId)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &PartnerTransferDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerTransferDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// Deprecated
// 推荐直接使用 client.V3TransferDetail() 方法
func (c *ClientV3) V3TransferDetailQuery(ctx context.Context, batchId, detailId string) (*TransferDetailRsp, error) {
return c.V3TransferDetail(ctx, batchId, detailId)
}
// 商家批次单号查询批次单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_4.shtml
func (c *ClientV3) V3TransferMerchantQuery(ctx context.Context, outBatchNo string, bm gopay.BodyMap) (*TransferMerchantQueryRsp, error) {
url := fmt.Sprintf(v3TransferMerchantQuery, outBatchNo)
bm.Remove("out_batch_no")
uri := url + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferMerchantQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferMerchantQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商家批次单号查询批次单API(服务商)
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_4.shtml
func (c *ClientV3) V3PartnerTransferMerchantQuery(ctx context.Context, outBatchNo string, bm gopay.BodyMap) (*PartnerTransferMerchantQueryRsp, error) {
url := fmt.Sprintf(v3PartnerTransferMerchantQuery, outBatchNo)
bm.Remove("out_batch_no")
uri := url + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &PartnerTransferMerchantQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerTransferMerchantQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商家明细单号查询明细单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter3_5.shtml
func (c *ClientV3) V3TransferMerchantDetail(ctx context.Context, outBatchNo, outDetailNo string) (*TransferMerchantDetailRsp, error) {
url := fmt.Sprintf(v3TransferMerchantDetail, outBatchNo, outDetailNo)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferMerchantDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferMerchantDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 商家明细单号查询明细单API(服务商)
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter3_5.shtml
func (c *ClientV3) V3PartnerTransferMerchantDetail(ctx context.Context, outBatchNo, outDetailNo string) (*PartnerTransferMerchantDetailRsp, error) {
url := fmt.Sprintf(v3PartnerTransferMerchantDetail, outBatchNo, outDetailNo)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &PartnerTransferMerchantDetailRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(PartnerTransferMerchantDetail)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// Deprecated
// 推荐直接使用 client.V3TransferMerchantDetail() 方法
func (c *ClientV3) V3TransferMerchantDetailQuery(ctx context.Context, outBatchNo, outDetailNo string) (*TransferMerchantDetailRsp, error) {
return c.V3TransferMerchantDetail(ctx, outBatchNo, outDetailNo)
}
// 转账电子回单申请受理API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_1.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter4_1.shtml
func (c *ClientV3) V3TransferReceipt(ctx context.Context, outBatchNo string) (*TransferReceiptRsp, error) {
bm := make(gopay.BodyMap)
bm.Set("out_batch_no", outBatchNo)
authorization, err := c.authorization(MethodPost, v3TransferReceipt, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3TransferReceipt, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferReceiptRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferReceipt)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询转账电子回单API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_2.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter4_2.shtml
func (c *ClientV3) V3TransferReceiptQuery(ctx context.Context, outBatchNo string) (*TransferReceiptQueryRsp, error) {
url := fmt.Sprintf(v3TransferReceiptQuery, outBatchNo)
authorization, err := c.authorization(MethodGet, url, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, url, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferReceiptQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferReceiptQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 转账明细电子回单受理API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_4.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter4_4.shtml
func (c *ClientV3) V3TransferDetailReceipt(ctx context.Context, bm gopay.BodyMap) (*TransferDetailReceiptRsp, error) {
authorization, err := c.authorization(MethodPost, v3TransferDetailReceipt, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3TransferDetailReceipt, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferDetailReceiptRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferDetailReceipt)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询转账明细电子回单受理结果API
// Code = 0 is success
// 商户文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer/chapter4_5.shtml
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter4_5.shtml
func (c *ClientV3) V3TransferDetailReceiptQuery(ctx context.Context, bm gopay.BodyMap) (*TransferDetailReceiptQueryRsp, error) {
uri := v3TransferDetailReceiptQuery + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &TransferDetailReceiptQueryRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TransferDetailReceiptQuery)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
+176
View File
@@ -0,0 +1,176 @@
package wechat
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
)
// 特约商户余额提现、二级商户预约提现
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter6_1.shtml
func (c *ClientV3) V3Withdraw(ctx context.Context, bm gopay.BodyMap) (*WithdrawRsp, error) {
if err := bm.CheckEmptyError("sub_mchid", "out_request_no", "amount"); err != nil {
return nil, err
}
authorization, err := c.authorization(MethodPost, v3Withdraw, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3Withdraw, authorization)
if err != nil {
return nil, err
}
wxRsp := &WithdrawRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(Withdraw)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 查询特约商户提现状态、二级商户查询预约提现状态
// 注意:withdrawId 和 outRequestNo 二选一
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter6_2.shtml
func (c *ClientV3) V3WithdrawStatus(ctx context.Context, withdrawId, outRequestNo string, bm gopay.BodyMap) (*WithdrawStatusRsp, error) {
if withdrawId == gopay.NULL && outRequestNo == gopay.NULL {
return nil, fmt.Errorf("[%w]: withdrawId[%s] and outRequestNo[%s] empty at the same time", gopay.MissParamErr, withdrawId, outRequestNo)
}
var uri string
if withdrawId != gopay.NULL {
uri = fmt.Sprintf(v3WithdrawStatusById, withdrawId) + "?" + bm.EncodeURLParams()
} else {
uri = fmt.Sprintf(v3WithdrawStatusByNo, outRequestNo) + "?" + bm.EncodeURLParams()
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &WithdrawStatusRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(WithdrawStatus)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 电商平台预约提现
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_8_2.shtml
func (c *ClientV3) V3EcommerceWithdraw(ctx context.Context, bm gopay.BodyMap) (*EcommerceWithdrawRsp, error) {
if err := bm.CheckEmptyError("out_request_no", "amount", "account_type"); err != nil {
return nil, err
}
authorization, err := c.authorization(MethodPost, v3EcommerceWithdraw, bm)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdPost(ctx, bm, v3EcommerceWithdraw, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceWithdrawRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceWithdraw)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 电商平台查询预约提现状态
// 注意:withdrawId 和 outRequestNo 二选一
// Code = 0 is success
// 电商文档:https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter7_8_6.shtml
func (c *ClientV3) V3EcommerceWithdrawStatus(ctx context.Context, withdrawId, outRequestNo string) (*EcommerceWithdrawStatusRsp, error) {
if withdrawId == gopay.NULL && outRequestNo == gopay.NULL {
return nil, fmt.Errorf("[%w]: withdrawId[%s] and outRequestNo[%s] empty at the same time", gopay.MissParamErr, withdrawId, outRequestNo)
}
var uri string
if withdrawId != gopay.NULL {
uri = fmt.Sprintf(v3EcommerceWithdrawStatusById, withdrawId)
} else {
uri = fmt.Sprintf(v3EcommerceWithdrawStatusByNo, outRequestNo)
}
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp := &EcommerceWithdrawStatusRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(EcommerceWithdrawStatus)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}
// 按日下载提现异常文件
// 注意:如 bill_date 为空,默认查前一天的
// Code = 0 is success
// 服务商文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/transfer_partner/chapter6_3.shtml
func (c *ClientV3) V3WithdrawDownloadErrBill(ctx context.Context, bm gopay.BodyMap) (wxRsp *BillRsp, err error) {
if bm != nil {
if bm.GetString("bill_date") == util.NULL {
now := time.Now()
yesterday := time.Date(now.Year(), now.Month(), now.Day()-1, 0, 0, 0, 0, time.Local).Format(util.DateLayout)
bm.Set("bill_date", yesterday)
}
bm.Remove("bill_type")
}
uri := fmt.Sprintf(v3WithdrawDownloadErrBill, "NO_SUCC") + "?" + bm.EncodeURLParams()
authorization, err := c.authorization(MethodGet, uri, nil)
if err != nil {
return nil, err
}
res, si, bs, err := c.doProdGet(ctx, uri, authorization)
if err != nil {
return nil, err
}
wxRsp = &BillRsp{Code: Success, SignInfo: si}
wxRsp.Response = new(TradeBill)
if err = json.Unmarshal(bs, wxRsp.Response); err != nil {
return nil, fmt.Errorf("[%w]: %v, bytes: %s", gopay.UnmarshalErr, err, string(bs))
}
if res.StatusCode != http.StatusOK {
wxRsp.Code = res.StatusCode
wxRsp.Error = string(bs)
return wxRsp, nil
}
return wxRsp, c.verifySyncSign(si)
}