'wxnative_appid', 'type' => 'text', 'value' => ''),
array('name' => 'wxnative_appsecret', 'type' => 'text', 'value' => ''),
array('name' => 'wxnative_mchid', 'type' => 'text', 'value' => ''),
array('name' => 'wxnative_key', 'type' => 'text', 'value' => ''),
);
return;
}
/**
* 类
*/
class wxnative
{
var $parameters; // cft 参数
var $payment; // 配置信息
/**
* 构造函数
*
* @access public
* @param
*
* @return void
*/
function __construct()
{
$this->wxnative();
}
function wxnative()
{
}
/**
* 生成支付代码
* @param array $order 订单信息
* @param array $payment 支付方式信息
*/
function get_code($order, $payment)
{
if (!defined('EC_CHARSET'))
{
$charset = 'utf-8';
}
else
{
$charset = EC_CHARSET;
}
//为respond做准备
$this->payment = $payment;
$charset = strtoupper($charset);
$root=$GLOBALS['ecs']->url();
$notify_url=$root."wx_native_callback.php";
$this->setParameter("body", $order['order_sn']); // 產品描述
$this->setParameter("out_trade_no", $order['order_sn'] . 'O' . $order['log_id'].'O'.$order['order_amount'] * 100); // 商户订单号
$this->setParameter("total_fee", $order['order_amount'] * 100); // 总金额
$this->setParameter("notify_url", urlencode($notify_url)); // 通知地址
$this->setParameter("trade_type", "NATIVE"); // 交易类型
$this->setParameter("product_id", $order['order_sn']);
$code_url = $this->getCodeUrl();
$payment_path=$root.'includes/modules/payment/wxnative/';
$javascript=' ';
if(!$code_url){
$button = '
';
$this->logResult("error::get_code::code_url为空");
return $javascript.$button;
}
if(JS_QR){
$javascript.='';
//参数1表示图像大小,取值范围1-10;参数2表示质量,取值范围'L','M','Q','H'
$javascript.='';
$button = '';
}else{
require_once('wxnative/cls_qrcode.php');
$qr_root_path=ROOT_PATH."data/wxqr/".date("Ym")."/";
if(!is_dir($qr_root_path)){
if(!mkdir($qr_root_path, 0777, true)){
$this->logResult("log::get_code::创建目录失败:");
$button = '';
return $javascript.$button;
}
}
$qr_file=$qr_root_path.md5($code_url).".png";
$errorCorrectionLevel = 'L';
$matrixPointSize = 10;
QRcode::png($code_url, $qr_file, $errorCorrectionLevel, $matrixPointSize, 2);
$qr_file_url=str_replace(ROOT_PATH,$root,$qr_file);
$button = '';
}
$javascript.='';
$this->logResult("log::get_code::code_url:",$code_url);
$this->logResult("log::get_code::button:".$javascript.$button);
return $button.$javascript;
}
function logResult($word = '',$var=array()) {
if(!WXPAY_DEBUG){
return true;
}
$output= strftime("%Y%m%d %H:%M:%S", time()) . "\n" ;
$output .= $word."\n" ;
if(!empty($var)){
$output .= print_r($var, true)."\n";
}
$output.="\n";
$log_path=ROOT_PATH . "/data/log/";
if(!is_dir($log_path)){
@mkdir($log_path, 0777, true);
}
file_put_contents($log_path."wxnative.txt", $output, FILE_APPEND | LOCK_EX);
}
/**
* 响应操作
*/
function respond() {
$this->payment = get_payment($_GET['code']);
if(!empty($_GET["check"])){
$this->logResult("log::respond::start from explore:");
$outTradeNo=$_GET["out_trade_no"];
$time=$_GET["time"];
if(empty($_SESSION["startTime"])){
$_SESSION["startTime"]=$time;
$_SESSION["lastTime"]=$time;
}
$outTradeNoArray = explode('O', $outTradeNo);
$log_id = $outTradeNoArray[1]; // 订单号log_id
$result=$this->_checkStatus($log_id,$outTradeNo,$time);
if(!empty($_GET["redirect"])){
if($result["error"]>0)
return false;
else
return true;
}else{
die(json_encode($result));
}
}else{
$this->logResult("log::respond::start from wx:");
$xml = $GLOBALS['HTTP_RAW_POST_DATA'];
$postdata=$this->xmlToArray($xml);
$this->logResult("log::respond::postdata:",$postdata);
$wxsign = $postdata['sign'];
unset($postdata['sign']);
$sign = $this->getSign($postdata);
$this->logResult("log::respond::sign:",$sign);
if ($wxsign == $sign) {
// 交易成功
if ($postdata['result_code'] == 'SUCCESS') {
// 获取log_id
$out_trade_no = explode('O', $postdata['out_trade_no']);
$order_sn = $out_trade_no[1]; // 订单号log_id
order_paid($order_sn, 2);
}
$returndata['return_code'] = 'SUCCESS';
} else {
$returndata['return_code'] = 'FAIL';
$returndata['return_msg'] = '签名失败';
}
$returnXML=$this->arrayToXml($returndata);
echo $returnXML;
exit;
}
}
private function _checkStatus($log_id,$outTradeNo,$time){
if(empty($outTradeNo) || empty($log_id))
return array("error"=>1,"message"=>"参数错误");
$sql = 'SELECT is_paid FROM ' . $GLOBALS['ecs']->table('pay_log') .
" WHERE log_id = '$log_id'";
$is_paid = $GLOBALS['db']->getOne($sql);
if($is_paid>0){
$_SESSION["startTime"]=0;
$_SESSION["lastTime"]=0;
return array("message"=>"交易成功","error"=>0);
}
//五分钟之内 间隔 1倍 QUERY_INTERVAL 时间
if($time-$_SESSION["lastTime"]>0 && $time-$_SESSION["lastTime"]"请求间隔","error"=>5);
}
// 五分钟之后 间隔2倍 QUERY_INTERVAL 时间
if($time-$_SESSION["startTime"]>300000 && $time-$_SESSION["lastTime"]>0 && $time-$_SESSION["lastTime"]"请求间隔","error"=>5);
}
$_SESSION["lastTime"]=$time;
$this->setParameter("out_trade_no",$outTradeNo);
$queryXML=$this->createXml();
$url="https://api.mch.weixin.qq.com/pay/orderquery";
$resultXML=$this->postXmlCurl($queryXML,$url);
$result=$this->xmlToArray($resultXML);
$this->logResult("log::_checkStatus::query:",$queryXML);
$this->logResult("log::_checkStatus::resultxml:",$resultXML);
$this->logResult("log::_checkStatus::result:",$result);
if(empty($result)){
return array("message"=>"通信出错:".$result['return_msg'],"error"=>2);
}
if ($result["return_code"] == "FAIL") {
return array("message"=>"通信出错:".$result['return_msg'],"error"=>2);
}
if ($result['trade_state'] == 'SUCCESS') {
// 获取log_id
$out_trade_no = explode('O', $result['out_trade_no']);
$order_sn = $out_trade_no[1]; // 订单号log_id
order_paid($order_sn, 2);
$_SESSION["startTime"]=0;
$_SESSION["lastTime"]=0;
return array("message"=>"交易成功","error"=>0);
}elseif($result['trade_state'] == 'NOTPAY' || $result['trade_state'] == 'USERPAYING'){
if( $result['trade_state'] == 'USERPAYING'){
$_SESSION["lastTime"]=$time-QUERY_INTERVAL*1500;
}
return array("message"=>$result["trade_state_desc"],"error"=>3);
}else{
$_SESSION["startTime"]=0;
$_SESSION["lastTime"]=0;
return array("message"=>$result["trade_state_desc"],"error"=>100);
}
}
/**
* 获取code_url
*/
function getCodeUrl(){
// 设置接口链接
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
if ($this->parameters["out_trade_no"] == null) {
$this->logResult("error::getCodeUrl::缺少统一支付接口必填参数out_trade_no");
} elseif ($this->parameters["body"] == null) {
$this->logResult("error::getCodeUrl::缺少统一支付接口必填参数body");
} elseif ($this->parameters["total_fee"] == null) {
$this->logResult("error::getCodeUrl::缺少统一支付接口必填参数total_fee");
} elseif ($this->parameters["notify_url"] == null) {
$this->logResult("error::getCodeUrl::缺少统一支付接口必填参数notify_url");
} elseif ($this->parameters["trade_type"] == null) {
$this->logResult("error::getCodeUrl::缺少统一支付接口必填参数trade_type");
} elseif ($this->parameters["trade_type"] == "JSAPI" && $this->parameters["openid"] == NULL) {
$this->logResult("error::getCodeUrl::统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
}
$this->parameters["appid"] = $this->payment['wxnative_appid']; // 公众账号ID
$this->parameters["mch_id"] = $this->payment['wxnative_mchid']; // 商户号
$this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR']; // 终端ip
$this->parameters["nonce_str"] = $this->createNoncestr(); // 随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters); // 签名
$this->logResult("log::getCodeUrl::parameters::",$this->parameters);
$xml=$this->arrayToXml($this->parameters);
$this->logResult("log::getCodeUrl::xml::",$xml);
$response =$this->postXmlCurl($xml,$url) ;
$result = $this->xmlToArray($response);
$this->logResult("log::getCodeUrl::curlresult",$response);
$code_url = $result["code_url"];
return $code_url;
}
/**
* 作用:产生随机字符串,不长于32位
*/
public function createNoncestr($length = 32){
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i ++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue){
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
function trimString($value)
{
$ret = null;
if (null != $value)
{
$ret = $value;
if (strlen($ret) == 0)
{
$ret = null;
}
}
return $ret;
}
public function getSign($Obj){
foreach ($Obj as $k => $v) {
$Parameters[$k] = $v;
}
// 签名步骤一:按字典序排序参数
ksort($Parameters);
$buff = "";
foreach ($Parameters as $k => $v) {
$buff .= $k . "=" . $v . "&";
}
$String="";
if (strlen($buff) > 0) {
$String = substr($buff, 0, strlen($buff) - 1);
}
// echo '【string1】'.$String.'';
// 签名步骤二:在string后加入KEY
$String = $String . "&key=" . $this->payment['wxnative_key'];
// echo "【string2】".$String."";
// 签名步骤三:MD5加密
$String = md5($String);
// echo "【string3】 ".$String."";
// 签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
// echo "【result】 ".$result_."";
return $result_;
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
function arrayToXml($arr)
{
$xml = "";
foreach ($arr as $key=>$val)
{
if (is_numeric($val))
{
$xml.="<".$key.">".$val."".$key.">";
}
else
$xml.="<".$key.">".$key.">";
}
$xml.="";
return $xml;
}
function createXml()
{
//检测必填参数
if($this->parameters["out_trade_no"] == null &&
$this->parameters["transaction_id"] == null){
return false;
}
$this->parameters["appid"] = $this->payment["wxnative_appid"];//公众账号ID
$this->parameters["mch_id"] = $this->payment["wxnative_mchid"];//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用:以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml,$url,$second=30)
{
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
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);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
//运行curl
$data = curl_exec($ch);
//返回结果
if(!$data){
$error = curl_errno($ch);
$this->logResult("error::postXmlCurl::curl出错,错误码:$error,http://curl.haxx.se/libcurl/c/libcurl-errors.html 错误原因查询");
}
curl_close($ch);
return $data;
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
function postXmlSSLCurl($xml,$url,$second=30)
{
$ch = curl_init();
//超时时间
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
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);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, SSLCERT_PATH);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, SSLKEY_PATH);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}
else {
$error = curl_errno($ch);
echo "curl出错,错误码:$error"."
";
echo "错误原因查询";
curl_close($ch);
return false;
}
}
}
?>