小程序支付

<?php

/**
 * Created by PhpStorm.
 * User: zb
 * Date: 17-10-9
 * Time: 下午7:15
 */
class PaysAction extends CommonAction
{
    public function _initialize(){
        parent::_initialize();
    }

    public function pay(){
        $openid = trim($_POST['openid']);
        $total_price = (float)$_POST['total_price'] * 100;//~~~~
        $coupon_id = (int)$_POST['coupon_id'];
        $coupon_price = (float)$_POST['coupon_price'] * 100;
        $order_remark = trim($_POST['order_remark']);
        $old_order_num = trim($_POST['order_num']);//有order_num,再来一单购买
        if ($old_order_num == 'undefined'){
            $old_order_num = '';
        }
        $product_id = (int)$_POST['product_id'];//有product_id,立即购买~~~~NaN,undefined在int转化后都为0;


        if (!$openid){
            $this->json->setErr(10000,'缺少参数');
            $this->json->Send();
        }
        $user = M('user');
        $tel_flag = $user->where(array('openid'=>$openid))->find();
        if (!$tel_flag || !$tel_flag['id']){
            $this->json->setErr(10001,'未绑定手机号');
            $this->json->Send();
        }
        $uid = $tel_flag['id'];
        if ($total_price <= 0){
            $this->json->setErr(10002,'支付金额不可为0或负数');
            $this->json->Send();
        }
        $order_info = $this->makeorder($old_order_num,$product_id,$openid,$uid,$total_price,$coupon_id,$coupon_price,$order_remark);
        $order_num = $order_info['order_num'];
        $products_name = $order_info['products_name'];


        $unifiedorder = $this->unifiedorder($openid,$order_num,$total_price,$products_name);
        $data = [
            'appId'     => C('APPID'),
            'timeStamp' => time(),
            'nonceStr'  => $this->createNonceStr(),
            'package'   => 'prepay_prepay_id'],
            'signType'  => 'MD5'
        ];

