package wxpayapi

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"crypto/tls"
	"encoding/xml"
	"fmt"
	"net/http"
	"sort"
	"strings"
	"time"

	"git.rosy.net.cn/baseapi"
	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
	"github.com/clbanning/mxj"
)

const (
	prodURL    = "https://api.mch.weixin.qq.com"
	sandboxURL = "https://api.mch.weixin.qq.com/sandboxnew"
)

const (
	ResponseCodeSuccess = "SUCCESS"
	ResponseCodeFail    = "FAIL"

	sigKey        = "sign"
	sigTypeKey    = "sign_type"
	sigTypeMd5    = "MD5"
	sigTypeSha256 = "HMAC-SHA256"

	specialAction = "mmpaymkttransfers/promotion/transfers"
)

const (
	FeeTypeCNY = "CNY"
)

const (
	TradeTypeJSAPI    = "JSAPI" // JSAPI支付（或小程序支付）
	TradeTypeNative   = "NATIVE"
	TradeTypeAPP      = "APP"
	TradeTypeMicroPay = "MICROPAY"

	OptYes = "Y"
	OptNo  = "N"

	Relation        = "PARTNER"
	AccountTypeWx   = "PERSONAL_WECHATID"
	AccountTypeOpen = "PERSONAL_OPENID"
	CheckName       = "NO_CHECK"
)

var (
	specialSignActions = map[string]string{
		"secapi/pay/multiprofitsharing":   "secapi/pay/multiprofitsharing",
		"pay/profitsharingaddreceiver":    "pay/profitsharingaddreceiver",
		"pay/profitsharingremovereceiver": "pay/profitsharingremovereceiver",
		"secapi/pay/profitsharingreturn":  "secapi/pay/profitsharingreturn",
		"secapi/pay/profitsharingfinish":  "secapi/pay/profitsharingfinish",
	}
)

type API struct {
	appID  string
	appKey string
	mchID  string
	client *http.Client
	config *platformapi.APIConfig
}

type RequestBase struct {
	XMLName xml.Name `json:"-" xml:"xml"`

	AppID string `json:"appid" xml:"appid,omitempty"`
	MchID string `json:"mch_id" xml:"mch_id,omitempty"`
	// 企业付款基础参数不一样
	AppID2 string `json:"mch_appid,omitempty" xml:"mch_appid,omitempty"`
	MchID2 string `json:"mchid,omitempty" xml:"mchid,omitempty"`

	DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`
	SignType   string `json:"sign_type,omitempty" xml:"sign_type,omitempty"`
}

type IRequestBase interface {
	SetAppID(action, appID string)
	SetMchID(action, mchID string)
	SetNonceStr(nonceStr string)
	SetSign(sign string)
	SetSignType(signType string)
}

func (r *RequestBase) SetAppID(action, appID string) {
	if action != specialAction {
		r.AppID = appID
	} else {
		r.AppID2 = appID
	}
}

func (r *RequestBase) SetMchID(action, mchID string) {
	if action != specialAction {
		r.MchID = mchID
	} else {
		r.MchID2 = mchID
	}
}

func (r *RequestBase) SetNonceStr(nonceStr string) {
	r.NonceStr = nonceStr
}

func (r *RequestBase) SetSign(sign string) {
	r.Sign = sign
}

func (r *RequestBase) SetSignType(signType string) {
	r.SignType = signType
}

type OrderQueryParam struct {
	RequestBase
	TransactionID string `json:"transaction_id" xml:"transaction_id"`
	OutTradeNo    string `json:"out_trade_no" xml:"out_trade_no"`
}

type OrderInfo struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	AppID      string `json:"appid" xml:"appid"`
	DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`
	ResultCode string `json:"result_code" xml:"result_code"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`

	Attach         string `json:"attach"`
	BankType       string `json:"bank_type"`
	CashFee        string `json:"cash_fee"`
	CashFeeType    string `json:"cash_fee_type"`
	FeeType        string `json:"fee_type"`
	IsSubscribe    string `json:"is_subscribe"`
	OpenID         string `json:"openid"`
	OutTradeNo     string `json:"out_trade_no"`
	TimeEnd        string `json:"time_end"`
	TotalFee       string `json:"total_fee"`
	TradeState     string `json:"trade_state"`
	TradeStateDesc string `json:"trade_state_desc"`
	TradeType      string `json:"trade_type"`
	TransactionID  string `json:"transaction_id"`
}

type CreateOrderSceneInfo struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	AreaCode string `json:"area_code"`
	Address  string `json:"address"`
}

