<?php

/**
 * User: Eden
 * Date: 2019/3/21
 * 共有内容
 */
namespace Common\Service;
use Think\Exception;
use Vendor\Func\Http;

class WxPayService extends CommonService {
    public static function unifiedOrder($openid,$order_num,$total_fee,$products_name,$notify_url = ''){
        $trade_no = $order_num;
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $data = [
            'appid'             => C('APPID'),
            'mch_id'            => C('MCHID'),
            'nonce_str'         => self::createNonceStr(),
            'sign_type'         => 'MD5',
            'body'              => $products_name,  //商品名称组合
            'attach'            => C('APP_NAME').'-附加信息',
            'out_trade_no'      => $trade_no,       //订单号
            'fee_type'          => 'CNY',
            'total_fee'         => $total_fee,
            'spbill_create_ip'  => $_SERVER['REMOTE_ADDR'],
            'goods_tag'         => C('APP_NAME').'-商品标记',
            'notify_url'        => $notify_url ?:C('NOTIFY_URL'),
            'trade_type'        => 'JSAPI',
            'openid'            => $openid
        ];

        $sign = self::MakeSign($data);
        $data['sign'] = $sign;
        $xml = self::ToXml($data);
        $result = self::FromXml(Http::postXmlCurl($url,$xml));

        // 加工数据
        $data = [
            'appId' => $result['appid'] ?: C('APPID'),
            'timeStamp' => time(),
            'nonceStr' => self::createNonceStr(),
            'package' => 'prepay_id=' . $result['prepay_id'],
            'signType' => 'MD5'
        ];
        $sign = self::MakeSign($data);
        $data['sign'] = $sign;
        return $data;
    }


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

