微信支付,退款,转账


废话不多说 ,直接上代码

 

gitee:https://gitee.com/wangqy415/wechat-payment.git

 

ApiUrls.php

<?php

namespace common\service\payments\wxpay\v2;

class ApiUrls
{
    // 创建订单链接
    public const PAY_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder';

    // 查询结果链接
    public const QUERY_URL = 'https://api.mch.weixin.qq.com/pay/orderquery';

    // 退款请求链接
    public const REFUND_URL = 'https://api.mch.weixin.qq.com/secapi/pay/refund';

    // 退款查询链接
    public const REFUND_QUERY_URL = 'https://api.mch.weixin.qq.com/pay/refundquery';

    // 转账交易链接
    public const TRANSFERS = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';

}

 

Base.php

<?php

/*
 *  +----------------------------------------------------------------------
 *  | wangqy
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023 http://upwqy.com All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Author: wangqy <529857614@qq.com>
 *  +----------------------------------------------------------------------
 */

namespace common\service\payments\wxpay\v2;

class Base
{
    protected $appId; // 应用appId
    protected $appSecret; // 小程序密钥
    protected $mchId; // 商户id
    protected $key; // 商户密钥key. https://pay.weixin.qq.com/index.php/account/api_cert.

    protected $notifyUrl; // 支付回调地址
    protected $refundNotifyUrl; // 退款回调地址

    // 证书地址
    private $clientCert;
    private $clientKey;

    /**
     * @return mixed
     */
    public function getAppId()
    {
        return $this->appId;
    }

    /**
     * @param mixed $appId
     */
    public function setAppId($appId): void
    {
        $this->appId = $appId;
    }

    /**
     * @return mixed
     */
    public function getAppSecret()
    {
        return $this->appSecret;
    }

    /**
     * @param mixed $appSecret
     */
    public function setAppSecret($appSecret): void
    {
        $this->appSecret = $appSecret;
    }

    /**
     * @return mixed
     */
    public function getMchId()
    {
        return $this->mchId;
    }

    /**
     * @param mixed $mchId
     */
    public function setMchId($mchId): void
    {
        $this->mchId = $mchId;
    }

    /**
     * @return mixed
     */
    public function getKey()
    {
        return $this->key;
    }

    /**
     * @param mixed $key
     */
    public function setKey($key): void
    {
        $this->key = $key;
    }

    /**
     * @return mixed
     */
    public function getClientCert()
    {
        return $this->clientCert;
    }

    /**
     * @param mixed $clientCert
     */
    public function setClientCert($clientCert): void
    {
        $this->clientCert = $clientCert;
    }

    /**
     * @return mixed
     */
    public function getClientKey()
    {
        return $this->clientKey;
    }

    /**
     * @param mixed $clientKey
     */
    public function setClientKey($clientKey): void
    {
        $this->clientKey = $clientKey;
    }

    /**
     * @return mixed
     */
    public function getNotifyUrl()
    {
        return $this->notifyUrl;
    }

    /**
     * @param mixed $notifyUrl
     */
    public function setNotifyUrl($notifyUrl): void
    {
        $this->notifyUrl = $notifyUrl;
    }

    /**
     * @return mixed
     */
    public function getRefundNotifyUrl()
    {
        return $this->refundNotifyUrl;
    }

    /**
     * @param mixed $refundNotifyUrl
     */
    public function setRefundNotifyUrl($refundNotifyUrl): void
    {
        $this->refundNotifyUrl = $refundNotifyUrl;
    }

    /**
     * 输出xml字符.
     *
     * @param mixed $data
     */
    public function toXml($data)
    {
        if (!\is_array($data)
            || \count($data) <= 0) {
            return '';
        }

        $xml = '<xml>';
        foreach ($data as $key => $val) {
            if (is_numeric($val)) {
                $xml .= '<'.$key.'>'.$val.'</'.$key.'>';
            } else {
                $xml .= '<'.$key.'><![CDATA['.$val.']]></'.$key.'>';
            }
        }
        $xml .= '</xml>';

        return $xml;
    }