type CreateOrderParam struct {
	RequestBase
	Body           string `json:"body" xml:"body"`
	NotifyURL      string `json:"notify_url" xml:"notify_url"`
	OutTradeNo     string `json:"out_trade_no" xml:"out_trade_no"`
	SpbillCreateIP string `json:"spbill_create_ip" xml:"spbill_create_ip"`
	TradeType      string `json:"trade_type" xml:"trade_type"`
	TotalFee       int    `json:"total_fee" xml:"total_fee"`

	// SubAppID      string `json:"sub_appid" xml:"sub_appid,omitempty"`
	SubMchID      string `json:"sub_mch_id" xml:"sub_mch_id,omitempty"`
	Detail        CData  `json:"detail.omitempty" xml:"detail,omitempty"`
	Attach        string `json:"attach,omitempty" xml:"attach,omitempty"`
	FeeType       string `json:"fee_type,omitempty" xml:"fee_type,omitempty"`
	TimeStart     string `json:"time_start,omitempty" xml:"time_start,omitempty"`
	TimeExpire    string `json:"time_expire,omitempty" xml:"time_expire,omitempty"`
	GoodsTag      string `json:"goods_tag,omitempty" xml:"goods_tag,omitempty"`
	ProductID     string `json:"product_id,omitempty" xml:"product_id,omitempty"`
	LimitPay      string `json:"limit_pay,omitempty" xml:"limit_pay,omitempty"`
	OpenID        string `json:"openid,omitempty" xml:"openid,omitempty"`
	Receipt       string `json:"receipt,omitempty" xml:"receipt,omitempty"`
	SceneInfo     string `json:"scene_info,omitempty" xml:"scene_info,omitempty"`
	ProfitSharing string `json:"profit_sharing,omitempty" xml:"profit_sharing,omitempty"`
}

type CloseOrderParam struct {
	RequestBase
	OutTradeNo string `json:"out_trade_no" xml:"out_trade_no"`
}

type CreateOrderResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	AppID      string `json:"appid" xml:"appid"`
	DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`
	ResultCode string `json:"result_code" xml:"result_code"`
	ResultMsg  string `json:"result_msg" xml:"result_msg"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`

	TradeType string `json:"trade_type"`
	PrepayID  string `json:"prepay_id"`
	CodeURL   string `json:"code_url"`
}

type PayRefundParam struct {
	RequestBase

	TransactionID string `json:"transaction_id,omitempty" xml:"transaction_id,omitempty"`
	OutTradeNo    string `json:"out_trade_no,omitempty" xml:"out_trade_no,omitempty"`
	OutRefundNo   string `json:"out_refund_no" xml:"out_refund_no"`
	TotalFee      int    `json:"total_fee" xml:"total_fee"`
	RefundFee     int    `json:"refund_fee" xml:"refund_fee"`

	RefundFeeType string `json:"refund_fee_type,omitempty" xml:"refund_fee_type,omitempty"`
	RefundDesc    CData  `json:"refund_desc,omitempty" xml:"refund_desc,omitempty"`
	NotifyURL     string `json:"notify_url,omitempty" xml:"notify_url,omitempty"`
}

type PayRefundResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	AppID      string `json:"appid" xml:"appid"`
	DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`
	ResultCode string `json:"result_code" xml:"result_code"`
	ResultMsg  string `json:"result_msg" xml:"result_msg"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`

	CashFee           string `json:"cash_fee"`
	CashRefundFee     string `json:"cash_refund_fee"`
	CouponRefundCount string `json:"coupon_refund_count"`
	CouponRefundFee   string `json:"coupon_refund_fee"`
	OutRefundNo       string `json:"out_refund_no"`
	OutTradeNo        string `json:"out_trade_no"`
	RefundChannel     string `json:"refund_channel"`
	RefundFee         string `json:"refund_fee"`
	RefundID          string `json:"refund_id"`
	TotalFee          string `json:"total_fee"`
	TransactionID     string `json:"transaction_id"`
}

type MultiProfitSharingParam struct {
	RequestBase
	TransactionID string `json:"transaction_id" xml:"transaction_id"`
	OutOrderNo    string `json:"out_order_no" xml:"out_order_no"`
	Receivers     CData  `json:"receivers" xml:"receivers"`
}

type MultiProfitSharingResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	ResultCode string `json:"result_code" xml:"result_code"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
	AppID      string `json:"appid" xml:"appid"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`

	TransactionID string `json:"transaction_id" xml:"transaction_id"`
	OutOrderNo    string `json:"out_order_no" xml:"out_order_no"`
	OrderID       string `json:"order_id" xml:"order_id"`
}

type TransfersParam struct {
	RequestBase
	PartnerTradeNo string `json:"partner_trade_no" xml:"partner_trade_no"`
	OpenID         string `json:"openid" xml:"openid"`
	CheckName      string `json:"check_name" xml:"check_name"`
	ReUserName     string `json:"re_user_name,omitempty" xml:"re_user_name,omitempty"`
	Amount         int    `json:"amount" xml:"amount"`
	Desc           string `json:"desc" xml:"desc"`
	SpbillCreateIP string `json:"spbill_create_ip" xml:"spbill_create_ip"`
}

type TransfersResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	ResultCode string `json:"result_code" xml:"result_code"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
	AppID      string `json:"appid" xml:"appid"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	DeviceInfo string `json:"device_info,omitempty" xml:"device_info,omitempty"`

	PartnerTradeNo string `json:"partner_trade_no" xml:"partner_trade_no"`
	PaymentNo      string `json:"payment_no" xml:"payment_no"`
	PaymentTime    string `json:"payment_time" xml:"payment_time"`
}

type ProfitSharingReceiverParam struct {
	RequestBase
	Receiver CData `json:"receiver" xml:"receiver"`
}

type ProfitSharingReceiverResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	ResultCode string `json:"result_code" xml:"result_code"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
	AppID      string `json:"appid" xml:"appid"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`
	Receiver   string `json:"receiver" xml:"receiver"`
}

type ReturnProfitSharingParam struct {
	RequestBase
	// OrderID           string `json:"order_id" xml:"order_id"`
	OutOrderNo        string `json:"out_order_no" xml:"out_order_no"`
	OutReturnNo       string `json:"out_return_no" xml:"out_return_no"`
	ReturnAccountType string `json:"return_account_type" xml:"return_account_type"`
	ReturnAccount     string `json:"return_account" xml:"return_account"`
	ReturnAmount      int    `json:"return_amount" xml:"return_amount"`
	Description       string `json:"description" xml:"description"`
}

type ReturnProfitSharingResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ErrorCode  string `json:"error_code,omitempty" xml:"error_code,omitempty"`
	ErrorMsg   string `json:"error_msg,omitempty" xml:"error_msg,omitempty"`

	AppID             string `json:"appid" xml:"appid"`
	MchID             string `json:"mch_id" xml:"mch_id"`
	NonceStr          string `json:"nonce_str" xml:"nonce_str"`
	Sign              string `json:"sign" xml:"sign"`
	OrderID           string `json:"order_id" xml:"order_id"`
	OutOrderNo        string `json:"out_order_no" xml:"out_order_no"`
	OutReturnNo       string `json:"out_return_no" xml:"out_return_no"`
	ReturnNo          string `json:"return_no" xml:"return_no"`
	ReturnAccountType string `json:"return_account_type" xml:"return_account_type"`
	ReturnAccount     string `json:"return_account" xml:"return_account"`
	ReturnAmount      int    `json:"return_amount" xml:"return_amount"`
	Description       string `json:"description" xml:"description"`
	Result            string `json:"result" xml:"result"`
	FailReason        string `json:"fail_reason,omitempty" xml:"fail_reason,omitempty"`
	FinishTime        string `json:"finish_time" xml:"finish_time"`
}

type FinishProfitSharingParam struct {
	RequestBase
	TransactionID string `json:"transaction_id" xml:"transaction_id"`
	OutOrderNo    string `json:"out_order_no" xml:"out_order_no"`
	Description   string `json:"description" xml:"description"`
}

type FinishProfitSharingResult struct {
	ReturnCode string `json:"return_code" xml:"return_code"`
	ReturnMsg  string `json:"return_msg" xml:"return_msg"`

	ResultCode string `json:"result_code" xml:"result_code"`
	ErrCode    string `json:"err_code,omitempty" xml:"err_code,omitempty"`
	ErrCodeDes string `json:"err_code_des,omitempty" xml:"err_code_des,omitempty"`
	AppID      string `json:"appid" xml:"appid"`
	MchID      string `json:"mch_id" xml:"mch_id"`
	NonceStr   string `json:"nonce_str" xml:"nonce_str"`
	Sign       string `json:"sign" xml:"sign"`

	TransactionID string `json:"transaction_id" xml:"transaction_id"`
	OutOrderNo    string `json:"out_order_no" xml:"out_order_no"`
	OrderID       string `json:"order_id" xml:"order_id"`
}

func New(appID, appKey, mchID string, config ...*platformapi.APIConfig) *API {
	curConfig := platformapi.DefAPIConfig
	if len(config) > 0 {
		curConfig = *config[0]
	}
	return &API{
		appID:  appID,
		appKey: appKey,
		mchID:  mchID,
		client: &http.Client{Timeout: curConfig.ClientTimeout},
		config: &curConfig,
	}
}