    public static 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;
    }

    public static 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 static function MakeSign($data)
    {
        //签名步骤一:按字典序排序参数
        ksort($data);
        $string = self::ToUrlParams($data);
        //签名步骤二:在string后加入KEY
        $string = $string . "&key=".C('WEIXIN_PAY_KEY');
        //签名步骤三:MD5加密
        $string = md5($string);
        //签名步骤四:所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    public static 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 pay()
{
    if (!$openid = trim($_POST['openid'])) {
        $this->json->setErr(10001, '缺少参数');
        $this->json->Send();
    }
    if (!$donate_id = $_POST['donate_id']) {
        $this->json->setErr(10001, '缺少参数donate_id');
        $this->json->Send();
    }
    $amount = (float)$_POST['amount']; // 实际支付金额
    if ($amount <= 0) {
        $this->json->setErr(10002, '支付金额不可为0或负数');
        $this->json->Send();
    }
    $donate = M('donate');
    $donate_info = $donate->where(array('id' => $donate_id, 'status' => 1, 'is_show' => 1))->find();
    if (!$donate_info) {
        $this->json->setErr(10002, '捐赠内容不存在');
        $this->json->Send();
    }

    // 判断当前是否可以捐赠
    $now = time();
    if ($now < (int)$donate_info['start_time']) {
        $this->json->setErr(10003, '捐赠未开始');
        $this->json->Send();
    }

    if ($now > (int)$donate_info['end_time']) {
        $this->json->setErr(10004, '捐赠已结束');
        $this->json->Send();
    }

    $money = $donate_info['money']; // 已有捐赠金额
    $user = M('user');
    $user_info = $user->where(array('openid' => $openid))->find();
    if (!$user_info) {
        $this->json->setErr(10001, '用户信息不存在');
        $this->json->Send();
    }
    $uid = $user_info['id'];

    // step1 生成订单
    $order_info = $this->makeOrder($uid, $donate_id, $amount, $money);
    $order_num = $order_info['order_num'];
    $products_name = $order_info['products_name'];

    // step2  unifiedOrder
    $unifiedorder = WxPayService::unifiedOrder($openid, $order_num, $amount * 100, $products_name);

    // step3 将数据package下放到小程序中
    $this->json->setAttr('data', $unifiedorder);
    $this->json->Send();
}


/***
 * 生成捐赠订单
 * @param $uid
 * @param $donate_id
 * @param $amount
 * @param $money
 * @return mixed
 */
private function makeOrder($uid, $donate_id, $amount, $money)
{
    $donate_order = M('donate_order');
    $now = time();
    $order_num = 'do' . $uid . substr($now, 3) . rand(1000, 9999);
    $order_add_data = [
        'donate_id' => $donate_id,
        'order_num' => $order_num,
        'amount' => $amount,    //订单价格
        'money' => $money + $amount,
        'uid' => $uid,
        'status' => 1,               //未到账
        'create_time' => $now,            //订单生成时间
    ];
    $order_add_flag = $donate_order->add($order_add_data);
    if (!$order_add_flag) {
        $this->json->setErr(10003, '生成订单失败');
        $this->json->Send();
    }
    $return_data['order_num'] = $order_num;
    $return_data['products_name'] = '捐赠';
    return $return_data;
}


//微信支付回调
public function order_notice()
{
    $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
    $data = WxPayService::FromXml($xml);
    // 保存微信服务器返回的签名sign
    $data_sign = $data['sign'];
    // sign不参与签名算法
    unset($data['sign']);
    $sign = WxPayService::MakeSign($data);
    Clog::setLog($data, 'order_notice 回调数据');
    // 判断签名是否正确  判断支付状态
    if (($sign === $data_sign) && ($data['return_code'] == 'SUCCESS') && ($data['result_code'] == 'SUCCESS')) {
        //获取服务器返回的数据
        $order_num = $data['out_trade_no'];         //订单单号
        $openid = $data['openid'];                  //付款人openID
        $total_fee = $data['total_fee'];            //付款金额
        $transaction_id = $data['transaction_id'];  //微信支付流水号
        $user = M('user');
        $user_info = $user->where(array('openid' => $openid))->find();
        $total_payed_price = $total_fee / 100;
        $save_data = array(
            'total_payed_price' => $total_payed_price,     //实际到帐金额
            'transaction_id' => $transaction_id,
            'pay_time' => time(),
            'status' => 2,
        );

        // 开启事务
        M()->startTrans();
        $error_count = 0;

        // step 1 修改充值订单数据
        $donate_order = M('donate_order');
        $donate_order_info = $donate_order->where(array('order_num' => $order_num, 'uid' => $user_info['id']))->find();
        $donate_amount = $donate_order_info['amount'];
        $save_flag = $donate_order->where(array('order_num' => $order_num, 'uid' => $user_info['id']))->save($save_data);
        if (!$save_flag) {
            $error_count++;
            Clog::setLog('修改订单失败', 'order_notice 订单数据');
        }

        // step 2 修改捐赠总数
        $donate = M('donate');
        $donate_info = $donate->where(['id' => $donate_order_info['donate_id']])->find();
        $save_donate_data = [
            'money' => $donate_info['money'] + $donate_amount
        ];
        $money_save_flag = $donate->where(['id' => $donate_order_info['donate_id']])->save($save_donate_data);
        if (!$money_save_flag) {
            $error_count++;
            Clog::setLog('修改捐赠总数失败', 'order_notice 捐款数据');
        }

        // step 3 处理configs中的统计信息
        $configService = new ConfigService();
        $key = ['key' => ['in', ['total_donate', 'total_help', 'total_join']]];
        $total_data = $configService->queryKey($key);
        $edit_donate = $configService->updateOneKey('total_donate',$total_data['total_donate']+$total_payed_price);
        if (!$edit_donate && $edit_donate !== 0) {
            $error_count ++;
        }
        if ((float)$donate_info['money'] === 0.00) {
            // 第一次被帮助
            $edit_help = $configService->updateOneKey('total_help',$total_data['total_help']+1);
            if (!$edit_help && $edit_help !== 0) {
                $error_count ++;
            }
        }

        $save_join = $configService->updateOneKey('total_join',$total_data['total_join']+1);
        if (!$save_join && $save_join !== 0) {
            $error_count ++;
        }

        if ($error_count > 0) {
            Clog::setLog('回滚了', 'order_notice');
            M()->rollback();
            $result = -2;
        } else {
            Clog::setLog('commit了', 'order_notice');
            M()->commit();
            $result = 0;
        }
    } else {
        Clog::setLog('签名有误', 'order_notice');
        $result = -1;
    }
    Clog::setLog($result, 'order_notice');
    // 返回状态给微信服务器
    $str = '';
    if ($result === 0) { // 成功之后不会再回调
        $str = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
    } elseif ($result === -1) { // 失败后会继续发送几次回调
        $str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
    } elseif ($result === -2) { // 失败后会继续发送几次回调
        $str = '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[操作失败]]></return_msg></xml>';
    }
    exit($str);
}