龚哥哥 爱生活、做自己!
微信支付接口,服务器端处理(新版)
发表于 2016-4-12 | PHP
<?php

/**
 * 微信支付驱动
 * @author  Devil
 * @version V_1.0.0
 */
class WeiXinPay
{
    private $appid;
    private $secret;
    private $mch_id;
    private $key;

    /**
     * [__construct 构造方法]
     */
    private function __construct($config)
    {
        $this->appid = isset($config['appid']) ? $config['appid'] : '';
        $this->secret = isset($config['secret']) ? $config['secret'] : '';
        $this->mch_id = isset($config['mchid']) ? $config['mchid'] : '';
        $this->key = isset($config['key']) ? $config['key'] : '';
    }

    /**
     * [Instantiate 静态方法]
     * @param [array] $config   [微信配置信息]
     * @return[object]          [当前类对象]
     */
    public static function Instantiate($config)
    {
        $object = null;
        if(!is_object($object)) $object = new self($config);
        return $object;
    }

    /**
     * [WechatPay 微信支付]
     * @param [string]  $param['body']              [商品简要描述]
     * @param [string]  $param['out_trade_no']      [商户订单号]
     * @param [int]     $param['total_fee']         [订单总金额]
     * @param [string]  $param['notify_url']        [异步通知地址]
     * @param [string]  $param['trade_type']        [交易类型(默认JSAPI)JSAPI | APP]
     * @param [string]  $param['openid']            [openid]
     * @param [string]  $param['attach']            [原样返回的数据(可选)]
     * @return[array]                                  [微信支付数据]
     */
    public function WechatPay($param)
    {
        if(empty($param)) return '';

        $data = $this->GetPayParam($param);

        $xml = '<xml>
                <appid>'.$this->appid.'</appid>
                <body>'.$data['data']['body'].'</body>
                <mch_id>'.$this->mch_id.'</mch_id>
                <nonce_str>'.$data['data']['nonce_str'].'</nonce_str>
                <notify_url>'.$data['data']['notify_url'].'</notify_url>
                <openid>'.$data['data']['openid'].'</openid>
                <out_trade_no>'.$data['data']['out_trade_no'].'</out_trade_no>
                <spbill_create_ip>'.$data['data']['spbill_create_ip'].'</spbill_create_ip>
                <total_fee>'.$data['data']['total_fee'].'</total_fee>
                <trade_type>'.$data['data']['trade_type'].'</trade_type>
                <attach>'.$data['data']['attach'].'</attach>
                <sign>'.$data['sign'].'</sign>
            </xml>';

        $result = $this->Xml_Array($this->Curl_Post('https://api.mch.weixin.qq.com/pay/unifiedorder', $xml));
        if(!empty($result['prepay_id']))
        {
            // 返回数据
            $pay_data = array(
                    'appid'         =>  $this->appid,
                    'partnerid'     =>  $this->mch_id,
                    'prepayid'      =>  $result['prepay_id'],
                    'package'       =>  'Sign=WXPay',
                    'noncestr'      =>  md5(time().rand()),
                    'timestamp'     =>  time(),
                );
            $pay_data['sign'] = $this->GetParamSing($pay_data);
            return $pay_data;
        }
        return '';
    }

    /**
     * [Refund 退款接口]
     * @param   [array] $param  [退款的参数]
     * @return  [boolean]       [成功true, 则false]
     */
    public function Refund($param)
    {
        if(empty($param)) return false;

        $data = array(
                'appid'         =>  $this->appid,
                'mch_id'        =>  $this->mch_id,
                'nonce_str'     =>  md5(time().rand()),
                'transaction_id'=>  $param['transaction_id'],
                'out_refund_no' =>  md5($param['transaction_id'].$param['total_fee']),
                'total_fee'     =>  $param['total_fee'],
                'refund_fee'    =>  $param['refund_fee'],
                'op_user_id'    =>  $this->mch_id,
            );
        $data['sign'] = $this->GetParamSing($data);

        $result = $this->Xml_Array($this->Curl_Post('https://api.mch.weixin.qq.com/secapi/pay/refund', $this->GetParamXml($data), true));
        return (!empty($result['result_code']) && $result['result_code'] == 'SUCCESS' && !empty($result['return_msg']) && $result['return_msg'] == 'OK');
    }

    /**
     * [GetParamXml xml键值对拼接]
     * @param [array] $param [需要拼接xml的数组]
     * @return[string]       [xml数据]
     */
    private function GetParamXml($param)
    {
        if(empty($param) || !is_array($param)) return '';

        $xml = '';
        foreach($param as $k=>$v)
        {
            $xml .= '<'.$k.'>'.$v.'</'.$k.'>';
        }
        return '<xml>'.$xml.'</xml>';
    }

    /**
     * [Xml_Array xml转数组]
     * @param [string] $xml [xml字符串]
     * @return[array]       [数组]
     */
    private function Xml_Array($xml)
    {
        if(!Xml_Parser($xml)) return '';

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

    /**
     * [GetPayParam 获取支付参数]
     * @param [array] $param [支付的数据]
     * @return[array] [支付的字符串和签名]
     */
    private function GetPayParam($param)
    {
        if(empty($param)) return '';

        $param['appid'] = $this->appid;
        $param['mch_id'] = $this->mch_id;
        $param['nonce_str'] = md5(time().rand().$param['out_trade_no']);
        $param['spbill_create_ip'] = get_client_ip();
        $param['trade_type'] = empty($param['trade_type']) ? 'JSAPI' : $param['trade_type'];
        $param['attach'] = empty($param['attach']) ? 'gongfuxiang' : $param['attach'];
        return array(
            'sign'  =>  $this->GetParamSing($param),
            'data'  =>  $param,
        );
    }

    /**
     * [GetParamSing 签名生成]
     * @param [array] $param    [需要参与签名的数据]
     * @return[string]          [签名]
     */
    private function GetParamSing($param)
    {
        if(empty($param)) return '';

        ksort($param);
        $sign  = '';
        foreach($param as $k=>$v)
        {
            if($k != 'sign') $sign .= "$k=$v&";
        }
        return strtoupper(md5($sign.'key='.$this->key));
    }

    /**
     * [Curl_Post curl模拟post]
     * @param  [string] $url        [请求地址]
     * @param  [array] $post        [发送的post数据]
     * @param  [boolean] $use_cert  [是否需要使用证书]
     */
    private function Curl_Post($url, $post, $use_cert = false)
    {
        $options = array(
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER         => false,
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $post,
        );

        if($use_cert == true)
        {
            //设置证书
            //使用证书:cert 与 key 分别属于两个.pem文件
            $options[CURLOPT_SSLCERTTYPE] = 'PEM';
            $options[CURLOPT_SSLCERT] = WEB_ROOT.'cert/wechat_app_apiclient_cert.pem';
            $options[CURLOPT_SSLKEYTYPE] = 'PEM';
            $options[CURLOPT_SSLKEY] = WEB_ROOT.'cert/wechat_app_apiclient_key.pem';
        }
 
        $ch = curl_init($url);
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    /**
     * [Notify 异步回调]
     * @return [array] [支付数据]
     */
    public function Notify()
    {
        $result = empty($GLOBALS['HTTP_RAW_POST_DATA']) ? '' : $this->Xml_Array($GLOBALS['HTTP_RAW_POST_DATA']);

        if(isset($result['sign']) && $result['sign'] == $this->GetParamSing($result)) return $result;
        return '';
    }
}
?>

发表评论:

TOP