        $sign = $this->MakeSign($data);
        $data['sign'] = $sign;
        $this->json->setAttr('data',$data);
        $this->json->Send();
    }


    /***    生成订单    优惠券处理
     * @param $openid
     * @param $uid
     * @param $total_price
     * @param $coupon_id user_coupon中的id字段,而非coupon_id字段
     * @param $coupon_price 用户实际获取优惠额度,而非一定是表格中的price字段;
     * @param $order_remark 用户提交订单的备注信息;
     * @param $old_order_num 如有此参数,再来一单购买
     * @param $product_id 立即购买方式
     * @param order表格中的status字段     1未支付;2已支付;3已申请退款;4已退款;5已完成
     * @return string
     */
    private function makeorder($old_order_num='',$product_id=0,$openid,$uid,$total_price,$coupon_id,$coupon_price,$order_remark){
        $order = M('order');
        $now = time();
        if ($old_order_num && !$product_id){
            $order_num = 'os'.$uid.substr($now,3).rand(1000,9999);  //生成12位以上订单号~~~~如果开头有os,表示从order_shoppingcar转变而来
            $shoppingcar = M('order_shoppingcar');//~~~~
        }elseif($product_id && !$old_order_num){
            $order_num = 'qs'.$uid.substr($now,3).rand(1000,9999);  //生成12位以上订单号~~~~如果开头有qs,表示从quickbuy_shoppingcar转变而来
            $shoppingcar = M('quickbuy_shoppingcar');//~~~~
        }else{
            $order_num = $uid.substr($now,3).rand(1000,9999);       //生成12位以上订单号
            $shoppingcar = M('shoppingcar');//~~~~
        }

        $order_add_data = [
            'order_num'     =>  $order_num,
            'uid'           =>  $uid,
            'coupon_id'     =>  $coupon_id,
            'coupon_price'  =>  $coupon_price,
            'total_price'   =>  $total_price,//订单价格
            'status'        =>  1,
            'addtime'       =>  $now,//订单生成时间
            'remark'        =>  $order_remark
        ];
        $order_add_flag = $order->add($order_add_data);
//        $sql = $order->getLastSql();
//        $this->json->setAttr('sql',$sql);
//        $this->json->Send();


        if (!$order_add_flag){
            $this->json->setErr(10003,'生成订单失败');
            $this->json->Send();
        }
        $return_data['order_num'] = $order_num;
        $map = array(
            'openid'    =>  $openid,
            'pnum'      =>  array('gt',0)
        );

        $shoppingcar_flag = $shoppingcar->where($map)->field('pid,pnum')->order('updatetime desc')->select();
        $shoppingcar_count = count($shoppingcar_flag);
        $product = M('product');
        $order_product = M('order_product');
        $products_name = '';
        for ($i=0;$i<$shoppingcar_count;$i++){
            $product_flag = $product->where(array('id'=>$shoppingcar_flag[$i]['pid']))->find();
            //如果该产品已下架或删除,购物车数据中该产品要剔除
            if ($product_flag['status'] == '0' || ($product_flag['is_del'] == '1')){
                unset($shoppingcar_flag[$i]);
                continue;
            }
            if (!$products_name){
                $products_name .= $product_flag['title'];
            }else{
                $products_name .= '-'.$product_flag['title'];
            }
            $product_add_data = [
                'order_id'  =>  $order_add_flag,
                'uid'       =>  $uid,
                'pid'       =>  $shoppingcar_flag[$i]['pid'],
                'pnum'      =>  $shoppingcar_flag[$i]['pnum'],
                'p_name'    =>  $product_flag['title'],
                'p_price'   =>  $product_flag['price'],
                'addtime'   =>  $now
            ];
            $product_add_flag = $order_product->add($product_add_data);
            if (!$product_add_flag){
                $this->json->setErr(10004,'订单产品数据添加失败');
                $this->json->Send();
            }
        }
        $return_data['products_name'] = $products_name;
        return $return_data;
    }

    private function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    public function unifiedorder($openid,$order_num,$total_fee,$products_name){
        $trade_no = $order_num;
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $data = [
            'appid'             => C('APPID'),
            'mch_id'            => C('MCHID'),
            'nonce_str'         => $this->createNonceStr(),
            'sign_type'         => 'MD5',
            'body'              =>  $products_name,  //商品名称组合
            'attach'            => '****-附加数据',
            //'detail'            => '*****线上销售',
            'out_trade_no'      => $trade_no,               //订单号
            'fee_type'          => 'CNY',
            'total_fee'         => $total_fee,       //$total_fee,   for test~~~
            'spbill_create_ip'  => $_SERVER['REMOTE_ADDR'],
//            'time_start'        => $this->getMillisecond(),
            'goods_tag'         => '******-商品标记',
            'notify_url'        => 'https://**************/api.php/Pays/order_notice',
            'trade_type'        => 'JSAPI',
            'openid'            => $openid
        ];
       $sign = $this->MakeSign($data);
       $data['sign'] = $sign;

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

       vendor('Func.Http');
       $result = $this->FromXml(Http::postXmlCurl($url,$xml));
        return $result;
    }


    public function FromXml($xml)
    {
        if(!$xml){
            throw new WxPayException("xml数据异常!");
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $this->values;
    }

    private  function getMillisecond()
    {
        //获取毫秒的时间戳
        $time = explode ( " ", microtime () );
        $time = $time[1] . ($time[0] * 1000);
        $time2 = explode( ".", $time );
        $time = $time2[0];
        return $time;
    }

    public function getSign(){
        $appid = C('APPID');
        $nocestr = $this->createNonceStr();
        $timeStamp = time();


    }

    public function ToXml($array)
    {

        if(!is_array($array)
            || count($array) <= 0)
        {
           return ;
        }

        $xml = '<xml version="1.0">';
        foreach ($array as $key=>$val)
        {

            if (is_numeric($val)){
              $xml.="<".$key.">".$val."</".$key.">";
            }else{
                $xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
            }
        }
        $xml.="</xml>";
        return $xml;
    }

    private function MakeSign($data)
    {
        //签名步骤一:按字典序排序参数
        ksort($data);

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

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

        return $result;

    }

    private function ToUrlParams($array)
    {
        $buff = "";
        foreach ($array as $k => $v)
        {
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    //微信支付回调
    public function order_notice(){
        $xml = $GLOBALS['HTTP_RAW_POST_DATA'];

// 这句file_put_contents是用来查看服务器返回的XML数据 测试完可以删除了~~for test
//        vendor('Func.Func');
//        $hostname = 'http://'.Func::getHostName();
//        $path = $hostname.'/static/log2.txt';
//        file_put_contents($path,$xml);

        //将服务器返回的XML数据转化为数组
        //$data = self::xml2array($xml);
        $data = $this->FromXml($xml);

        // 保存微信服务器返回的签名sign
        $data_sign = $data['sign'];
        // sign不参与签名算法
        unset($data['sign']);
        //$sign = self::makeSign($data);
        $sign = $this->makeSign($data);

        // 判断签名是否正确  判断支付状态
        if ( ($sign===$data_sign) && ($data['return_code']=='SUCCESS') && ($data['result_code']=='SUCCESS') ) {
            $result = $data;
            //获取服务器返回的数据
            $order_num = $data['out_trade_no'];         //订单单号
            $openid = $data['openid'];                  //付款人openID
            $total_fee = $data['total_fee'];            //付款金额
            $transaction_id = $data['transaction_id'];  //微信支付流水号

            $user = M('user');
            $user_flag = $user->where(array('openid'=>$openid))->field('id')->find();
            $uid = $user_flag['id'];

            $save_data = array(
                'total_payed_price' =>  $total_fee,     //实际到帐金额
                'transaction_id'    =>  $transaction_id,
                'paytime'           =>  time(),
                'status'            =>  2       //1未支付;2已支付;3已申请退款;4已退款;5已完成
            );

            $order = M('order');
            $order_save_flag = $order->where(array('order_num'=>$order_num,'uid'=>$uid))->save($save_data);


            //优惠券信息处理~~~~~发起订单还未必就支付~~~~~支付成功才处理
            $order_coupon_flag = $order->where(array('order_num'=>$order_num,'uid'=>$uid))->field('coupon_id,id')->find();
            $coupon_id = $order_coupon_flag['coupon_id'];
            if ($coupon_id){
                $user_coupon = M('user_coupon');
                $coupon_save_data = array(
                    'is_use'       =>  1,
                    'oid'          =>  $order_coupon_flag['id'],        //~~~~order订单的id
                    'update_time'  =>  time()
                );

                $user_coupon_save_flag = $user_coupon->where(array('id'=>$coupon_id))->save($coupon_save_data);
                $user_coupon_find_flag = $user_coupon->where(array('id'=>$coupon_id))->field('coupon_id')->find();
                if ($user_coupon_save_flag){
                    $coupon = M('coupon');
                    $coupon->where(array('id'=>$user_coupon_find_flag['coupon_id']))->setInc('used_number','+1');
                }
            }
            //购物车信息清理
            // 1.订单号数字开头,立即购买方式,清空shoppingcar;
            // 2.订单号开头有sn,再来一单购买方式,清空order_shoppingcar(进入我的订单会清理),一定不要清理了shoppingcar
            // 2.订单号开头有qn,立即购买方式,清空quickbuy_shoppingcar(立即购买触发时间前会清理),一定不要清理了shoppingcar
            if (preg_match('/^(os)/',$order_num)){
                $shoppingcar = M('order_shoppingcar');
            }elseif(preg_match('/^(qs)/',$order_num)){
                $shoppingcar = M('quickbuy_shoppingcar');
            }else{
                $shoppingcar = M('shoppingcar');
            }
            $clear_data['pnum'] = 0;
            $shoppingcar->where(array('openid'=>$openid))->save($clear_data);
        }else{
            $result = false;
        }
        // 返回状态给微信服务器
        if ($result) {
            $str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
        }else{
            $str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
        }
        echo $str;
        return $result;
    }


}
go_to_pay: function (e) {
var that = this;
var coupon_id = parseInt(e.currentTarget.dataset.coupon_id);//user_coupon的id
var coupon_price = e.currentTarget.dataset.coupon_price;//实际优惠价格
var total_price = e.currentTarget.dataset.total_price;
var products_name = e.currentTarget.dataset.products_name;
var order_num = e.currentTarget.dataset.order_num;//可以'',undefined
var product_id = parseInt(e.currentTarget.dataset.product_id);//undefined转化int->NaN
var input_order_remark = that.data.input_order_remark;
wx.request({
url: 'https://**************.net/api.php/Pays/pay',
header: { 'content-type': 'application/x-www-form-urlencoded' },
data: {
openid: app.globalData.open_id,
total_price: total_price,
order_num: order_num,
product_id: product_id,
coupon_id: coupon_id,
coupon_price: coupon_price,
order_remark: input_order_remark,
},
method: 'POST', //注意header
success: function (res) {
if (res.errno) {
wx.showToast(res.errdesc);
return;
}
var datas = res.data;
//console.log(typeof datas.data.timeStamp);
wx.requestPayment({
'timeStamp': datas.data.timeStamp.toString(),
'nonceStr': datas.data.nonceStr,
'package': datas.data.package,
'signType': 'MD5',
'paySign': datas.data.sign,
'success': function (res) {
console.log('支付成功');
console.log(res);
//return;
},
'fail': function (res) {
console.log('支付失败');
//console.log(res);
return;
},
'complete': function (res) {
console.log('支付完成');
console.log(res);
if (res.errMsg == 'requestPayment:ok') {
wx.navigateTo({
url: '/pages/pay/pay_success?products_name=' + products_name
})
}
return;
}
});
// console.log(res.data);
}
})
},