    /**
     * 以post方式提交xml到对应的接口url.
     *
     * @param string $xml     需要post的xml数据
     * @param string $url     url
     * @param bool   $useCert 是否需要证书,默认不需要
     * @param int    $second  url执行超时时间,默认30s
     *
     * @throws
     */
    protected function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        // 设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        // 如果有配置代理这里就设置代理
//        if(WxPayConfig::CURL_PROXY_HOST != "0.0.0.0"
//            && WxPayConfig::CURL_PROXY_PORT != 0){
//            curl_setopt($ch,CURLOPT_PROXY, WxPayConfig::CURL_PROXY_HOST);
//            curl_setopt($ch,CURLOPT_PROXYPORT, WxPayConfig::CURL_PROXY_PORT);
//        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 严格校验
        // 设置header
        curl_setopt($ch, CURLOPT_HEADER, false);
        // 要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        if (true == $useCert) {
            // 设置证书
            // 使用证书:cert 与 key 分别属于两个.pem文件
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLCERT, $this->getClientCert());
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            curl_setopt($ch, CURLOPT_SSLKEY, $this->getClientKey());
        }
        // post提交方式
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        // 运行curl
        $data = curl_exec($ch);
        // 返回结果
        if ($data) {
            curl_close($ch);

            return $data;
        }
        $error = curl_errno($ch);
        curl_close($ch);

        throw new \think\Exception("curl出错,错误码:{$error}");
    }

    /**
     * 将xml转为array.
     *
     * @param string $xml
     */
    protected function fromXml($xml)
    {
        if (!$xml) {
            throw new \think\Exception('xml数据异常!');
        }
        // 将XML转为array
        // 禁止引用外部xml实体
        libxml_disable_entity_loader(true);

        return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
    }

    /**
     * 格式化参数格式化成url参数.
     *
     * @param mixed $value
     */
    protected function toUrlParams($value)
    {
        $buff = '';
        foreach ($value as $k => $v) {
            if ('sign' != $k && '' != $v && !\is_array($v)) {
                $buff .= $k.'='.$v.'&';
            }
        }

        return trim($buff, '&');
    }

    protected function makeSign($values)
    {
        // 签名步骤一:按字典序排序参数
        ksort($values);
        $string = $this->toUrlParams($values);

        // 签名步骤二:在string后加入KEY
        $string = $string.'&key='.$this->getKey();

        // 签名步骤三:MD5加密
        $string = md5($string);
        // 签名步骤四:所有字符转为大写
        return strtoupper($string);
    }
}

 

Pay.php

<?php

/*
 *  +----------------------------------------------------------------------
 *  | wangqy
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023 http://upwqy.com All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Author: wangqy <529857614@qq.com>
 *  +----------------------------------------------------------------------
 */

namespace common\service\payments\wxpay\v2;

use app\enum\pay\BillPaymentsEnum;
use think\exception\ValidateException;
use think\helper\Str;

class Pay extends Base
{
    protected $tradeType; // 交易类型
    protected $totalFee; // 支付金额 单位:分
    protected $outTradeNo; // 商户交易单号.
    protected $transactionId; // 微信交易订单号
    protected $body; // 描述.
    protected $openId; // 支付人标识.
    protected $attach; // 附加信息.
    protected $nonceStr; // 随机字符串
    protected $signType = 'MD5'; // 签名类型
    protected $spbillCreateIp; // 终端ip

    /**
     * @return mixed
     */
    public function getTradeType()
    {
        return $this->tradeType;
    }

    /**
     * @param mixed $tradeType
     */
    public function setTradeType($tradeType): void
    {
        $this->tradeType = $tradeType;
    }

    /**
     * @return mixed
     */
    public function getTotalFee()
    {
        return $this->totalFee;
    }

    /**
     * @param mixed $totalFee
     */
    public function setTotalFee($totalFee): void
    {
        $this->totalFee = $totalFee;
    }

    /**
     * @return mixed
     */
    public function getOutTradeNo()
    {
        return $this->outTradeNo;
    }

    /**
     * @param mixed $outTradeNo
     */
    public function setOutTradeNo($outTradeNo): void
    {
        $this->outTradeNo = $outTradeNo;
    }

    /**
     * @return mixed
     */
    public function getTransactionId()
    {
        return $this->transactionId;
    }

