thinkphp微信H5支付整合
/app/index/model/Wxh5.php
<?php
namespace app\index\model;
use think\Db;
use think\Validate;
use think\Loader;
use think\Model;
class Wxh5 extends model
{
public function __construct() {
$this->config = $this->wxConfig();
}
/**
* 获取秘钥配置
* @return [type] 数组
*/
public function wxConfig() {
$config = [
"appid" => set('appid',3), // 开放平台或商户平台APPID
"mch_id" => set('mch_id',3), // 商户平台 商户号
"key" => set('key',3), // 商户平台 秘钥KEY
"TOKEN" => "", // 此参数非必传 有的前端在jsapi 支付时会要求返回signature 参数 此参数即为此准备
];
return $config;
}
public function index($param,$t="")
{
if ($t) {
$order = [
'out_trade_no' => $param['out_trade_no'],
'out_refund_no' => $param['out_refund_no'],
'total_fee' => intval($param['total_fee']*100),
'refund_fee' => intval($param['total_fee']*100)
];
// 退款
$unified_order=$this->curl_post_ssl($order);
}else{
$order = [
'out_trade_no' => $param['out_trade_no'],// 订单号
'total_fee' => intval($param['total_fee']*100),// 订单金额 以(分)为单位
'body' => $param['body'],// 商品描述
'notify_url' => $param['notify_url'], //回调地址
'spbill_create_ip' => $param['spbill_create_ip'], //对应IP
'trade_type' => $param['trade_type'] //对应支付类型
];
#统一下单 获取prepay_id
$unified_order=$this->unifiedOrder($order);
}
#获取当前时间戳
$data = $unified_order;
return $data;
}
/**
* 统一下单
* @param array $order 订单 必须包含支付所需要的参数 body(产品描述)、total_fee(订单金额)、out_trade_no(订单号)、product_id(产品id)、trade_type(类型:JSAPI,NATIVE,APP)
*/
public function unifiedOrder($order)
{
$config =[
'appid' => $this->config['appid'], //appid
'mch_id' => $this->config['mch_id'], //商户号ID
'nonce_str' => $this->getNonceStr(),
];
# 合并配置数据和订单数据
$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);
#显示错误信息
if ($result['return_code']=='FAIL')
{
die($result['return_msg']);
}
// var_dump($result);
$result['sign']=$sign;
$result['nonce_str']=$this->getNonceStr();
return $result;
}
/**
* 微信支付退款发起请求
*/
public function curl_post_ssl($order, $second=30,$aHeader=array()){
$config =[
'appid' => $this->config['appid'], //appid
'mch_id' => $this->config['mch_id'], //商户号ID
'nonce_str' => $this->getNonceStr(),
];
# 合并配置数据和订单数据
$data=array_merge($order,$config);
# 生成签名
$sign=$this->makeSign($data);
$data['sign']=$sign;
#转换成xml
$xml=$this->toXml($data);
// return $data;
$url = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
$ch = curl_init();
//超时时间
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl_setopt($ch,CURLOPT_PROXY, '8.8.8.8');
//curl_setopt($ch,CURLOPT_PROXYPORT, 8080);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
//设置header
curl_setopt($ch, CURLOPT_HEADER, FALSE);
//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLCERT, 'cert' . DIRECTORY_SEPARATOR . 'apiclient_cert.pem');
curl_setopt($ch, CURLOPT_SSLKEY, 'cert' . DIRECTORY_SEPARATOR . 'apiclient_key.pem');
//post提交方式
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
$data = curl_exec($ch);
$result=$this->toArray($data);
//返回结果
if ($result) {
curl_close($ch);
return $result;
} else {
return false;
}
}
/**
* 生成签名
* @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 结构的字符串
* @param array $arr 要转换的数组
* @param int $level 节点层级, 1 为 Root.
* @return string XML 结构的字符串
*/
public function array2xml($arr, $level = 1) {
$s = $level == 1 ? "<xml>" : '';
foreach($arr as $tagname => $value) {
if (is_numeric($tagname)) {
$tagname = $value['TagName'];
unset($value['TagName']);
}
if(!is_array($value)) {
$s .= "<{$tagname}>".(!is_numeric($value) ? '<![CDATA[' : '').$value.(!is_numeric($value) ? ']]>' : '')."</{$tagname}>";
} else {
$s .= "<{$tagname}>" . $this->array2xml($value, $level + 1)."</{$tagname}>";
}
}
$s = preg_replace("/([\x01-\x08\x0b-\x0c\x0e-\x1f])+/", ' ', $s);
return $level == 1 ? $s."</xml>" : $s;
}
/**
* 将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)
{
$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>';
}
return $result;
}
}
?>
下单
use app\index\model\Wxh5;
$title = '充值';
$total_fee = 0.01;
$out_trade_no=date('YmdHis').rand(1000,9999);
$notify_url = http().$_SERVER['HTTP_HOST'].url('index/chongzhi/notifywx'); //回调
$wxpay = new Wxh5();
$trade_type = 'MWEB';
$date = [
'out_trade_no' => $out_trade_no,
'total_fee' => $total_fee,
'body' => $title,
'description' => $title,
'spbill_create_ip' => $_SERVER["REMOTE_ADDR"],
'notify_url' => $notify_url,
'trade_type' => $trade_type,
];
$data['title'] = $title;
$data['id'] = $out_trade_no;
$data['zhifu'] = '微信';
$data['tid'] = 0;
$data['model'] = '';
$data['trade_no'] = 0;
$data['status'] = 0;
$data['jiage'] = $total_fee;
$data['add_time'] = time();
$data['errorcode'] = 0;
$data['uid'] = input('uid');
$data['ip'] = $_SERVER["REMOTE_ADDR"];
Db::name('dingdan')->insert($data);
$res = $wxpay->index($date);
$url = $res['mweb_url'];
echo '<meta http-equiv="refresh" content="0;url='.$url.'">';