mchid = '9999999999'; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送
$this->appid = 'wxff9999999999'; //公众号APPID 通过微信支付商户资料审核后邮件发送
$this->key = 'Demo9999999999'; //https://pay.weixin.qq.com 帐户设置-安全设置-API安全-API密钥-设置API密钥
// 设置自定义行为
\Think\Hook::add('paystatus', 'Home\\Behaviors\\PaystatusBehavior');
}
public function wxpay(){
$uid=$_POST['uid'];
$orderid=$_POST['orderid'];
$where['uid']=$uid;
$where['orderid']=$orderid;
$order_info=M('order')->where($where)->find();
if(empty($order_info)){
$msg=array('code'=>'300','msg'=>'订单不存在');
$this->ajaxReturn($msg);
}
$outTradeNo=$order_info['tag'].time();
//$totalFee=$order_info['total_money']*100;
$totalFee=$order_info['total_money']*100;
//$outTradeNo=201801151686367077;
$total = round(floatval($totalFee),2);
if (!$total) {
$total = 0;
}
$notifyUrl = "https://".$_SERVER['HTTP_HOST']."/App/wxpay/wx_notify";
$orderName = "订单编号:".$order_info['orderid'];
$spbillIp = $_SERVER['SERVER_ADDR'];
$this->createJsBizPackage($total, $outTradeNo, $orderName, $notifyUrl, $spbillIp);
}
protected function createJsBizPackage($totalFee, $outTradeNo, $orderName, $notifyUrl, $spbillIp) {
$config = array(
'mch_id' => $this->mchid,
'appid' => $this->appid,
'key' => $this->key,
);
$unified = array(
'appid' => $config['appid'],
'body' => $orderName,
'mch_id' => $config['mch_id'],
'nonce_str' => $this->createNonceStr(),
'notify_url' => $notifyUrl,
'out_trade_no' => $outTradeNo,
'spbill_create_ip' => $spbillIp,
'total_fee' => intval($totalFee * 1), //单位 转为分
'trade_type' => 'APP',
);
$unified['sign'] = $this->getSign($unified, $config['key']); //第一次签名
//print_R($this->arrayToXml($unified));
$responseXml = $this->curlPost('https://api.mch.weixin.qq.com/pay/unifiedorder', $this->arrayToXml($unified));
$unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
//print_r($unifiedOrder);die;
if ($unifiedOrder === false) {
$this->error('parse xml error');
die('parse xml error');
}
if ($unifiedOrder->return_code != 'SUCCESS') {
$this->error($unifiedOrder->return_msg);
die($unifiedOrder->return_msg);
}
if ($unifiedOrder->result_code != 'SUCCESS') {
$this->error($unifiedOrder->err_code);
die($unifiedOrder->err_code);
}
//$unifiedOrder->trade_type 交易类型 调用接口提交的交易类型,取值如下:JSAPI,NATIVE,APP
//$unifiedOrder->prepay_id 预支付交易会话标识 微信生成的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
//$unifiedOrder->code_url 二维码链接 trade_type为NATIVE是有返回,可将该参数值生成二维码展示出来进行扫码支付
//该数组内容是需要二次签名返回给客服端的,必须有时间戳,键名命名不能用驼峰法,不能有下划线或其他任何符号
//而且只能是微信要求的这个参数参与签名,其余的不能参与签名,否则客服端会报签名错误
//以下是标准命名,已在线上测试通过
/*$nonce_str = $this->toArray($unifiedOrder->nonce_str);
print_R($nonce_str);die;
$prepay_id = $this->toArray($unifiedOrder->prepay_id);*/
$nonce_str = $unifiedOrder->nonce_str;
$nonce_str = (String)$nonce_str;
$prepay_id = $unifiedOrder->prepay_id;
$prepay_id = (String)$prepay_id;
$arr = array(
"appid" => $config['appid'],
"partnerid" => $config['mch_id'],
"timestamp" => time(),
"noncestr" => $nonce_str,
"prepayid" => $prepay_id,
"package" => 'Sign=WXPay',
);
$arr['sign'] = $this->getSign($arr, $config['key']); //第二次签名
$arr['outtradeno'] = $unified['out_trade_no']; //返回商户订单号
//print_R($arr);die;
$this->ajaxReturn($arr) ;
}
public static function toArray($simplexml_obj, $array_tags=array(), $strip_white=1)
{
if( $simplexml_obj )
{
if( count($simplexml_obj)==0 ){
return $strip_white?trim((string)$simplexml_obj):(string)$simplexml_obj;
}
$attr = array();
foreach ($simplexml_obj as $k=>$val) {
if( !empty($array_tags) && in_array($k, $array_tags) ) {
$attr[] = self::toArray($val, $array_tags, $strip_white);
}else{
$attr[$k] = self::toArray($val, $array_tags, $strip_white);
}
}
return $attr;
}
return false;
}
/**
* 生成随机字符串
* @param type $length 设置随机长度,默认32位
*/
protected function createNonceStr($length = 32) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* 获取签名
* @param type $params 签名的对象
* @param type $key 签名key |_mC`3^+z!kDlN>U~5cB{fcWSqZt|>
/V-P|
*/
protected function getSign($params, $key) {
ksort($params, SORT_STRING);
$unSignParaString = $this->formatQueryParaMap($params, false);
$signStr = strtoupper(md5($unSignParaString . "&key=" . $key));
return $signStr;
}
/**
* 签名规则
* @param type $paraMap
* @param type $urlEncode
* @return type
*/
private function formatQueryParaMap($paraMap, $urlEncode = false) {
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if (null != $v && "null" != $v) {
if ($urlEncode) {
$v = urlencode($v);
}
$buff .= $k . "=" . $v . "&";
}
}
$reqPar = '';
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
/**
* curl post
* @param type $url
* @param type $postData
* @param type $options
* @return type
*/
protected function curlPost($url = '', $postData = '', $options = array()) {
if (is_array($postData)) {
$postData = http_build_query($postData);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
/**
* curl get
* @param string $url
* @param array $options
* @return mixed
*/
protected function curlGet($url = '', $options = array()) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
//https请求 不验证证书和host
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
/**
* 数组转化为XML
* @param type $arr
* @return string
*/
protected function arrayToXml($arr) {
$xml = "";
foreach ($arr as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "" . $key . ">";
} else
$xml .= "<" . $key . ">" . $key . ">";
}
$xml .= "";
return $xml;
}
public function logResult($postStr){
$world = date("Y-m-d h:i:s",time());
$file = $_SERVER['DOCUMENT_ROOT'].'/log/Wxpay_app_notify.txt';
date_default_timezone_set("PRC");
$fp = fopen($file,"a");
flock($fp, LOCK_EX) ;
fwrite($fp,"执行日期:".print_r($postStr,true)."\n".$world."\n");
flock($fp, LOCK_UN);
fclose($fp);
}
function xmlToArray($xml){
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring),true);
return $val;
}
/**
* 微信回调地址
* @return type
* 通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒
*/
public function wx_notify(){
//echo "123";die;
$config = array(
'mch_id' => '999999999',
'appid' => 'wxff99999999999',
'key' => 'Demo99999999999999',
);
$postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; //获取微信返回的数据
//$this->logResult($postStr);
$this->logResult($postStr);
$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); //转化数据,进行验签
if ($postObj === false) {
die('parse xml error');
}
if ($postObj->return_code != 'SUCCESS') {
die($postObj->return_msg);
}
if ($postObj->result_code != 'SUCCESS') {
die($postObj->err_code);
}
$arr = (array) $postObj;
unset($arr['sign']);
if ($this->getSign($arr, $config['key']) == $postObj->sign) {
if ($postObj->return_code == "FAIL") {
//此处应该更新一下订单状态,商户自行增删操作;失败
echo ''; //通知微信支付失败;设置返回码
return $postObj;
} else if ($postObj->result_code == "FAIL") {
//此处应该更新一下订单状态,商户自行增删操作;失败
echo ''; //通知微信支付失败;设置返回码
return $postObj;
} else {
$tag=substr($arr['out_trade_no'],0,strlen($arr['out_trade_no'])-10);
$where['tag']=substr($arr['out_trade_no'],0,strlen($arr['out_trade_no'])-10);
//$where['tag']=$arr['out_trade_no'];
$order=M("order");
$re=$order->where($where)->find();
if($re['status'] == -1){
// 监听用户支付状态
\Think\Hook::listen('paystatus',$tag);
$order->status=1;
$order->ispay=2;
$order->wx_transaction_id=$arr['transaction_id'];
$order->time_pay = $arr['time_end'];
$order->payway=4;
$order->where($where)->save();
//改变销量
$data=M('shoplist')->where($where)->getField('goodid,num');
foreach ($data as $k=>$v)
{
$whereid['id']=array('eq',$k);
$sale=M('document')->where($whereid)->getField('sale');
$set['sale']=intval($v)+intval($sale);
M('document')->where($where)->setField($set);
}
}
echo exit('');
//此处应该更新一下订单状态,商户自行增删操作;操作成功
//这里就可以做自己数据库订单判断及操作了
//这里可以选择直接sql操作数据库,也可以根据自己需要,模拟自己的加密算法去请求自己的接口
}
} else {
echo ''; //通知微信支付失败;设置返回码
return $postObj;
}
}
}