    /**
     * @param mixed $transactionId
     */
    public function setTransactionId($transactionId): void
    {
        $this->transactionId = $transactionId;
    }

    /**
     * @return mixed
     */
    public function getBody()
    {
        return $this->body;
    }

    /**
     * @param mixed $body
     */
    public function setBody($body): void
    {
        $this->body = $body;
    }

    /**
     * @return mixed
     */
    public function getOpenId()
    {
        return $this->openId;
    }

    /**
     * @param mixed $openId
     */
    public function setOpenId($openId): void
    {
        $this->openId = $openId;
    }

    /**
     * @return mixed
     */
    public function getAttach()
    {
        return $this->attach;
    }

    /**
     * @param mixed $attach
     */
    public function setAttach($attach): void
    {
        $this->attach = $attach;
    }

    /**
     * @return mixed
     */
    public function getNonceStr()
    {
        return $this->nonceStr;
    }

    /**
     * @param mixed $nonceStr
     */
    public function setNonceStr($nonceStr): void
    {
        $this->nonceStr = $nonceStr;
    }

    public function getSignType(): string
    {
        return $this->signType;
    }

    public function setSignType(string $signType): void
    {
        $this->signType = $signType;
    }

    /**
     * @return mixed
     */
    public function getSpbillCreateIp()
    {
        return $this->spbillCreateIp;
    }

    /**
     * @param mixed $spbillCreateIp
     */
    public function setSpbillCreateIp($spbillCreateIp): void
    {
        $this->spbillCreateIp = $spbillCreateIp;
    }