func NewWithCertificate(appID, appKey, mchID string, certPEMBlock, keyPEMBlock interface{}, config ...*platformapi.APIConfig) (a *API) {
	var certs tls.Certificate
	var err error
	if binCertPEMBlock, ok := certPEMBlock.([]byte); ok {
		certs, err = tls.X509KeyPair(binCertPEMBlock, keyPEMBlock.([]byte))
	} else {
		certs, err = tls.LoadX509KeyPair(certPEMBlock.(string), keyPEMBlock.(string))
	}
	if err == nil {
		// pool := x509.NewCertPool()
		// pool.AppendCertsFromPEM(rootCa.([]byte))
		a = New(appID, appKey, mchID, config...)
		a.client.Transport = &http.Transport{
			TLSClientConfig: &tls.Config{
				// RootCAs:      pool,
				Certificates: []tls.Certificate{certs},
			},
		}
	} else {
		baseapi.SugarLogger.Warnf("NewWithCertificate failed with err:%v", err)
	}
	return a
}

func (a *API) GetAppID() string {
	return a.appID
}

func (a *API) GetMchID() string {
	return a.mchID
}

func (a *API) signParam(signType string, params map[string]interface{}) (sig string) {
	var valueList []string
	for k, v := range params {
		if k != sigKey {
			if str := fmt.Sprint(v); str != "" {
				valueList = append(valueList, fmt.Sprintf("%s=%s", k, str))
			}
		}
	}
	sort.Sort(sort.StringSlice(valueList))
	valueList = append(valueList, fmt.Sprintf("key=%s", a.appKey))
	sig = strings.Join(valueList, "&")
	var binSig []byte
	if signType == sigTypeSha256 {
		mac := hmac.New(sha256.New, []byte(a.appKey))
		mac.Write([]byte(sig))
		binSig = mac.Sum(nil)
	} else {
		binSig2 := md5.Sum([]byte(sig))
		binSig = binSig2[:]
	}
	sig = fmt.Sprintf("%X", binSig)
	// baseapi.SugarLogger.Debug(sig)
	return sig
}

func mustMarshalXML(obj interface{}) []byte {
	byteArr, err := xml.Marshal(obj)
	if err != nil {
		panic(fmt.Sprintf("err when Marshal obj:%v with error:%v", obj, err))
	}
	return byteArr
}

func (a *API) AccessAPI(action string, requestParam IRequestBase) (retVal map[string]interface{}, err error) {
	requestParam.SetAppID(action, a.appID)
	requestParam.SetMchID(action, a.mchID)
	requestParam.SetNonceStr(utils.GetUUID())
	var sigType string
	if specialSignActions[action] != "" {
		sigType = sigTypeSha256
		requestParam.SetSignType(sigType)
	} else if action == specialAction {
		sigType = sigTypeMd5
	} else {
		sigType = sigTypeMd5
		requestParam.SetSignType(sigType)
	}
	signStr := a.signParam(sigType, utils.Struct2FlatMap(requestParam))
	requestParam.SetSign(signStr)

	fullURL := utils.GenerateGetURL(prodURL, action, nil)

	//var responseStr string
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			request, _ := http.NewRequest(http.MethodPost, fullURL, bytes.NewReader(mustMarshalXML(requestParam)))
			return request
		},
		a.config,
		func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
			//responseStr = bodyStr
			if jsonResult1 == nil {
				return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
			}
			retVal, errLevel, err = a.checkResultAsMap(jsonResult1[platformapi.KeyData].(string))
			return errLevel, err
		})
	if err == nil {
		if utils.Interface2String(retVal["result_code"]) != ResponseCodeSuccess {
			err = utils.NewErrorCode(utils.Interface2String(retVal["err_code_des"]), utils.Interface2String(retVal["err_code"]))
			if err != nil {
			}
			retVal = nil
		}
	}
	return retVal, err
}

func (a *API) parseXmlStrAsMap(xmlStr string) (result map[string]interface{}, errLevel string, err error) {
	mv, err := mxj.NewMapXml([]byte(xmlStr))
	if err != nil {
		errLevel = platformapi.ErrLevelGeneralFail
	} else {
		result = mv["xml"].(map[string]interface{})
	}
	return result, errLevel, err
}

