微信支付类
class WeiXinPay
{
//----- [基本信息设置] -----
//微信公众号身份的唯一标识
protected $APPID = 'wx056xxxxxxxxa02';//填写您的appid。微信公众平台里的
protected $APPSECRET = '4c11xxxxx5451f6508a7c';
//受理商ID,身份标识
protected $MCHID = 155xxx1 ;//商户id
//商户支付密钥Key
protected $KEY = 'ASASDF542xxxxxWE45123ZX4C78F789';
//回调通知接口
protected $APPURL = 'https://wbstp.xxx.cn/xcxhome/payNotify';
//交易类型
protected $TRADETYPE = 'JSAPI';
//商品类型信息
protected $BODY = 'wx/book' ;
//微信支付类的构造函数
function __construct($openid, $outTradeNo, $totalFee)
{
$this->openid = $openid; //用户唯一标识
$this->outTradeNo = $outTradeNo; //商户订单号
$this->totalFee = $totalFee; //总价
}
//微信支付类向外暴露的支付接口
public function pay()
{
$result = $this->weixinapp();
return $result;
}
//对微信统下单接返回的支付相关 数据进行处理
private function weixinapp()
{
$unifiedorder = $this ->unifiedorder();
$parameters = array(
'appId' =>$this ->APPID, //小程序ID
'timeStamp'=> '' .time().'', //时间戳
'nonceStr' =>$this->createNoncestr(), //随机串
'package' => 'prepay_id=' . $unifiedorder['prepay_id'],//数据包
'signType' => 'MD5'//签名方式
);
$parameters['paySign']=$this->getSign($parameters);
unset($parameters['appId']);
return $parameters;
}
//请求微信统下单接口
private function unifiedorder()
{
$parameters = array(
'appid' => $this->APPID,//小程序id
'mch_id' => $this->MCHID,//商户id
'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],//终端ip
'notify_url' =>$this->APPURL, //通知地址
'nonce_str'=> $this->createNoncestr(),//随机字符串
'out_trade_no' =>$this->outTradeNo,//商户订单编号
'total_fee' =>floatval($this->totalFee), //总金额
'openid' =>$this->openid,//用户openid
'trade_type' =>$this->TRADETYPE,//交易类型
'body' =>$this->BODY, //商品信息
);
$parameters['sign'] = $this->getSign($parameters);
$xmlData = $this ->arrayToXml($parameters);
$xml_result = $this->postXmlCurl($xmlData, 'https://api.mch.weixin.qq.com/pay/unifiedorder',60);
$result = $this->xmlToArray($xml_result);
//var_dump($result);
return $result;
}
//数组转xml字符串方法
protected function arrayToXml($arr)
{
$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;
}
//xml转数组
protected function xmlToArray($xml)
{
$array_data = json_decode(json_encode(simplexml_load_string($xml,'SimpleXmlElement',LIBXML_NOCDATA)),true);
return $array_data;
}
//发送xm1请求方法
private static function postXmlCurl($xml, $url, $second = 30)
{
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $second);
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);
//post提交方式
curl_setopt($ch, CURLOPT_POST, TRUE);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch, CURLOPT_TIMEOUT, 40);
set_time_limit(0);
//运行curl
$data = curl_exec($ch);
//var_dump($data);
//返回结果
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_error($ch);
curl_close($ch);
throw new Exception("curl出错错误码$error",500);
}
}
//对要发送到微信统一下单接口的数据进行签名
protected function getSign($obj)
{
foreach ($obj as $k => $v){
$Parameters[$k] = $v;
}
//签名步骤一:按字典字排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//签名步骤二:在string后加入KEY
$String = $String. "&key=" .$this->KEY;
//签名步骤三: MD5加密
$String = md5($String);
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
return $result_;
}
//排序并格式化参数方法,签名时需要使用
protected function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap );
foreach ($paraMap as $k => $v){
if ($urlencode ){
$v = urlencode($v);
}
$buff .= $k . "=" .$v . "&";
}
$reqPar = '';
if (strlen($buff) > 0){
$reqPar = substr($buff, 0, strlen($buff)-1);
}
return $reqPar;
}
protected function createNoncestr($length = 32){
$str = '';
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol) - 1;
for ($i = 0; $i < $length; $i++) {
$str .= $strPol[rand(0, $max)];
}
return $str;
}
}
微信退款类
use think\facade\Log;
class WeiXinRefund extends WeiXinPay
{
protected $sslCertPath = __DIR__. '/cert/apiclient_cert.pem';//正书路径
protected $sslKeyPath = __DIR__.'/cert/apiclient_key.pem';//证书路径
function __construct($openid, $transactionId, $totalFee, $outRefundNo, $refundFee)
{
//初始化退款类需要的变量
$this->openid = $openid;
$this->transactionId = $transactionId;
$this->totalFee = $totalFee;
$this->outRefundNo = $outRefundNo;
$this->refundFee = $refundFee;
}
public function refund()
{
//对外暴露的退款接口
$result = $this->wxRefundApi();
return $result;
}
private function wxRefundApi()
{
//通过微信api进行退款流程
$param = array(
'appid' => $this->APPID,
'mch_id' => $this->MCHID,
'nonce_str' => $this->createNoncestr(),
'out_refund_no' => $this->outRefundNo,
'transaction_id' => $this->transactionId,
'total_fee' => $this->totalFee,
'refund_fee' => $this->refundFee,
);
$param['sign'] = $this->getsign($param);
$xmlData = $this->arrayToXml($param);
$xmlResult = $this->postXmlSSLCurl($xmlData, 'https://api.mch.weixin.qq.com/secapi/pay/refund');
$result = $this->xmlToArray($xmlResult);
return $result;
}
//需要使用证书的请求
private function postXmlSSLCurl($xml, $url, $second=30)
{
$ch = curl_init();
//超时时间
curl_setopt ($ch, CURLOPT_TIMEOUT, $second);
//这里设置代理,如果有的话
//curl setopt(Sch,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_SSLCERTTYPE,'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, $this->sslCertPath);
//默认格式为PEM,可以注释
curl_setopt($ch, CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, $this->sslKeyPath);
//post提交方式
curl_setopt($ch,CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS,$xml);
$data = curl_exec($ch);
//返回结果
if($data){
curl_close($ch);
return $data;
}else {
$error = curl_error($ch);
Log::write( 'curl出错,错误内容:'.$error) ;
curl_close($ch);
return false;
}
}
}
亲测运行可用,代码有待改进,有什么不足之处大佬们可以给点意见