    /**
     * 支付.
     */
    public function pay()
    {
        $url = ApiUrls::PAY_URL;

        $data['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
        $data['nonce_str'] = Str::random(32);
        $data['sign_type'] = $this->getSignType(); // 签名类型
        $data['mch_id'] = $this->getMchId(); // 商户号
        $data['appid'] = $this->getAppId(); // appid
        $data['trade_type'] = $this->getTradeType(); // 交易类型
        $data['body'] = $this->getBody(); // 描述
        $data['out_trade_no'] = $this->getOutTradeNo(); // 商户订单号
        $data['total_fee'] = $this->getTotalFee() * 100; // 订单总金额,单位为分
        $data['notify_url'] = $this->getNotifyUrl();  // 回调地址

        $openId = $this->getOpenId();
        $attach = $this->getAttach();

        if ($openId) {
            $data['openid'] = $openId; // 用户openid
        }
        if ($attach) {
            $data['attach'] = $attach; // 附加参数
        }

        // 生成签名
        $data['sign'] = $this->makeSign($data);

        $xml = $this->toXml($data);

        $response = $this->postXmlCurl($xml, $url, false, 6);

        $re = $this->fromXml($response);

        if (isset($re['return_code']) && 'FAIL' == $re['return_code']) {
            throw new ValidateException($re['return_msg']);
        }
        if ('SUCCESS' == $re['return_code'] && 'SUCCESS' == $re['result_code']) {
            return $this->buildPayData($re);
        }

        throw new \Exception($re['err_code_des']);
    }

    /**
     * 主动查询订单支付结果.
     *
     * @return array
     */
    public function query()
    {
        $url = ApiUrls::QUERY_URL;

        $data['appid'] = $this->getAppId();
        $data['mch_id'] = $this->getMchId();
        $data['out_trade_no'] = $this->getOutTradeNo();
        $data['nonce_str'] = Str::random(32); // 32位随机数
        $data['sign_type'] = $this->getSignType();
        $data['sign'] = $this->makeSign($data);

        record_log('wxQueryData', ['data' => $data]);

        $xml = $this->toXml($data);

        $response = $this->postXmlCurl($xml, $url, false, 6);

        $re = $this->fromXml($response);

        return $this->checkCallbackData($re);
    }

    /**
     * 异步回调.
     */
    public function callback()
    {
        // 获取通知的数据
        $xml = file_get_contents('php://input');
        $data = $this->fromXml($xml);

        record_log('wxCallbackResult', ['data' => $data]);

        return $this->checkCallbackData($data);
    }

    /**
     * 核对支付结果.
     *
     * @param mixed $data
     */
    public function checkCallbackData($data): array
    {
        $result = [
            'status' => false,
            'data' => [],
        ];

        if ($data && 'SUCCESS' == $data['return_code'] && $data['sign'] == $this->makeSign($data)) {
            // 说明值没问题,并且验证签名通过
            if ('SUCCESS' == $data['result_code']) {
                $result['data']['status'] = BillPaymentsEnum::STATUS_PAYED;  // 1未支付,2支付成功,3其他
                $result['data']['payed_msg'] = $data['result_code'];
                $result['data']['transaction_id'] = $data['transaction_id'] ?? null; // 微信支付订单号
            } else {
                // 如果未支付成功,也更新支付单
                $result['data']['status'] = BillPaymentsEnum::STATUS_OTHER;          // 1未支付,2支付成功,3其他
                $result['data']['payed_msg'] = $data['err_code'].':'.$data['err_code_des'];
                $result['data']['transaction_id'] = null;
            }
            $result['status'] = true;
            $result['msg'] = 'SUCCESS';
            $result['data']['out_trade_no'] = $data['out_trade_no']; // 订单编号
            $result['data']['total_fee'] = $data['total_fee'] / 100;
            $result['data']['attach'] = $data['attach'] ?? null;
            $result['data']['more'] = $data;

            return $result;
        }
        $result['msg'] = 'FAIl';

        return $result;
    }

    /**
     * 构建支付所返回的参数.
     *
     * @param mixed $data
     */
    public function buildPayData($data): array
    {
        $appData = [];

        switch ($data['trade_type']) {
            case 'JSAPI':                   // 微信小程序组建数据
                $time = time();
                $appData['timeStamp'] = "{$time}";
                $appData['nonceStr'] = $data['nonce_str'];
                $appData['package'] = 'prepay_id='.$data['prepay_id'];
                $appData['signType'] = 'MD5';
                $appData['appId'] = $data['appid'];
                $appData['paySign'] = md5(
                    'appId='.$data['appid'].
                    '&nonceStr='.$appData['nonceStr'].
                    '&package='.$appData['package'].
                    '&signType='.$appData['signType'].
                    '&timeStamp='.$appData['timeStamp'].
                    '&key='.$this->getKey()
                );

                break;

            case 'MWEB':                // 微信H5支付
                $appData['mweb_url'] = $data['mweb_url'];

                break;

            case 'NATIVE':               // 扫码支付
                $appData['code_url'] = $data['code_url'];

                break;

            case 'APP':                // 微信APP支付
                $time = time();
                $appData['appid'] = $data['appid'];
                $appData['partnerid'] = $this->getMchId();
                $appData['prepayid'] = $data['prepay_id'];
                $appData['noncestr'] = $data['nonce_str'];
                $appData['packageValue'] = 'Sign=WXPay';
                $appData['timestamp'] = "{$time}";
                $appData['sign'] = $this->makeSign($appData);

                break;
        }

        return $appData;
    }
}

 

 

Refund.php

<?php

/*
 *  +----------------------------------------------------------------------
 *  | wangqy
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023 http://upwqy.com All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Author: wangqy <529857614@qq.com>
 *  +----------------------------------------------------------------------
 */

namespace common\service\payments\wxpay\v2;

use app\enum\pay\BillRefundEnum;
use think\helper\Str;

class Refund extends Base
{
    protected $totalFee; // 支付金额 单位:分
    protected $outTradeNo; // 商户交易单号.
    protected $signType = 'MD5'; // 签名类型

    protected $refundFee; // 退款金额. 单位:分.
    protected $outRefundNo; // 商户退款单号.
    protected $refundId; // 微信退款单号.
    protected $refundDesc; // 退款描述.

    /**
     * @return mixed
     */
    public function getTotalFee()
    {
        return $this->totalFee;
    }

    /**
     * @param mixed $totalFee
     */
    public function setTotalFee($totalFee): void
    {
        $this->totalFee = $totalFee;
    }

    /**
     * @return mixed
     */
    public function getOutTradeNo()
    {
        return $this->outTradeNo;
    }

    /**
     * @param mixed $outTradeNo
     */
    public function setOutTradeNo($outTradeNo): void
    {
        $this->outTradeNo = $outTradeNo;
    }

