WxPay.Api.php 9.89 KB
<?php
require_once IA_ROOT . '/addons/zh_cjdianc/inc/Log.php';
require_once IA_ROOT . "/addons/zh_cjdianc/inc/wxlib/WxPay.Exception.php";
require_once IA_ROOT . "/addons/zh_cjdianc/inc/wxlib/WxPay.Data.php";
require_once IA_ROOT . '/addons/zh_cjdianc/inc/wxlib/WxUnifiedOrder.php';

/**
 * 
 * 接口访问类,包含所有微信支付API列表的封装,类中方法为static方法,
 * 每个接口有默认超时时间(除提交被扫支付为10s,上报超时时间为1s外,其他均为6s)
 * @author widyhu
 *
 */
class WxPayApi
{
	public static $orderQueryURL	= "https://api.mch.weixin.qq.com/pay/orderquery";
	public static $refundURL		= "https://api.mch.weixin.qq.com/secapi/pay/refund";
	public static $transferURL		= "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
	public static $unifiedOrderURL	= 'https://api.mch.weixin.qq.com/pay/unifiedorder';
	/**
	 * 
	 * 查询订单,WxPayOrderQuery中out_trade_no、transaction_id至少填一个
	 * appid、mchid、spbill_create_ip、nonce_str不需要填入
	 * @param WxPayOrderQuery $inputObj
	 * @param int $timeOut
	 * @throws WxPayException
	 * @return 成功时返回,其他抛异常
	 */
	public static function orderQuery($inputObj, $timeOut = 6, $key = '')
	{
		//检测必填参数
		if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) {
			throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
		}
		if(!$inputObj->IsAppidSet() ) {
			throw new WxPayException("APPID 未填!");
		}
		if(!$inputObj->IsMch_idSet() ) {
			throw new WxPayException("MCHID 未填!");
		}

		$inputObj->SetNonce_str(self::getNonceStr());//随机字符串
		$inputObj->SetSign($key);					//签名
		$xml = $inputObj->ToXml();
		
