<?php
/**
 * WxPay.php
 *
 * 微信小程序开发API接口
 * - 小程序码
 * - 安全内容
 * - 微信支付
 *
 */

namespace echowx;

use think\Exception;

use echowx\utils\HttpCurl;
use echowx\utils\Error;
use echowx\utils\SHA1;
use echowx\utils\Xml;

class WxPay
{
    
    // 开发者中心-配置项-AppID(应用ID)
    protected $appId;
    // 开发者中心-配置项-AppSecret(应用密钥)
    protected $appSecret;
    // 微信支付商户号，商户申请微信支付后，由微信支付分配的商户收款账号
    protected $payMchId;
    // API密钥,微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
    protected $payKey;
	// 微信支付回调地址
	protected $payNotifyUrl;
    
    
    /**
     * 设定配置项
     *
     * @param 微信小程序配置文件读取
     */
    public function __construct()
    {
        $this->appId                    =   config('wxconfig.appId');
        $this->appSecret                =   config('wxconfig.appSecret');
        $this->payMchId               	=   config('wxconfig.payMchId');
        $this->payKey                   =   config('wxconfig.payKey');
		$this->payNotifyUrl             =   config('wxconfig.payNotifyUrl');
    }
    
    
    /**
     * 校验access_token是否过期
     * @param string $token
     * @return bool
     */
    public function valid_access_token($token)
    {
        return $token && isset($token['expires_in']) && ($token['expires_in'] > time() + 1200);
    }
    
    /**
     * 生成新的access_token
     * @return mixed
     */
    public function new_access_token()
    {
        $url = self::API_DOMAIN . 'cgi-bin/token?grant_type=client_credential&appid=' . $this->appId . '&secret=' . $this->appSecret;
        $res = HttpCurl::get($url, 'json');
        // 异常处理: 获取access_token网络错误
        if ($res === false) {
            @error_log('Http Get AccessToken Error.', 0);
            return false;
        }
        // 异常处理: access_token获取失败
        if (!isset($res['access_token'])) {
            @error_log('Get AccessToken Error: ' . json_encode($res), 0);
            return false;
        }
        $res['expires_in'] += time();
        $fp = fopen(dirname(__FILE__)."/json/access_token.json", "w");
        fwrite($fp, json_encode($res));
        fclose($fp);
        return $res;
    }
    