    public function getSignType(): string
    {
        return $this->signType;
    }

    public function setSignType(string $signType): void
    {
        $this->signType = $signType;
    }

    /**
     * @return mixed
     */
    public function getRefundFee()
    {
        return $this->refundFee;
    }

    /**
     * @param mixed $refundFee
     */
    public function setRefundFee($refundFee): void
    {
        $this->refundFee = $refundFee;
    }

    /**
     * @return mixed
     */
    public function getOutRefundNo()
    {
        return $this->outRefundNo;
    }

    /**
     * @param mixed $outRefundNo
     */
    public function setOutRefundNo($outRefundNo): void
    {
        $this->outRefundNo = $outRefundNo;
    }

    /**
     * @return mixed
     */
    public function getRefundId()
    {
        return $this->refundId;
    }

    /**
     * @param mixed $refundId
     */
    public function setRefundId($refundId): void
    {
        $this->refundId = $refundId;
    }

    /**
     * @return mixed
     */
    public function getRefundDesc()
    {
        return $this->refundDesc;
    }

    /**
     * @param mixed $refundDesc
     */
    public function setRefundDesc($refundDesc): void
    {
        $this->refundDesc = $refundDesc;
    }

    /**
     * 退款.
     *
     * @return mixed
     *
     * @throws \Exception
     */
    public function refund()
    {
        $url = ApiUrls::REFUND_URL;

        $data['appid'] = $this->getAppId();
        $data['mch_id'] = $this->getMchId();
        $data['nonce_str'] = Str::random(32);
        $data['sign_type'] = $this->getSignType();
        $data['out_trade_no'] = $this->getOutTradeNo();
        $data['total_fee'] = $this->getTotalFee() * 100;
        $data['out_refund_no'] = $this->getOutRefundNo();
        $data['refund_fee'] = $this->getRefundFee() * 100;
        $data['refund_desc'] = $this->getRefundDesc();
        $data['notify_url'] = $this->getRefundNotifyUrl();
        $data['sign'] = $this->makeSign($data);

        record_log('refundData', ['data' => $data]);

        $xml = $this->toXml($data);

        $response = $this->postXmlCurl($xml, $url, true, 6);

        $re = $this->fromXml($response);

        record_log('wxRefundApplyResult', ['result' => $re]);

        if (isset($re['return_code']) && 'FAIL' == $re['return_code']) {
            throw new \Exception($re['return_msg']);
        }
        if ('SUCCESS' == $re['return_code'] && 'SUCCESS' == $re['result_code']) {
            return $re;
        }

        throw new \Exception($re['err_code_des']);
    }

    /**
     * 退款查询.
     */
    public function refundQuery()
    {
    }

    /**
     * 退款回调.
     */
    public function refundCallback()
    {
        $xml = file_get_contents('php://input');
        $data = $this->fromXml($xml);

        record_log('refundCallback', ['data' => $data]);

        $result = [
            'status' => false,
            'data' => [],
        ];

        if ('SUCCESS' == $data['return_code']) {
            $decryptData = $this->refundDataDecrypt($data['req_info']);
            $decryptData = $this->fromXml($decryptData);
            $data['decrypt_data'] = $decryptData;
            record_log('refundCallback', ['decrypt_data' => $decryptData]);

            if ('SUCCESS' == $decryptData['refund_status']) {
                $result['data']['third_refund_id'] = $decryptData['refund_id'];
                $result['data']['third_party_status'] = BillRefundEnum::THIRD_REFUND_STATUS_REFUNDED; // 退款成功
                $result['data']['refund_status'] = BillRefundEnum::REFUND_STATUS_REFUNDED; // 退款成功
                $result['data']['memo'] = null;
            } else {
                $result['data']['third_refund_id'] = null;

                $result['data']['third_party_status'] = BillRefundEnum::THIRD_REFUND_STATUS_REFUND_FAILURE; // 退款失败
                $result['data']['refund_status'] = BillRefundEnum::REFUND_STATUS_REFUND_FAILURE; // 退款失败
                $result['data']['memo'] = '退款失败:'.$decryptData['refund_status'];
            }

            $result['status'] = true;
            $result['msg'] = 'SUCCESS';
            $result['data']['refund_no'] = $decryptData['out_refund_no'];
            $result['data']['more'] = $data;

            return $result;
        }
        $result['msg'] = 'SUCCESS';

        return $result;
    }