		$startTimeStamp = self::getMillisecond();//请求开始时间
		$response = self::postXmlCurl($xml, self::$orderQueryURL, false, $timeOut,'1','2');
		$result = WxPayResults::Init($response);
		return $result;
	}
	
	/**
	 *
 	 *退款接口
 	 *
  	 */
	public static function refund($inputObj, $timeOut = 6,$f1,$f2,$key)
	{
		//检测必填参数
		if(!$inputObj->IsOut_trade_noSet() && !$inputObj->IsTransaction_idSet()) {
			throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
		} else if(!$inputObj->IsOut_refund_noSet()) {
			throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
		} else if(!$inputObj->IsTotal_feeSet()) {
			throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
		} else if(!$inputObj->IsRefund_feeSet()) {
			throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
		} else if(!$inputObj->IsOp_user_idSet()) {
			throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
		}
		$inputObj->SetNonce_str(self::getNonceStr());//随机字符串
		$inputObj->SetSign($key);//签名
		$xml = $inputObj->ToXml();
		$startTimeStamp = self::getMillisecond();//请求开始时间
		$response = self::postXmlCurl($xml, self::$refundURL, true, $timeOut,$f1,$f2);
		$result = WxPayResults::Init($response);
		return $result;
	}

	/**
	 * 
	 * 产生随机字符串,不长于32位
	 * @param int $length
	 * @return 产生的随机字符串
	 */
	public static function getNonceStr($length = 32) 
	{
		$chars = "abcdefghijklmnopqrstuvwxyz0123456789";  
		$str ="";
		for ( $i = 0; $i < $length; $i++ )  {  
			$str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);  
		} 
		return $str;
	}

	/**
	 * 以post方式提交xml到对应的接口url
	 * 
	 * @param string $xml  需要post的xml数据
	 * @param string $url  url
	 * @param bool $useCert 是否需要证书,默认不需要
	 * @param int $second   url执行超时时间,默认30s
	 * @throws WxPayException
	 */
	private static function postXmlCurl($xml, $url, $useCert, $second = 30, $f1 = '', $f2 = '')
	{
		$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($useCert == true){
			//设置证书
			//使用证书:cert 与 key 分别属于两个.pem文件
			curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLCERT, $f1);
			curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
			curl_setopt($ch,CURLOPT_SSLKEY, $f2);
		}
		//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;
		} else {
			$error = curl_errno($ch);
			curl_close($ch);
			message("退款失败,CURL出错,错误码:".$error);
			exit;
//			throw new WxPayException("curl出错,错误码:$error");
		}
	}

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

	/**
	 * @param $inputObj
	 * @param int $timeOut
	 * @param $f1
	 * @param $f2
	 * @param $key
	 * @return array
	 * @throws WxPayException
	 */
	public static function transferTo($inputObj, $timeOut = 6, $f1, $f2, $key)
	{
		//检测必填参数
		if(!$inputObj->IsMchAppIdSet()) {
			throw new WxPayException("转账接口未填 mch_appid!");
		} else if(!$inputObj->IsMchIdSet()) {
			throw new WxPayException("转账接口未填 mchid!");
		} else if(!$inputObj->IsPartnerTradeNoSet()) {
			throw new WxPayException("订单号未设置!");
		} else if(!$inputObj->IsSpbilCreateIpSet()) {
			throw new WxPayException("IP 地址未设置!");
		} else if(!$inputObj->IsOpenIdSet()) {
			throw new WxPayException("未指定收款人微信openid!");
		} else if(!$inputObj->IsAmountSet()) {
			throw new WxPayException("未指定金额!");
		} else if(!$inputObj->IsReUserNameSet()) {
			throw new WxPayException("未指收款人名称!");
		}
		$inputObj->SetNonceStr(self::getNonceStr());//随机字符串
		$inputObj->SetSign($key);//签名
		$xml = $inputObj->ToXml();

		$response = self::postXmlCurl($xml, self::$transferURL, true, $timeOut, $f1, $f2);
		$result = WxPayResults::Init($response);

		return $result;
	}

	public static function buildUnifiedOrder($inputObj, $key)
	{
		if(!$inputObj->IsAppIdSet()) {
			throw new WxPayException("转账接口未填 appid!");
		} else if(!$inputObj->IsMchIdSet()) {
			throw new WxPayException("转账接口未填 mchid!");
		} else if(!$inputObj->IsOutTradeNoSet()) {
			throw new WxPayException("内部交易号未设置!");
		} else if(!$inputObj->IsSpbilCreateIpSet()) {
			throw new WxPayException("IP 地址未设置!");
		} else if(!$inputObj->IsOpenIdSet()) {
			throw new WxPayException("未指定收款人微信openid!");
		} else if(!$inputObj->IsTotalFeeSet()) {
			throw new WxPayException("未指定金额!");
		}
		$inputObj->SetNonceStr(self::getNonceStr());//随机字符串
		$inputObj->SetSign($key);//签名
		$xmlData = $inputObj->ToXml();
		//Log::debug(json_encode('toxml:'.$xmlData, JSON_UNESCAPED_UNICODE));
		$response = self::postXmlCurl($xmlData, self::$unifiedOrderURL, false, 60);
		$result = WxPayResults::Init($response);
		//Log::debug(json_encode('toxml - result:'. $result, JSON_UNESCAPED_UNICODE));
		return $result;
	}

	/**
	 * @param $inputObj
	 * @param $key
	 * @return array
	 * @throws WxPayException
	 */
	public static function payParams($inputObj, $key)
	{
		$unifiedOrderResult = self::buildUnifiedOrder($inputObj, $key);
		$parameters = array(
				'appId' 	=> $inputObj->GetAppId(), //小程序ID
				'timeStamp' => '' . time() . '', //时间戳
				'nonceStr' 	=> self::getNonceStr(),
				'package' 	=> 'prepay_id=' . $unifiedOrderResult['prepay_id'], //数据包
				'signType' 	=> 'MD5'//签名方式
		);
		//签名
		$parameters['paySign'] = $inputObj->MakeSign($key, $parameters);

		return $parameters;
	}

	/**
	 *
	 * 支付结果通用通知
	 * @param function $callback
	 * 直接回调函数使用方法: notify(you_function);
	 * 回调类成员函数方法:notify(array($this, you_function));
	 * $callback  原型为:function function_name($data){}
	 */
	public static function notify($callback, &$msg)
	{
		//获取通知的数据
		$xml = file_get_contents("php://input") ;
		//如果返回成功则验证签名
		try {
			$result = WxPayResults::Init($xml);
		} catch (WxPayException $e){
			$msg = $e->errorMessage();
			return false;
		}
		//Log::debug('notify----:'.$xml);
		return call_user_func($callback, $result);
	}


	/**
	 * 直接输出xml
	 * @param string $xml
	 */
	public static function replyNotify($xml)
	{
		echo $xml;
	}


	/*
 * $params = [$appid,$openid,$mch_id,$key,$out_trade_no,$body,$total_fee,$notify_url, ip]
 *
 * @param array $params
 * @return array
 */
	public static function payJsParams($params = [])
	{
		$unifiedOrder = new WxUnifiedOrder();
		$unifiedOrder->SetAppId($params['appid']);
		$unifiedOrder->SetMchId($params['mch_id']);
		$unifiedOrder->SetBody($params['body']);
		$unifiedOrder->SetOpenId($params['openid']);
		$unifiedOrder->SetOutTradeNo($params['out_trade_no']);
		$unifiedOrder->SetAttach($params['attach']);
		$unifiedOrder->SetNotifyUrl($params['notify_url']);
		$unifiedOrder->SetTotalFee($params['total_fee']);
		$unifiedOrder->SetSpbilCreateIp($params['ip']);
		$unifiedOrder->SetTradeType();
		$wxPayApi = new WxPayApi();
		$parameters = $wxPayApi->payParams($unifiedOrder, $params['key']);

		return $parameters;
	}
}