关于微信,支付宝支付都做过了,但是很少有时间去写个博客,笔记啥的。话不多少,直接上代码吧!
此版本基于tp5(thinkphp5)的一个简易的微信支付类,目前可以正常支付哦,退款没有做,哈哈~~ 如下:
Pay.php(/baby/extend/wx/Pay.php)
<?php
namespace wx;
class Pay
{
/**
* pay config
* @var array
*/
private $_payCfg = array(
'appId' => 'your appId',
'appSecret' => 'your appSecret',
'mchId' => 'your mchId',#商户号
'mchSecret' => 'your 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']) ? 'xoxoxo' : $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' => 'https://www.baidu.com',//todo 你的支付回调url
'trade_type' => 'JSAPI',
'openid' => $openId
);
$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);//严格校验
//设置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;
}
}
}
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);
}
}
简单的支付类已经OK了,现在差调用层了,代码如下:
Test.php(/baby/application/index/controller/Test.php)
<?php
namespace app\index\controller;
use think\Controller;
use think\Request;
class Test extends Controller
{
public function index(){
$redirectUri = 'https://www.baidu.com/test.html';
$payObj = new \wx\Pay($redirectUri);
$authInfo = $payObj->getAuthInfo();
if(!empty($authInfo['openId'])){
$payInfo = array(
'body'=>'xoxoxoxo',
'fee'=>0.01,
'openId'=>$authInfo['openId'],
);
$payRes = $payObj->toPay($payInfo);
$this->assign('payRes',$payRes);
}
return $this->fetch('index/test');
}
//异步通知没有做,后续补充~
public function notify(){
exit('回调');
}
}
调用层的代码也已经OK了,最后就差模板了,如下:
test.html(/baby/application/index/view/index/test.html)
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>微信JSAPI支付</title>
<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="http://www.baidu.com" target="_blank" rel="external nofollow" ;
}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>
<br/>
<font color="#9ACD32"><b>该笔订单支付金额为<span style="color:#f00;font-size:50px">1分</span>钱</b></font><br/><br/>
<font color="#9ACD32"><b><span style="color:#f00;font-size:50px;margin-left:40%;">1分</span>钱也是爱</b></font><br/><br/>
<div align="center">
<button style="width:210px; height:50px; border-radius: 15px;background-color:#FE6714; border:0px #FE6714 solid; cursor: pointer; color:white; font-size:16px;" type="button" οnclick="callpay()" >剁手吧</button>
</div>
</body>
</html>
关键在微信公众号平台要配置好,说实在的,初次去配置,确实费劲,通读下微信官方文档吧!有问题@me.