func (a *API) checkResultAsMap(xmlStr string) (result map[string]interface{}, errLevel string, err error) {
	result, errLevel, err = a.parseXmlStrAsMap(xmlStr)
	if err == nil {
		returnCode := utils.Interface2String(result["return_code"])
		if returnCode != ResponseCodeSuccess {
			errLevel = platformapi.ErrLevelGeneralFail
			err = utils.NewErrorCode(utils.Interface2String(result["return_msg"]), returnCode)
			result = nil
		}
	}
	return result, errLevel, err
}

func SceneInfoList2String(sceneList []*CreateOrderSceneInfo) (str string) {
	return string(utils.MustMarshal(sceneList))
}

func Time2PayTime(t time.Time) (str string) {
	return t.Format("20060102150405")
}

func PayTime2Time(str string) (t time.Time) {
	t, _ = time.ParseInLocation("20060102150405", str, time.Local)
	return t
}

func (a *API) mapData2Err(mapData map[string]interface{}) (err error) {
	if resultCode := utils.Interface2String(mapData["result_code"]); resultCode != ResponseCodeSuccess {
		err = utils.NewErrorCode(utils.Interface2String(mapData["err_code_des"]), utils.Interface2String(mapData["err_code"]))
	}
	return err
}

func (a *API) translateResult(mapData map[string]interface{}, dataPtr interface{}) (err error) {
	if err = a.mapData2Err(mapData); err == nil && dataPtr != nil {
		err = utils.Map2StructByJson(mapData, dataPtr, false)
	}
	return err
}

func (a *API) OrderQuery(transactionID, outTradeNo string) (orderInfo *OrderInfo, err error) {
	param := &OrderQueryParam{
		TransactionID: transactionID,
		OutTradeNo:    outTradeNo,
	}
	retVal, err := a.AccessAPI("pay/orderquery", param)
	if err == nil {
		err = a.translateResult(retVal, &orderInfo)
	}
	return orderInfo, err
}

func (a *API) CreateUnifiedOrder(param *CreateOrderParam) (createOrderResult *CreateOrderResult, err error) {
	retVal, err := a.AccessAPI("pay/unifiedorder", param)
	if err == nil {
		err = a.translateResult(retVal, &createOrderResult)
	}
	return createOrderResult, err
}

// 关闭订单
// https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
// 注意：订单生成后不能马上调用关单接口，最短调用时间间隔为5分钟。
// 此函数好像可以重复操作，且关闭一个不存在的订单也不会报错的样
func (a *API) CloseOrder(outTradeNo string) (err error) {
	retVal, err := a.AccessAPI("pay/closeorder", &CloseOrderParam{
		OutTradeNo: outTradeNo,
	})
	if err == nil {
		err = a.translateResult(retVal, nil)
	}
	return err
}

func (a *API) PayRefund(param *PayRefundParam) (refundResult *PayRefundResult, err error) {
	if a.client.Transport == nil {
		return nil, fmt.Errorf("没有配置证书，不能退款")
	}
	retVal, err := a.AccessAPI("secapi/pay/refund", param)
	if err == nil {
		err = a.translateResult(retVal, &refundResult)
	}
	return refundResult, err
}

//请求多次分账
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_6&index=2
func (a *API) MultiProfitSharing(param *MultiProfitSharingParam) (result *MultiProfitSharingResult, err error) {
	retVal, err := a.AccessAPI("secapi/pay/multiprofitsharing", param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}

//企业付款
//https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
func (a *API) Transfers(param *TransfersParam) (result *TransfersResult, err error) {
	retVal, err := a.AccessAPI(specialAction, param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}

//添加分账接收方
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_3&index=4
func (a *API) AddProfitSharingReceiver(param *ProfitSharingReceiverParam) (result *ProfitSharingReceiverResult, err error) {
	retVal, err := a.AccessAPI("pay/profitsharingaddreceiver", param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}

//删除分账接收方
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_4&index=5
func (a *API) DeleteProfitSharingReceiver(param *ProfitSharingReceiverParam) (result *ProfitSharingReceiverResult, err error) {
	retVal, err := a.AccessAPI("pay/profitsharingremovereceiver", param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}

//分账回退
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_7&index=7
func (a *API) ReturnProfitSharing(param *ReturnProfitSharingParam) (result *ReturnProfitSharingResult, err error) {
	retVal, err := a.AccessAPI("secapi/pay/profitsharingreturn", param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}

//完结分账
//https://pay.weixin.qq.com/wiki/doc/api/allocation.php?chapter=27_5&index=6
func (a *API) FinishProfitSharing(param *FinishProfitSharingParam) (result *FinishProfitSharingResult, err error) {
	retVal, err := a.AccessAPI("secapi/pay/profitsharingfinish", param)
	if err == nil {
		err = a.translateResult(retVal, &result)
	}
	return result, err
}