    /**
     * 退款回调数据解密.
     *
     * @param mixed $encryptData
     */
    public function refundDataDecrypt($encryptData)
    {
        $key = strtolower(md5($this->getKey()));
        $str = base64_decode($encryptData);

        return openssl_decrypt($str, 'aes-256-ecb', $key, OPENSSL_RAW_DATA);
    }
}

 

Transfers.php

<?php

namespace common\service\payments\wxpay\v2;

use think\helper\Str;

class Transfers extends Base
{
    protected $partnerTradeNo;
    protected $openId;
    protected $amount;

    /**
     * @return mixed
     */
    public function getPartnerTradeNo()
    {
        return $this->partnerTradeNo;
    }

    /**
     * @param mixed $partnerTradeNo
     */
    public function setPartnerTradeNo($partnerTradeNo): void
    {
        $this->partnerTradeNo = $partnerTradeNo;
    }

    /**
     * @return mixed
     */
    public function getOpenId()
    {
        return $this->openId;
    }

    /**
     * @param mixed $openId
     */
    public function setOpenId($openId): void
    {
        $this->openId = $openId;
    }

    /**
     * @return mixed
     */
    public function getAmount()
    {
        return $this->amount;
    }

    /**
     * @param mixed $amount
     */
    public function setAmount($amount): void
    {
        $this->amount = $amount;
    }





    /**
     * 转到个人零钱.
     *
     * @return bool
     *
     * @throws \think\Exception
     */
    public function transfers()
    {
        $url = ApiUrls::TRANSFERS;

        $data['mch_appid'] = $this->getAppId();
        $data['mchid'] = $this->getMchId();
        $data['nonce_str'] = Str::random(32);
        $data['partner_trade_no'] = $this->getPartnerTradeNo();
        $data['openid'] = $this->getOpenId();
        $data['check_name'] = 'NO_CHECK';
//        $data['re_user_name'] = $this->getReUserName();
        $data['amount'] = $this->getAmount() * 100;
        $data['desc'] = '提现到零钱';

        $data['sign'] = $this->makeSign($data);

        record_log('wxTransfersData', ['data' => $data]);

        $xml = $this->toXml($data);

        $response = $this->postXmlCurl($xml, $url, true, 6);

        $re = $this->fromXml($response);

        record_log('wxTransfersData', ['res' => $re]);

        if (isset($re['return_code']) && 'FAIL' == $re['return_code']) {
            throw new \Exception($re['return_msg']);
        }

        if ('SUCCESS' == $re['return_code'] && 'SUCCESS' == $re['result_code']) {
            return $re;
        }

        throw new \Exception($re['err_code_des']);
    }
}

 

 

业务层

 

WxPayV2Logic.php

 

<?php

/*
 *  +----------------------------------------------------------------------
 *  | wangqy
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023 http://upwqy.com All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Author: wangqy <529857614@qq.com>
 *  +----------------------------------------------------------------------
 */

namespace app\logic\pay;

use common\basic\BaseLogic;
use common\service\payments\wxpay\v2\Pay;
use common\service\payments\wxpay\v2\Refund;
use common\service\payments\wxpay\v2\Transfers;
use think\exception\ValidateException;

class WxPayV2Logic extends BaseLogic
{
    public const TYPE_WEB = 'web'; // 公众号
    public const TYPE_OPEN = 'open'; // 开放平台
    public const TYPE_APPLET = 'applet'; // 小程序

    protected $appId;
    protected $appSecret;
    protected $mchId;
    protected $key;
    protected $notifyUrl;
    protected $refundNotifyUrl;

    protected $clientCert;
    protected $clientKey;

    protected $attach;
    protected $tradeType;
    protected $openId;
    protected $type;

    public function __construct()
    {
        $this->mchId = config('site.wx_mch_id');
        $this->key = config('site.wx_mch_key');

        // 支付回调地址
        $this->notifyUrl = config('site.wx_notify_url');

        // 退款回调地址
        $this->refundNotifyUrl = config('site.wx_refund_notify_url');

        $this->getClientCert();
        $this->getClientKey();
    }

