admin 管理员组

文章数量: 1086866

微信APP支付demo

<?php
namespace App\Controller;
use Think\Controller;
/*** 微信支付控制器*/
class WxPayController extends HomeController
{protected $mchid; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送protected $appid; //公众号APPID 通过微信支付商户资料审核后邮件发送protected $key; // 帐户设置-安全设置-API安全-API密钥-设置API密钥protected $notify; //微信异步回调地址protected $main_url; //接口访问主地址(可根据自己项目取舍)/*** @param string $openid 调用【网页授权获取用户信息】接口获取到用户在该公众号下的Openid* @param float $totalFee 收款总费用 单位元* @param string $outTradeNo 唯一的订单号* @param string $orderName 订单名称* @param string $notifyUrl 支付结果通知url 不要有问号* @param string $spbillIp 操作IP*   / 微信支付-开发配置-测试目录*   测试目录 /  最后需要斜线,(需要精确到二级或三级目录)* @return string*//*** 初始化方法*/public function _initialize() {$this->mchid = '9999999999'; // 微信支付商户号 PartnerID 通过微信支付商户资料审核后邮件发送$this->appid = 'wxff9999999999'; //公众号APPID 通过微信支付商户资料审核后邮件发送$this->key = 'Demo9999999999';   // 帐户设置-安全设置-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('', $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请求 不验证证书和hostcurl_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请求 不验证证书和hostcurl_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 = "<xml>";foreach ($arr as $key => $val) {if (is_numeric($val)) {$xml .= "<" . $key . ">" . $val . "</" . $key . ">";} else$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";}$xml .= "</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 '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[通信出错]]></return_msg></xml>'; //通知微信支付失败;设置返回码return $postObj;} else if ($postObj->result_code == "FAIL") {//此处应该更新一下订单状态,商户自行增删操作;失败echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[业务出错]]></return_msg></xml>'; //通知微信支付失败;设置返回码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('<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>');//此处应该更新一下订单状态,商户自行增删操作;操作成功//这里就可以做自己数据库订单判断及操作了//这里可以选择直接sql操作数据库,也可以根据自己需要,模拟自己的加密算法去请求自己的接口}} else {echo '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>'; //通知微信支付失败;设置返回码return $postObj;}}
}

本文标签: 微信APP支付demo