tp3.2下 整合jsapi支付 无需官方下载demo

浏览:913 发布日期:2018/03/13 分类:功能实现
tp3.2整合微信jsapi支付实现功能(暂无退款)
微信公众号及商户平台的配置就不多说了!

在Thinkphp/ThinkPHP/Library/Vendor/Weixinpay 创建Weixinpay.php 文件
<?php
namespace wx;
class Pay
{
/**
* pay config
* @var array
*/
private $_payCfg = array(
'appId' => '',//公众号id
'appSecret' => '',//公众号秘钥
'mchId' => '',//商户号
'mchSecret' => '',//商户号secret
);
/**
* redirect_url
* @var
*/
private $_redirectUrl;
public function __construct($redirectUrl = NULL)
{
if (!empty($redirectUrl)) {
$this->_redirectUrl = $redirectUrl;
}
}
/**
* 通过redirectUri获取授权信息
* @return mixed
*/
public function getAuthInfo()
{
//通过appId获取code
$redirectUri = urlencode($this->_redirectUrl);
$codeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' . $this->_payCfg['appId'] . '&redirect_uri=' . $redirectUri . '&response_type=code&scope=snsapi_base&state=YQJ#wechat_redirect';

if (empty($_REQUEST['code'])) {
header('Location:' . $codeUrl);
exit;
}
//通过code换取网页授权信息
$curl = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $this->_payCfg['appId'] . '&secret=' . $this->_payCfg['appSecret'] . '&code=' . $_REQUEST['code'] . '&grant_type=authorization_code';
$res = $this->_curlGetReq($curl);
$authInfo = '';
if ($res['tag']) {
if (is_object($res['msg'])) {
$formatRes = (array)$res['msg'];
$authInfo = array(
'accessToken' => !empty($formatRes['access_token']) ? $formatRes['access_token'] : '',
'expiresIn' => !empty($formatRes['expires_in']) ? $formatRes['expires_in'] : '',
'refreshToken' => !empty($formatRes['refresh_token']) ? $formatRes['refresh_token'] : '',
'openId' => !empty($formatRes['openid']) ? $formatRes['openid'] : '',
'scope' => !empty($formatRes['snsapi_base']) ? $formatRes['snsapi_base'] : '',
);
}
}
return $authInfo;
}

/**
* 下单支付
* @param $param
* @return array|string
*/
public function toPay($param)
{
$body = empty($param['body']) ? '' : $param['body'];
$orderSn = empty($param['orderSn']) ? $this->generateOrderNum() : $param['orderSn'];
$totalFee = empty($param['fee']) ? 0.01 : $param['fee'];
$openId = empty($param['openId']) ? '' : $param['openId'];

//统一下单参数构造
$unifiedOrder = array(
'appid' => $this->_payCfg['appId'],
'mch_id' => $this->_payCfg['mchId'],
'nonce_str' => $this->getNonceStr(),
'body' => $body,
'out_trade_no' => $orderSn,
'total_fee' => $totalFee*100,
'spbill_create_ip' => $this->getClientIp(),
'notify_url' => 'http://你的域名/index.php/Home/控制器/方法/',//todo 你的支付回调url
'trade_type' => 'JSAPI',
'openid' => $openId
);
// echo 1;die;
$unifiedOrder['sign'] = $this->makeSign($unifiedOrder);

//请求数据,统一下单
$xmlData = $this->toxml($unifiedOrder);
$url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
$res = $this->postxmlCurl($url, $xmlData);
if (!$res) {
return array('status' => 0, 'msg' => "Can't connect the server");
}
$content = $this->toArr($res);

if (!empty($content) && is_array($content)) {
if (!empty($content['result_code'])) {
if ($content['result_code'] == 'FAIL') {
return array('status' => 0, 'msg' => $content['err_code'] . ':' . $content['err_code_des']);
}
}
if (!empty($content['return_code'])) {
if ($content['return_code'] == 'FAIL') {
return array('status' => 0, 'msg' => $content['return_msg']);
}
}

$time = time();
settype($time, "string");
$resData = array(
'appId' => strval($content['appid']),
'nonceStr' => strval($content['nonce_str']),
'package' => 'prepay_id=' . strval($content['prepay_id']),
'signType' => 'MD5',
'timeStamp' => $time
);
$resData['paySign'] = $this->makeSign($resData);
}
return json_encode($resData);
}

/**
* 产生随机字符串,不长于32位
* @param int $length
* @return string
*/
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;
}
/**
* 生成签名
* @param $unifiedOrder
* @return string
*/
public function makeSign($unifiedOrder)
{
//签名步骤一:按字典序排序参数
ksort($unifiedOrder);
$string = $this->toUrlParams($unifiedOrder);
//签名步骤二:在string后加入KEY
$string = $string . "&key=" . $this->_payCfg['mchSecret'];
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}