    public function getClientCert()
    {
        $remoteClientCert = config('site.wx_cert_path');
        $localUrl = app()->getRootPath().'extend/cert/wx/apiclient_cert.pem';

        if (!is_file($localUrl)) {
            download_file($remoteClientCert, $localUrl);
        }

        $this->clientCert = $localUrl;
    }

    public function getClientKey()
    {
        $remoteClientKey = config('site.wx_key_path');

        $localUrl = app()->getRootPath().'extend/cert/wx/apiclient_key.pem';

        if (!is_file($localUrl)) {
            download_file($remoteClientKey, $localUrl);
        }

        $this->clientKey = $localUrl;
    }

    public function getApp()
    {
        switch ($this->type) {
            // 小程序
            case self::TYPE_APPLET:
                $appId = config('site.wx_applet_app_id');
                $appSecret = config('site.wx_applet_app_secret');

                break;

            default:
                throw new ValidateException('类型不支持');
        }

        $this->appId = $appId;
        $this->appSecret = $appSecret;
    }

    /**
     * 微信小程序支付.
     *
     * @param mixed $body
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     */
    protected function pay($outTradeNo, $totalFee, $body)
    {
        $this->getApp();

        $pay = new Pay();
        $pay->setAppId($this->appId);
        $pay->setAppSecret($this->appSecret);
        $pay->setMchId($this->mchId);
        $pay->setKey($this->key);
        $pay->setNotifyUrl($this->notifyUrl);

        $pay->setTradeType($this->tradeType);
        $pay->setOutTradeNo($outTradeNo);
        $pay->setTotalFee($totalFee);
        $pay->setBody($body);
        if ($this->openId) {
            $pay->setOpenId($this->openId);
        }

        if ($this->attach) {
            $pay->setAttach($this->attach);
        }

        return $pay->pay();
    }

    /**
     * 微信公众号支付.
     *
     * @param mixed $openId
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $body
     */
    public function webPay($openId, $outTradeNo, $totalFee, $body = '消费')
    {
        $this->type = self::TYPE_WEB;
        $this->tradeType = 'JSAPI';
        $this->openId = $openId;

        return $this->pay($outTradeNo, $totalFee, $body);
    }

    /**
     * 小程序支付.
     *
     * @param mixed $openId
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $body
     */
    public function appletPay($openId, $outTradeNo, $totalFee, $body = '消费')
    {
        $this->type = self::TYPE_APPLET;
        $this->tradeType = 'JSAPI';
        $this->openId = $openId;

        return $this->pay($outTradeNo, $totalFee, $body);
    }

    /**
     * 微信h5支付.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $body
     */
    public function h5Pay($outTradeNo, $totalFee, $body = '消费')
    {
        $this->type = self::TYPE_WEB;
        $this->tradeType = 'MWEB';

        return $this->pay($outTradeNo, $totalFee, $body);
    }

    /**
     * app 微信支付.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $body
     */
    public function appPay($outTradeNo, $totalFee, $body = '消费')
    {
        $this->type = self::TYPE_OPEN;
        $this->tradeType = 'APP';

        return $this->pay($outTradeNo, $totalFee, $body);
    }

    /**
     * 校验回调参数 并拿到回调参数.
     */
    public function callback()
    {
        $pay = new Pay();
        $pay->setKey($this->key);

        return $pay->callback();
    }

    /**
     *主动查询订单支付结果.
     *
     * @param mixed $outTradeNo
     */
    public function query($outTradeNo)
    {
        $this->getApp();

        $pay = new Pay();
        $pay->setAppId($this->appId);
        $pay->setMchId($this->mchId);
        $pay->setKey($this->key);
        $pay->setOutTradeNo($outTradeNo);

        return $pay->query();
    }

    /**
     * 公众号支付单查询.
     *
     * @param mixed $outTradeNo
     */
    public function webQuery($outTradeNo)
    {
        $this->type = self::TYPE_WEB;

        return $this->query($outTradeNo);
    }

