微信支付方式有很多,在公众号开发中比较常用的就是用JSAPI进行支付处理,方便快捷,官网提供的有sdk,接入方面比较简单,不做说明,或有时间了再整理吧,着重总结下在处理支付结果通知的方面遇到的恶心问题。
项目框架:tp5
头部引入微信支付类:
//微信支付类
use wxpay\database\WxPayUnifiedOrder;
use wxpay\database\WxPayOrderQuery;
use wxpay\JsApiPay;
use wxpay\NativePay;
use wxpay\PayNotifyCallBack;
use wxpay\WxPayApi;
use wxpay\WxPayConfig;
jsapi中支付回调域名:
$tools = new JsApiPay();
$openId = $member_session['openid'];
//本站域名
$web_url = "http://".$_SERVER['HTTP_HOST'];
//统一下单
$input = new WxPayUnifiedOrder();
$input->setBody("支付".($order_info['need_pay'])."元");
$input->setAttach("test");
$input->setOutTradeNo($out_trade_no);
$input->setTotalFee($order_info['need_pay']*100);
$input->setTimeStart(date("YmdHis"));
$input->setTimeExpire(date("YmdHis", time()+600));
$input->setGoodsTag("Reward");
$input->setNotifyUrl($web_url."/index/base/notify/");
$input->setTradeType("JSAPI");
$input->setOpenid($openId);
$order = WxPayApi::unifiedOrder($input);
$jsApiParameters = $tools->getJsApiParameters($order);
踩坑一:回调域名中不能有参数,当时我为了方便,把订单id放到回调域名里面准备当做参数接收的,不可取
踩坑二:回调域名需要外网能访问【说了句废话】,同时不能有登陆等验证,比方说我放到会员控制器里面,结果在方法初始化的时候检测不到用户名结果做了302重定向,那这个回调域名就压根起不到作用了,所以我放在base公共页面里面用以接收。
base.php
因为有的服务器环境限制,file_get_contents无法使用,另外加上php7好像又限制了$GLOBALS['HTTP_RAW_POST_DATA']获取流文本,所以最好的方式就是把两个都加上:
$post = input("post.");
if ($post == null) {
$post = file_get_contents("php://input");
}
if ($post == null) {
$post = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
}
至少保证有一个方法能获取到数据。
同时,因为支付通知是异步进行的,所以如何检测返回的信息是否正确以及对信息进行处理就变得比较重要了,这个地方我用的是写入日志文件:将支付信息以天为单位,存到文件夹中,如果文件夹不存在,则创建并赋予777权限
define('BASE_PATH',$_SERVER['DOCUMENT_ROOT']);
$dir = BASE_PATH.'/backdata/'.date("Ymd")."/";
if(!file_exists($dir)){
mkdir($dir,0777,true);
}
file_put_contents($dir."backdata.txt",$post,FILE_APPEND);
如果不出权限意外,在指定文件夹里面应该能看到这样的数据:
10
接下来就是处理接收到的xml数据了
调试技巧:返回上面xml数据后,可以将最开始的接收数据注释掉,然后用 file_get_contents 将刚获取并存入文件的xml数据当做数据源赋值给$post,然后就能直接进行下面的调试操作,非常方便。
libxml_disable_entity_loader(true); //禁止引用外部xml实体
$xml = simplexml_load_string($post, 'SimpleXMLElement', LIBXML_NOCDATA);//XML转数组
$post_data = (array)$xml;
//订单号
$out_trade_no = isset($post_data['out_trade_no']) && !empty($post_data['out_trade_no']) ? $post_data['out_trade_no'] : 0;
//查询订单信息
$order_info = Loader::model("Member")->order_info('',$post_data['out_trade_no']);
//var_dump($order_info);exit;
if(count($order_info) > 0 && $order_info['pay_status']==1){
//平台支付key
$wxpay_key = '';
//接收到的签名
$post_sign = $post_data['sign'];
unset($post_data['sign']);
//重新生成签名
$newSign = $this->MakeSign($post_data,$wxpay_key);
//签名统一,则更新数据库
if($post_sign == $newSign){
$pay_status = 2;
Loader::model("Member")->update_order($pay_status,$order_info['ordernum']);
}
}
public function MakeSign($params,$key){
//签名步骤一:按字典序排序数组参数
ksort($params);
$string = $this->ToUrlParams($params); //参数进行拼接key=value&k=v
//签名步骤二:在string后加入KEY
$string = $string . "&key=".$key;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
public function ToUrlParams( $params ){
$string = '';
if( !empty($params) ){
$array = array();
foreach( $params as $key => $value ){
$array[] = $key.'='.$value;
}
$string = implode("&",$array);
}
return $string;
}
在操作完订单后,应该返回微信一个通知,让他不要再重复推送
$str=' ';
echo $str;exit;
至此,大功告成。