class
<?php
namespace app\api\controller;
/***
* jsapi支付
*/
class WxPay
{
const KEY = 'fc209d'; //api密钥
const APPID = 'wxa2c'; //appid
const SECRET = 'fc2093d'; //商户密钥
const MCH_ID = '16040'; //商户id
const CODEURL = 'https://open.weixin.qq.com/connect/oauth2/authorize?'; //微信获取code接口
const REDIRECT_URI = 'REDIRECT_URI'; //授权回调地址
const ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token?'; //获取access_token接口
const USER_INFO_URL = 'https://api.weixin.qq.com/sns/userinfo?'; //拉取用户信息接口
const NOTIFY_URL = 'NOTIFY_URL'; //支付结果通知地址
const UNI_URL = 'https://api.mch.weixin.qq.com/pay/unifiedorder'; //微信统一下单接口
protected $openid = '';
protected $notify_url = '';
protected $out_trade_no = '';
protected $total_fee = '';
public function __construct( $openid , $notify_url , $out_trade_no , $total_fee ) {
$this->openid = $openid;
$this->notify_url = $notify_url;
$this->out_trade_no = $out_trade_no;
$this->total_fee = $total_fee;
}
/*
*获取用户openid
微信文档地址:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
*/
public function getOpenid()
{
$appid = self::APPID;
$secert = self::SECRET;
$redirect_url = self::REDIRECT_URI;
$access_token_url = self::ACCESS_TOKEN_URL;
$user_info_url = self::USER_INFO_URL;
$code_url = self::CODEURL;
session_start();
//如果session中有openid,直接返回openid,否则...
if(!isset($_SESSION['openid'])){
if(!isset($_GET['code'])){
//如果没有code,给用户构建一个跳转地址获得code,最终微信会跳转回redirect_url这个地址(这里是本页面),
//跳转回来之后,会携带一个code
$url = $code_url."appid=$appid&redirect_uri=$redirect_url&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
header('Location:'.$url);
exit;
}else{
//如果存在access_token,直接使用access_token拉取用户信息
if(!isset($_SESSION['access_token'])){
$code = $_GET['code'];
$url = $access_token_url."appid=$appid&secret=$secert&code=$code&grant_type=authorization_code";
$result = file_get_contents($url);
$result = json_decode($result,true);
$_SESSION['access_token'] = $result['access_token'];
$_SESSION['openid'] = $result['openid'];
}
//拉取用户信息
$openid = $_SESSION['openid'];
$access_token = $_SESSION['access_token'];
$url1 = $user_info_url."access_token=$access_token&openid=$openid&lang=zh_CN";
//result2 为获取到的用户信息,包含openid 微信头像....
$result2 = file_get_contents($url1);
}
}
return $_SESSION['openid'];
}
/*
*生成签名
微信文档签名规则:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
*/
public function getSign($arr = [])
{
//1.参数的值为空不参与签名,过滤参数为空的key
$isNotEmptyArr = array_filter($arr);
//2.按照字典进行排序
ksort($isNotEmptyArr);
//3.组成URL键值对,使用urldecode是为了适应isNotEmptyArr中包含了中文串
$queryStr = urldecode(http_build_query($isNotEmptyArr));
//4.和KEY拼接
$stringSignTemp = $queryStr . '&key='.SELF::KEY;
//对结果进行md5之后,全部转换成大写
$sign =strtoupper(md5($stringSignTemp));
//返回签名
return $sign;
}
/**
* 返回一个带签名的数组
*/
public function setSign($arr)
{
$arr['sign'] = $this->getSign($arr);
return $arr;
}
/*
*验证签名
checkSign:需要校验的签名
mySign:生成的校验签名
返回:bool
*/
public function checkSign($checkSign,$arr)
{
$mySign = $this->getSign($arr);
return $mySign == $checkSign;
}
/**
* 微信统一下单
*微信文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
*/
public function unifiedOrder()
{
//由api文档可知 以下是必须参数
$params = [
'appid' => self::APPID,
'mch_id' => self::MCH_ID,
// 'openid' => $this->getOpenid(),
'openid' => $this->openid,
'nonce_str' => md5(time()),
'body' => 'test',
// 'out_trade_no' => uniqid(),
'out_trade_no' => $this->out_trade_no,
'total_fee' => $this->total_fee,
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
// 'notify_url' => self::NOTIFY_URL,
'notify_url' => $this->notify_url,
'trade_type' => 'JSAPI'
];
//生成带签名的数组
$params = $this->setSign($params);
//微信统一下单api需要的参数是一个xml,先把数组转换成xml
$xml = $this->ArrToXml($params);
//调用统一下单api,给微信发送以上xml,微信的返回结果也是一个xml
$result = $this->postXml(self::UNI_URL,$xml);
//为了方便看,把微信返回的订单xml转换成数组
$orderData = $this->XmlToArr($result);
// echo "<pre>";var_dump( $orderData );die;
//返回订单结果
return $orderData;
}
/***
* 给指定的url地址发送xml数据的方法
*/
public function postXml($url,$postfields)
{
$ch = curl_init();
$params[CURLOPT_URL] = $url; //请求url地址
$params[CURLOPT_HEADER] = false; //是否返回响应头信息
$params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
$params[CURLOPT_FOLLOWLOCATION] = true; //是否重定向
$params[CURLOPT_POST] = true; //请求方式
$params[CURLOPT_POSTFIELDS] = $postfields;
$params[CURLOPT_SSL_VERIFYPEER] = false; //是否跳过安全证书
$params[CURLOPT_SSL_VERIFYHOST] = false;
curl_setopt_array($ch, $params); //传入curl参数
$content = curl_exec($ch); //执行
curl_close($ch); //关闭连接
return $content;
}
/*
xml转换成数组
*/
public function XmlToArr($xml)
{
if($xml == '') return '';
libxml_disable_entity_loader(true);
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $arr;
}
/**
* 数组转换成xml
*/
public function ArrToXml($arr)
{
if(!is_array($arr) || count($arr) == 0)
return '';
$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;
}
/*
*获取微信的prepayid
*/
public function getPrePayId()
{
$orderData = $this->unifiedOrder();
return $orderData['prepay_id'];
}
/**
* 获取到微信支付的getPrePayId参数后,构建支付所需要的json参数
*/
public function getJsParams()
{
$params = [
'appId' => self::APPID,
'timeStamp' => time(),
'nonceStr' => md5(time()),
'package' => "prepay_id=".$this->getPrePayId(),
'signType' => 'MD5',
];
//注意 微信支付的签名参数是paySign 而不是sign
$params['paySign'] = $this->setSign($params)['sign'];
//返回前端支付的基本参数(json)
return json_encode($params);
}
}
// $pay = new WxPay( $openid , $notify_url , $out_trade_no , $total_fee );
// $json = $pay->getJsParams();
调用
$pay_info = new WxPay( $pb_openid , $return_url , $order_info['order_sn'] , $order_info['pay_money'] * 100 );
$pay_json = $pay_info->getJsParams();
$pay_json_arr = json_decode($pay_json ,true );
$this->success( "ok" , $pay_json_arr );
回调验签
public function wx_hall_notify() {
$testxml = file_get_contents("php://input");
// file_put_contents('pay_wx_xml.txt', $testxml);
$jsonxml = json_encode(simplexml_load_string($testxml, 'SimpleXMLElement', LIBXML_NOCDATA));
// file_put_contents('pay_wx.txt', $jsonxml);
$result = json_decode($jsonxml, true); //转成数组
if($result){
$res_verify = $this->verifySign( $result );
if( $res_verify != $result['sign'] ){
echo "fial";die;
}
//如果成功返回了
$this->charge_logic( $result );
// $out_trade_no = $result['out_trade_no'];
// if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS'){
// //
// }
}
// echo "<pre>";var_dump( $res );die;
}
public function verifySign($params, $apikey="fc4d") {
ksort($params);
$string = "";
foreach ($params as $k => $v) {
if ($k != "sign" && $v != "" && !is_array($v)) {
$string .= $k . "=" . $v . "&";
}
}
$string = $string . "key=" . $apikey;
$string = md5($string);
$result = strtoupper($string);
return $result;
}