    /**
     * 小程序支付单查询.
     *
     * @param mixed $outTradeNo
     */
    public function appletQuery($outTradeNo)
    {
        $this->type = self::TYPE_APPLET;

        return $this->query($outTradeNo);
    }

    /**
     * app微信支付单查询.
     *
     * @param mixed $outTradeNo
     */
    public function appQuery($outTradeNo)
    {
        $this->type = self::TYPE_OPEN;

        return $this->query($outTradeNo);
    }

    /**
     * h5支付单查询.
     *
     * @param mixed $outTradeNo
     */
    public function h5Query($outTradeNo)
    {
        $this->type = self::TYPE_WEB;

        return $this->query($outTradeNo);
    }

    /**
     * 微信小程序 退款.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $outRefundNo
     * @param mixed $refundFee
     * @param mixed $refundDesc
     */
    public function refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc = '取消订单')
    {
        $this->getApp();

        $refund = new Refund();
        $refund->setAppId($this->appId);
        $refund->setMchId($this->mchId);
        $refund->setKey($this->key);
        $refund->setRefundNotifyUrl($this->refundNotifyUrl);
        $refund->setClientKey($this->clientKey);
        $refund->setClientCert($this->clientCert);

        $refund->setOutTradeNo($outTradeNo);
        $refund->setTotalFee($totalFee);
        $refund->setOutRefundNo($outRefundNo);
        $refund->setRefundFee($refundFee);
        $refund->setRefundDesc($refundDesc);

        return $refund->refund();
    }

    /**
     * app退款.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $outRefundNo
     * @param mixed $refundFee
     * @param mixed $refundDesc
     */
    public function appRefund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc = '取消订单')
    {
        $this->type = self::TYPE_OPEN;

        return $this->refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc);
    }

    /**
     * 公众号退款.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $outRefundNo
     * @param mixed $refundFee
     * @param mixed $refundDesc
     */
    public function webRefund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc = '取消订单')
    {
        $this->type = self::TYPE_WEB;

        return $this->refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc);
    }

    /**
     * h5退款.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $outRefundNo
     * @param mixed $refundFee
     * @param mixed $refundDesc
     */
    public function h5Refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc = '取消订单')
    {
        $this->type = self::TYPE_WEB;

        return $this->refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc);
    }

    /**
     * 小程序退款.
     *
     * @param mixed $outTradeNo
     * @param mixed $totalFee
     * @param mixed $outRefundNo
     * @param mixed $refundFee
     * @param mixed $refundDesc
     */
    public function appletRefund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc = '取消订单')
    {
        $this->type = self::TYPE_APPLET;

        return $this->refund($outTradeNo, $totalFee, $outRefundNo, $refundFee, $refundDesc);
    }

    public function refundCallback()
    {
        $refund = new Refund();
        $refund->setKey($this->key);

        return $refund->refundCallback();
    }

    public function h5RefundQuery()
    {
    }

    public function webRefundQuery()
    {
    }

    public function appRefundQuery()
    {
    }

    public function appletRefundQuery()
    {
    }

    /**
     * 小程序端申请退款.
     *
     * @param mixed $openId
     * @param mixed $cashNo
     * @param mixed $amount
     */
    public function appletTransfers($openId, $cashNo, $amount)
    {
        $this->type = self::TYPE_APPLET;
        $this->openId = $openId;

        return $this->transfers($cashNo, $amount);
    }

    /**
     * 提现.
     *
     * @param mixed $cashNo
     * @param mixed $amount
     */
    public function transfers($cashNo, $amount)
    {
        $this->getApp();

        $transfers = new Transfers();
        $transfers->setAppId($this->appId);
        $transfers->setMchId($this->mchId);
        $transfers->setKey($this->key);
        $transfers->setPartnerTradeNo($cashNo);
        $transfers->setAmount($amount);
        $transfers->setOpenId($this->openId);
        $transfers->setClientCert($this->clientCert);
        $transfers->setClientKey($this->clientKey);

        return $transfers->transfers();
    }
}
发布时间 : 2023-02-28,阅读量:821 , 分类: 微信支付
本文链接:https://upwqy.com/details/255.html
SEO优化策略 企业微信三种token