/**
* 格式化参数格式化成url参数
* @param $unifiedOrder
* @return string
* @internal param $unifiedOrder
*/
public function toUrlParams($unifiedOrder)
{
$buff = "";
foreach ($unifiedOrder as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}

/**
* 输出xml字符
* @param $unifiedOrder
* @return string
*/
public function toxml($unifiedOrder)
{
if (!is_array($unifiedOrder) || count($unifiedOrder) <= 0) exit('数组异常');

$xml = "<xml>";
foreach ($unifiedOrder as $key => $val) {
if (is_numeric($val)) {
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
} else {
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
}
}
$xml .= "</xml>";
return $xml;
}
/**
* 将xml转为array
* @param $xml
* @return mixed
*/
public function toArr($xml)
{
//将xml转为array,禁止引用外部xml实体
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimplexmlElement', LIBxml_NOCDATA)), true);
}

/**
* curl get request
* @param $url
* @return array
*/
public function _curlGetReq($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$res = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);

if ($res === false) {
return array('tag' => false, 'msg' => $error);
}
return array(
'tag' => true,
'msg' => json_decode($res)
);
}

/**
* 以post方式提交xml到对应的接口url
* @param $xml
* @param $url
* @return mixed
*/
public function postxmlCurl($url, $xml)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
//如果有配置代理这里就设置代理
curl_setopt($ch, CURLOPT_URL, $url);
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);//严格校验
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);//严格校验2
//设置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) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
curl_close($ch);
exit("curl出错,错误码:$error");
}
}

/**
* 获取ip
* @return bool
*/
public function getClientIp()
{
$ip = false;
if (!empty($_SERVER["HTTP_CLIENT_IP"]) && '127.0.0.1' != $_SERVER["HTTP_CLIENT_IP"]) {
$ip = $_SERVER["HTTP_CLIENT_IP"];
}
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ips = explode(", ", $_SERVER['HTTP_X_FORWARDED_FOR']);
if ($ip) {
array_unshift($ips, $ip);
$ip = FALSE;
}
for ($i = 0; $i < count($ips); $i++) {
//echo $ips[$i].'<br>';
if (!preg_match('/^(10|172\.16|192\.168)\./', $ips[$i])) {
$ip = $ips[$i];
break;
}
}
}
//@author jzzmly 2015-8-11 验证从HTTP变量里拿到的ip信息,防止伪造非法字符攻击
return (empty($ip) || preg_match('/[^0-9^\.]+/', $ip)) ? $_SERVER['REMOTE_ADDR'] : $ip;
//return ($ip ? $ip : $_SERVER['REMOTE_ADDR']);
}

