微信支付,退款,企业转账类–参数配置可直接使用
封装的比较垃圾,各位大师请勿吐槽
<?php
/**
* 微信支付
*/
class WxPay
{
protected $values = [];//参数集合
const APPID = ''; //绑定支付的APPID
const MCHID = ''; //商户号
const KEY = ''; //商户支付密钥
const APPSECRET = ''; //公众帐号secert(仅JSAPI支付的时候需要配置
#证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要,可登录商户平台下载
#API证书下载地址:https://pay.weixin.qq.com/index.php/account/api_cert,下载之前需要安装商户操作证书)
const SSLCERT_PATH = ROOT_PATH.'/cert/apiclient_cert.pem';
const SSLKEY_PATH = ROOT_PATH.'/cert/apiclient_key.pem';
function __construct()
{
$this->values = [];
}
public function wxPay($orderId='')
{
if (null == $orderId || empty($orderId)) {
exit("<script> alert('该订单不存在!');history.back(); </script>");
}
do {
$order_sn = order_sn();
} while ( Db::table('orders')->where('pay_order_num',$order_sn)->value('id') );
Db::table('orders')->where('id',$orderId)->update(['pay_order_num'=>$order_sn]);
$orderInfo = Db::table('orders')->where('id',$orderId)->field('id,pay_order_num,real_price,uid')->find();
$openid = Db::table('users')->where('id',$orderInfo['uid'])->value('openid');
$setAttr = [
'body' =>'微信季利亚',
'openid' =>$openid,
'appid' =>self::APPID,
'out_trade_no' =>$orderInfo['pay_order_num'],
'total_fee' => $orderInfo['real_price'] * 100,
'notify_url' =>WEB_URL.'/home/notify/wechatNotify',
'mch_id' => self::MCHID,
'spbill_create_ip' => self::clientIp(),
'nonce_str' => self::getNonceStr(),
'trade_type' =>'JSAPI'
];
$this->values = $setAttr; //配置赋给values属性
$sign = $this->MakeSign(); //获取签名
$this->values['sign'] = $sign;
$xmlstr = $this->ToXml(); //生成微信请求xml数据格式
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //请求url参数
$dataxml =self::postXmlCurl($xml,$url);//传参调用curl请求
$objectxml = $this->xmlToArray($dataxml);
$prepay_id = '';
if(isset($objectxml["prepay_id"])){
$prepay_id = $objectxml["prepay_id"];
}
$timeStamp = time();
$data = [
'appId' =>self::APPID,
'timeStamp' => "$timeStamp",
'nonceStr' =>self::getNonceStr(),
'package' => "prepay_id=$prepay_id",
'signType' => "MD5"
];
$this->values = $data;
$data['paySign'] = $this->MakeSign();
$this->assign('jsApiParameters',json_encode($data));
return view('wechat/wxchatpay');
}
/**微信退款
*@param $out_trade_no 订单号
*/
public function refund($out_trade_no='')
{
if(!$out_trade_no){
throw new \Exception("订单号出错", 1);
}
$order = db('orders')->where('pay_order_num',$out_trade_no)->where('status',7)->find();
if (empty($order)) {
throw new \Exception("订单不存在", 1);
}
$config = [
'out_trade_no'=> $out_trade_no,
'refund_fee' =>(int)$order['return_price'] * 100,
'total_fee' =>(int)$order['real_price']*100,
'out_refund_no'=>self::MCHID.date("YmdHis"),
'appid' =>self::APPID,
'mch_id' =>self::MCHID,
'nonce_str' =>self::getNonceStr()
];
$this->values = $config;
$sign = $this->MakeSign(); //生成sign签名
$this->values['sign'] = $sign;
$xmlstr = $this->ToXml(); //生成微信请求xml数据格式
$url = "https://api.mch.weixin.qq.com/secapi/pay/refund"; //退款url
$dataxml = self::postXmlCurl($xmlstr,$url,true);//传参调用curl请求
$objectxml = $this->xmlToArray($dataxml);
if($objectxml['return_code'] =='SUCCESS' && $objectxml['result_code'] =='SUCCESS'){
return 1;
}else{
throw new \Exception($objectxml['return_msg'], 1);
}
}
//微信提现
public function Withdrawals($orderCode,$openid,$money){
if(!$orderCode || !$openid || $money<1){
return 0;
}
$config = [
'amount'=> $amount,
'check_name' =>'NO_CHECK',
'desc' =>'季利亚金币提现',
'mch_appid'=>self::APPID,
'mchid' =>self::MCHID,
'nonce_str' =>self::getNonceStr(),
'openid' =>$openid,
'partner_trade_no'=>$orderCode,
'spbill_create_ip'=>self::clientIp
];
$this->values = $config;
$sign = $this->MakeSign(); //生成sign签名
$this->values['sign'] = $sign;
$xmlstr = $this->ToXml(); //生成微信请求xml数据格式
//接口地址
$url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
$dataxml = self::postXmlCurl($xmlstr,$url,true);//传参调用curl请求
$objectxml = $this->xmlToArray($dataxml);//将微信返回的XML 转换成数组
if($objectxml['return_code'] =='SUCCESS' && $objectxml['result_code'] =='SUCCESS'){
return 1;
}else{
throw new \Exception($objectxml['return_msg'], 1);
}
}
/**
* 作用:将xml转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
*
* 产生随机字符串,不长于32位
* @param int $length
* @return 产生的随机字符串
*/
public static 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;
}
/**
* 生成签名
* @return 签名,本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
*/
protected function MakeSign()
{
//签名步骤一:按字典序排序参数
ksort($this->values);
$string = $this->ToUrlParams(ksort($this->values));
//签名步骤二:在string后加入KEY
$string = $string . "&key=".self::KEY;
//签名步骤三:MD5加密
$string = md5($string);
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
/**
* 格式化参数格式化成url参数
*/
public function ToUrlParams()
{
$buff = "";
foreach ($this->values as $k => $v)
{
if($k != "sign" && $v != "" && !is_array($v)){
$buff .= $k . "=" . $v . "&";
}
}
$buff = trim($buff, "&");
return $buff;
}
/**
* 输出xml字符
**/
public function ToXml()
{
if(!is_array($this->values)
|| count($this->values) <= 0)
{
throw new Exception('数组异常', 1);
}
$xml = "<xml>";
foreach ($this->values as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
#获取客户端ip
private static function clientIp()
{
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}elseif(isset($_SERVER['HTTP_CLIENT_IP'])){
$ip = $_SERVER['HTTP_CLIENT_IP'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];
}
/**
* 以post方式提交xml到对应的接口url
*
* @param string $xml 需要post的xml数据
* @param string $url url
* @param bool $useCert 是否需要证书,默认不需要
* @param int $second url执行超时时间,默认30s
* @throws WxPayException
*/
private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
{
// halt($url);
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
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);
if($useCert == true){
//设置证书
//使用证书:cert 与 key 分别属于两个.pem文件
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT,self::SSLCERT_PATH);
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY,self::SSLKEY_PATH);
}
//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);
throw new \Exception("curl出错,错误码:$error");
}
}
}