    /**
     * 获取access_token
     * @return string
     */
    public function get_access_token()
    {
        $token = json_decode(file_get_contents(dirname(__FILE__)."/json/access_token.json"), true);
        // 验证AccessToken是否有效
        if (!$this->valid_access_token($token)) {
            // 生成新的AccessToken
            $token = $this->new_access_token();
            if ($token === false) {
                return false;
            }
        }
        return $token['access_token'];
    }
    
	
	/**
	 * WxPay - 统一下单 - 生成预订单
	 * @param array $conf 配置数组
	 * @return bool|mixed
	 */
	public function wxpay_unified_order($conf = [])
	{
	
	    // [必填]公众账号ID、应用ID
	    $conf['appid'] = $this->appId;
	
	    // [必填]商户号、
	    $conf['mch_id'] = $this->payMchId;
	
	    // 设备号
	    // - device_info
	
	    // [必填]nonce_str 随机字符串
	    $conf['nonce_str'] = SHA1::get_random_str(32);
	
	    // 签名类型
	    $conf['sign_type'] = 'MD5';
	
	    // [必填]商品描述
	    // - body
	
	    // 商品详情
	    // - detail
	
	    // 附加数据
	    // - attach
	
	    // [必填]商户订单号
	    // - out_trade_no
	
	    // 货币类型
	    // - fee_type
	
	    // [必填]总金额
	    // - total_fee
	
	    // [必填]终端IP
	    $conf['spbill_create_ip'] = $_SERVER['REMOTE_ADDR'];
	
	    // 交易起始时间
	    // - time_start
	
	    // 交易结束时间
	    // - time_expire
	
	    // 商品标记
	    // - goods_tag
	
	    // [必填]通知地址
	    $conf['notify_url'] = $this->payNotifyUrl;
	
	    // [必填]交易类型
	    $conf['trade_type'] = "JSAPI";
	
	    // 指定支付方式
	    // - limit_pay
	
	    // 用户标识, trade_type=JSAPI时（即公众号支付），此参数必传
	    // - openid
	
	    // [必填]签名
	    $conf['sign'] = SHA1::getSign2($conf, 'key='.$this->payKey);

	    // 生成xml
	    $xml = Xml::toXml($conf);
	
	    // 调用接口
	    $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
	    try {
	        $res = HttpCurl::post($url, $xml);
	        libxml_disable_entity_loader(true);
	        $datas = json_decode(json_encode(simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
			if($datas['return_code'] == 'FAIL'){
			    $result = array('status'=>40014,'data'=>$datas['return_msg']);
			    return $result;
			}if($datas['result_code'] == 'FAIL'){ 
				$result = array('status'=>40014,'data'=>$datas['err_code']."：".$datas['err_code_des']);
				return $result;
			}
			//判断签名是否存在,及签名是否正确
			if(!array_key_exists('sign', $datas)){
			    $result = array('status'=>40011,'data'=>'签名错误！');
			    return $result;
			}
			$result = array('status'=>1,'data'=>$datas);
			return $result;
	    } catch (\Exception $e) {
	        $result = array('status'=>40011,'data'=>'下单失败，请稍后重试！');
	        return $result;
	    }
	}
	
	/**
	 * WxPay - 获取jsapi支付的参数
	 * 用于直接填入js函数作为参数
	 * @param string $prepayId 预生成订单ID
	 * @return string
	 */
	public function get_jsapi_param($prepayId)
	{
	    $input = [
	        'appId'			=> $this->appId,
			'nonceStr'		=> SHA1::get_random_str(32),
			'package'		=> 'prepay_id='.$prepayId,
	        'timeStamp'		=> (string)time(),
	        'signType'		=> 'MD5'
	    ];
	    $input['paySign'] = SHA1::getSign2($input, 'key='.$this->payKey);
	    return $input;
	}
	
	
	/**
	 * WxPay - 处理微信支付异步通知
	 * 包含
	 * @return array [是否支付成功, 异步通知的原始数据, 回复微信异步通知的数据]
	 */
	public function notify()
	{
	    $xml = file_get_contents('php://input');
	    try {
	        libxml_disable_entity_loader(true);
	        $data = Xml::toArr($xml);
			if (! is_array($data)) {
				$result = array('status'=>40014,'data'=>'支付回调出错。');
				return $result;
			}
			// 格式是否正确
			if (! array_key_exists('return_code', $data)) {
				$result = array('status'=>40011,'data'=>'return_code is not set。');
				return $result;
			}
			// 是否支付成功
			if ($data['return_code'] != 'SUCCESS') {
				$result = array('status'=>40011,'data'=>'return_code is '.$data['return_code']);
				return $result;
			}
			// 签名是否正确
			$sign1 = SHA1::getSign2($data, 'key='.$this->payKey);
			if ($sign1 != $data['sign']) {
				$result = array('status'=>40011,'data'=>'签名验证失败。');
				return $result;
			}
			//返回结果
			$result = array('status'=>1,'data'=>$data);
			return $result;
	    } catch (\Exception $e) {
	        $result = array('status'=>40011,'data'=>'回调失败！');
	        return $result;
	    }
	}
	
	/**
	 * WxPay - 回复微信异步通知
	 * 包含
	 * @param array $info 回复内容数组
	 */
	public function reply_notify()
	{
	    $data['return_code'] = 'SUCCESS';
	    $data['return_msg'] = 'OK';
	    $xml = Xml::toXml($data);
	    echo $xml;
	    die();
	}
	
	
	
	
}
