本篇主要是说了一下整合TP5的微信公众号支付。
不过由于最近TP6已经出了,小伙伴们要记得向最新的进发哦。
好了,废话不多说了。开始。
首先呢,需要引入我们封装好的类库:
同样在 extend/ 下
因为会将支付类库放在一起,于是就在extend 文件夹下创建了一个pay文件夹用来存储所有类文件。
以下内容为wxpay类的内容:
namespace pay;
class Wxpay
{
private $config =[
"appid" => "********", // 公众号ID
"mch_id" => "********", // 商户号
"notify_url" => "********", // 回调地址
"key" => "********", // 微信支付 商户秘钥KEY
];
public function index($param,$openid)
{
$order=array(
'body' => $param['body'],// 商品描述
'total_fee' => intval($param['total']*100),// 订单金额 以(分)为单位
// 'total_fee' => 1,
'out_trade_no' => $param['order_sn'],// 订单号
'trade_type' => 'JSAPI',// JSAPI公众号支付
'openid' => $openid// 获取到的openid
);
$userip = $param['userip'];
// 统一下单 获取prepay_id
$unified_order=$this->unifiedOrder($order,$userip);
// 获取当前时间戳
$time=time();
// 组合jssdk需要用到的数据
$data=array(
'appId' =>$this->config['appid'], //appid
'timeStamp' =>strval($time), //时间戳
'nonceStr' =>$unified_order['nonce_str'],// 随机字符串
'package' =>'prepay_id='.$unified_order['prepay_id'],// 预支付交易会话标识
'signType' =>'MD5' //加密方式
);
// 生成签名
$data['paySign']=$this->makeSign($data);
return $data;
}
/**
* 统一下单
* @param array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
*/
public function unifiedOrder($order,$userip)
{
$config=array(
'appid' => $this->config['appid'], //appid
'mch_id' => $this->config['mch_id'], //商户号ID
'nonce_str' => $this->getNonceStr(),
'spbill_create_ip' => $userip, //你的IP
'notify_url' => $this->config['notify_url'],
//'notify_url' =>$url
);
// 合并配置数据和订单数据
$data=array_merge($order,$config);
// 生成签名
$sign=$this->makeSign($data);
$data['sign']=$sign;
//转换成xml
$xml=$this->toXml($data);
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';//接收xml数据的文件
$header[] = "Content-type: text/xml";//定义content-type为xml,注意是数组
$ch = curl_init ($url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 兼容本地没有指定curl.cainfo路径的错误
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$response = curl_exec($ch);
if(curl_errno($ch)){
// 显示报错信息;终止继续执行
die(curl_error($ch));
}
curl_close($ch);
//转换成数组
$result=$this->toArray($response);
//dump($result);
// 显示错误信息
if ($result['return_code']=='FAIL') {
die($result['return_msg']);
}
$result['sign']=$sign;
$result['nonce_str']=$this->getNonceStr();
return $result;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
public function makeSign($data)
{
// 去空
$data=array_filter($data);
//签名步骤一:按字典序排序参数
ksort($data);
//将数组转成url形式
$string_a=http_build_query($data);
$string_a=urldecode($string_a);
//签名步骤二:在string后加入KEY
$string_sign_temp=$string_a."&key=".$this->config['key'];
//签名步骤三:MD5加密
$sign = md5($string_sign_temp);
// 签名步骤四:所有字符转为大写
$result=strtoupper($sign);
return $result;
}
/**
* 将xml转为array
* @param string $xml xml字符串
* @return array 转换得到的数组
*/
public function toArray($xml){
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $result;
}
/**
*
* 产生随机字符串,不长于32位
* @param int $length
* @return 产生的随机字符串
*/
public 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;
}
/**
* 输出xml字符
* @throws WxPayException
*/
public function toXml($data){
if(!is_array($data) || count($data) <= 0){
throw new WxPayException("数组数据异常!");
}
$xml = "<xml>";
foreach ($data as $key=>$val){
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
/**
* 验证
* @return array 返回数组格式的notify数据
*/
public function notify(){
// 获取xml
$xml=file_get_contents('php://input', 'r');
// 转成php数组
$data=$this->toArray($xml);
// 保存原sign
$data_sign=$data['sign'];
// sign不参与签名
unset($data['sign']);
$sign=$this->makeSign($data);
// 判断签名是否正确 判断支付状态
if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') {
$result=$data;
}else{
$result=false;
}
// 返回状态给微信服务器
if ($result) {
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
}
// file_put_contents("./Public/aaa.text",$result);
// echo $str;
return $result;
}
public function https_request($url, $data = null)
{
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
if (!empty($data)){
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($curl);
// var_dump($output);
curl_close($curl);
return $output;
}
}
然后就是调用方法了:
namespace app\index\controller;
use pay\Wxpay;
class Index extends Base
{
/**
*调用支付接口
*
*/
public function getorder()
{
$param = input('get.');
$pays = model('pay')->where('id',$id)->find();
$wxpay = [
'order_sn' => $pays['pay_sn'], //支付订单号
'total' => $pays['money'], //支付总额
'body' => $pays['body'], //支付说明
'userip' => $pays['userip'], //用户IP
];
$openid = $this->_user['openid'];
$pay = new Wxpay();
$data = $pay->index($wxpay,$openid);
$logUrl = "********"; //支付完成跳转地址
$this->assign([
'data'=>json_encode($data),
'logUrl'=>$logUrl,
]);
return $this->fetch();
}
}
其次是对应的调用空白页面:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>订单提交成功</title>
<style>
.info{
text-align: center;
margin-top: 40px;
}
</style>
</head>
<body>
<div class="info">支付进行中,请稍候...</div>
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script type="text/javascript">
//调用微信JS api 支付
function onBridgeReady(){
var data = {$data};
WeixinJSBridge.invoke(
'getBrandWCPayRequest', data,
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
window.location.href="{$logUrl}";
}else{
alert('支付失败');
// alert(res.err_code+res.err_desc+res.err_msg); // 显示错误信息
}
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
</script>
</body>
</html>
最后就是回调了(拿到回调在此控制器汇中进行数据库操作):
namespace app\index\controller;
use Think\Controller;
/**
* 支付回调 -- 接收并处理调整数据库
*/
class ClassName extends AnotherClass
{
private $config =[
"key" => "*******", //微信支付 商户秘钥KEY
];
/**
* notify_url接收页面
*/
public function wxpaynotify()
{
$result=$this->notify();
if ($result)
{//以下为处理数据库操作等 操作完成后输出success
echo "SUCCESS";
}
}
/**
* 一下验证类 活获取 token 等可写入至 common 公告类中
* 验证
* @return array 返回数组格式的notify数据
*/
public function notify(){
// 获取xml
$xml=file_get_contents('php://input', 'r');
// 转成php数组
$data=$this->toArray($xml);
// 保存原sign
$data_sign=$data['sign'];
// sign不参与签名
unset($data['sign']);
$sign=$this->makeSign($data);
// 判断签名是否正确 判断支付状态
if ($sign===$data_sign && $data['return_code']=='SUCCESS' && $data['result_code']=='SUCCESS') {
$result=$data;
}else{
$result=false;
}
// 返回状态给微信服务器
if ($result) {
$str='<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}else{
$str='<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[签名失败]]></return_msg></xml>';
}
// file_put_contents("./Public/aaa.text",$result);
// echo $str;
return $result;
}
/**
* 将xml转为array
* @param string $xml xml字符串
* @return array 转换得到的数组
*/
public function toArray($xml){
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$result= json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $result;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
public function makeSign($data)
{
// 去空
$data=array_filter($data);
//签名步骤一:按字典序排序参数
ksort($data);
//将数组转成url形式
$string_a=http_build_query($data);
$string_a=urldecode($string_a);
//签名步骤二:在string后加入KEY
$string_sign_temp=$string_a."&key=".$this->config['key'];
//签名步骤三:MD5加密
$sign = md5($string_sign_temp);
// 签名步骤四:所有字符转为大写
$result=strtoupper($sign);
return $result;
}
}
以上就是微信公众号支付的全部内容。
如有疑问,请评论或留言。
感谢您的查看。
2020年05月27日