/**
* 生成16位订单号
* @return string
*/
public function generateOrderNum()
{
return $order_number = date('Ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}


/**
* 验证
* @return array 返回数组格式的notify数据
*/
public function notify($res) //此处$res是控制器中传递过来的微信返回值
{
// 获取xml
// $xml=$res;
// 转成php数组
$data=$this->xml_to_array($res);
// 保存原sign
$data_sign=$data['sign'];
// sign不参与签名
unset($data['sign']);
ksort($data);// 对数据进行排序
$str = $this->ToUrlParams($data);//对数组数据拼接成key=value字符串
$sign = strtoupper(md5($str)); //再次生成签名,与$data_sign比较
// 判断签名是否正确 判断支付状态
if ($data_sign=$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>';
}
echo $str;
return $result;
}

/**
* 将xml转为array
* @param string $xml
* return array
*/
public function xml_to_array($xml){
if(!$xml){
return false;
}
//将xml转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$data = json_decode(json_encode(simplexml_load_string($xml, 'SimplexmlElement', LIBxml_NOCDATA)), true);
return $data;
}

}

创建相应的控制器:
<?php
namespace Home\Controller;
class OperateController extends Controller {

public function payment(){
if(IS_POST){
//接收前一个页面用户提交的数据
$post = I('post.');
//此处最好将用户提交数据存入数据库
}
/*支付参数配置*/
Vendor('Weixinpay.Weixinpay');
$redirectUri = 'http://你的域名/index.php/Home/Operate/payment';
$payObj=new \wx\Pay($redirectUri);
$authInfo = $payObj->getAuthInfo();
$fee = $total['fee'];//此处费用数从数据表中查出
if(!empty($authInfo['openId'])){
$payInfo = array(
'body'=>'自定义',
'fee'=>0.01,//方便测试使用0.01 上线带入$fee
'orderSn'=>"订单号",//自定义订单号
'openId'=>$authInfo['openId'],
);
//此处我将用户openid存入数据库后续可能用到
$m = M('表名');
$m -> openid = $authInfo['openId'];
$where = "我以订单号为条件";
$m -> where($where) -> save();
$payRes = $payObj->toPay($payInfo);//支付所需参数
}
$assign=array(
'data'=>$data,//此处data 是我从数据库查出数据回显前台用于用户订单确认页面
'payRes' => $payRes
);
$this->assign($assign);
$this -> display();
}

public function notify(){
$res = post_data();//获取微信返回值
// 导入微信支付sdk
Vendor('Weixinpay.Weixinpay');
$wxpay=new \wx\Pay();
$result=$wxpay->notify($res);
if ($result){
// 验证成功 修改数据库的订单状态等 $result['out_trade_no']为订单id

}
}
}

此函数返回xml数据
function post_data(){
$receipt = $_REQUEST;
if($receipt==null){
$receipt = file_get_contents("php://input");
if($receipt == null){
$receipt = $GLOBALS['HTTP_RAW_POST_DATA'];
}
}
return $receipt;
}

前台页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>订单确认</title>
<link rel="stylesheet" type="text/css" href="__HOME__/css/reset.css"/>
<link rel="stylesheet" type="text/css" href="__HOME__/css/yygh.css"/>
<script src="__HOME__/js/jquery.min.js"></script>
<script type="text/javascript">
function jsApiCall()
{
WeixinJSBridge.invoke(
'getBrandWCPayRequest',{$payRes},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok"){
// alert("支付成功!");
window.location.href="首页";
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
alert("用户取消!");
}else{
alert("支付失败!");
}
}
);
}

function callpay()
{
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', jsApiCall, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', jsApiCall);
document.attachEvent('onWeixinJSBridgeReady', jsApiCall);
}
}else{
jsApiCall();
}
}
</script>
</head>
<body class="over">
<header class="dl-head">
<h2 class="head-title">订单确认</h2>
</header>
<foreach name="data" item="data">
<div class="zxyy-btn-box">
<input value="稍后支付" type="button"/>
<input value="立即支付" type="button" onclick="callpay()" class="zxyy-btn-lj" />
</div>
</foreach>
</body>
</html>
评论( 相关
后面还有条评论,点击查看>>