You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

559 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
/**
* 配置账号信息
*/
class WxPayConfPub
{
public static $APPID;
public static $APPSECRET;
public static $MCHID;
public static $KEY;
public static $SSLCERT_PATH;
public static $SSLKEY_PATH;
//=======【基本信息设置】=====================================
//=======【异步通知url设置】===================================
//异步通知url商户根据实际开发过程设定
//const NOTIFY_URL = 'https://tiantianxinye.langye.net/customer/pay-callback';
//=======【curl超时设置】===================================
//本例程通过curl使用HTTP POST方法此处可修改其超时时间默认为30秒
const CURL_TIMEOUT = 30;
public function __construct($project_id, $merchant_id = null)
{
if ($merchant_id) {
$wechatpayAccount = \App\Models\WechatpayAccount::where("mchid", $merchant_id)->first();
} else {
$project = \App\Models\Project::with("wechatpayAccount")->find($project_id);
$wechatpayAccount = $project->wechatpayAccount;
}
self::$APPID = env("WEIXINPAY_APPID");
self::$APPSECRET = env("WEIXINPAY_APPSECRET");
self::$MCHID = $wechatpayAccount->mchid;
self::$KEY = $wechatpayAccount->key;
self::$SSLCERT_PATH = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_cert.pem";
self::$SSLKEY_PATH = env("WEIXINPAY_CERT_PUBLIC_PATH") . $wechatpayAccount->mchid . "/apiclient_key.pem";
}
}
class SDKRuntimeException extends Exception
{
public function errorMessage()
{
return $this->getMessage();
}
}
class CommonUtilPub
{
public $project_id;
public $merchant_id;
function __construct($project_id, $merchant_id = null)
{
$this->project_id = $project_id;
$this->merchant_id = $merchant_id;
}
function trimString($value)
{
$ret = null;
if (null != $value) {
$ret = $value;
if (strlen($ret) == 0) {
$ret = null;
}
}
return $ret;
}
/**
* 作用产生随机字符串不长于32位
*/
public function createNoncestr($length = 32)
{
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}
/**
* 作用:格式化参数,签名过程需要使用
*/
function formatBizQueryParaMap($paraMap, $urlencode)
{
$buff = "";
ksort($paraMap);
foreach ($paraMap as $k => $v) {
if ($urlencode) {
$v = urlencode($v);
}
//$buff .= strtolower($k) . "=" . $v . "&";
$buff .= $k . "=" . $v . "&";
}
$reqPar = "";
if (strlen($buff) > 0) {
$reqPar = substr($buff, 0, strlen($buff) - 1);
}
return $reqPar;
}
/**
* 作用:生成签名
*/
public function getSign($Obj)
{
foreach ($Obj as $k => $v) {
$Parameters[$k] = $v;
}
//签名步骤一:按字典序排序参数
ksort($Parameters);
$String = $this->formatBizQueryParaMap($Parameters, false);
//echo '【string1】'.$String.'</br>';
//签名步骤二在string后加入KEY
$KEY = (new WxPayConfPub($this->project_id, $this->merchant_id))::$KEY;
$String = $String . "&key=" . $KEY;
//echo "【string2】".$String."</br>";
//签名步骤三MD5加密
$String = md5($String);
//echo "【string3】 ".$String."</br>";
//签名步骤四:所有字符转为大写
$result_ = strtoupper($String);
//echo "【result】 ".$result_."</br>";
return $result_;
}
/**
* 作用array转xml
*/
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转为array
*/
public function xmlToArray($xml)
{
//将XML转为array
$array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $array_data;
}
/**
* 作用以post方式提交xml到对应的接口url
*/
public function postXmlCurl($xml, $url, $second = 30)
{
//初始化curl
$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);
//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);
echo "curl出错错误码:$error" . "<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
/**
* 作用使用证书以post方式提交xml到对应的接口url
*/
function postXmlSSLCurl($xml, $url, $second = 30)
{
$ch = curl_init();
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
//超时时间
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_SSLCERTTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLCERT, $WxPayConfPub::$SSLCERT_PATH);
//默认格式为PEM可以注释
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
curl_setopt($ch, CURLOPT_SSLKEY, $WxPayConfPub::$SSLKEY_PATH);
//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_errno($ch);
echo "curl出错错误码:$error" . "<br>";
echo "<a href='http://curl.haxx.se/libcurl/c/libcurl-errors.html'>错误原因查询</a></br>";
curl_close($ch);
return false;
}
}
/**
* 作用:打印数组
*/
function printErr($wording = '', $err = '')
{
print_r('<pre>');
echo $wording . "</br>";
var_dump($err);
print_r('</pre>');
}
}
/**
* 请求型接口的基类
*/
class WxpayClientPub extends CommonUtilPub
{
var $parameters;//请求参数,类型为关联数组
public $response;//微信返回的响应
public $result;//返回参数,类型为关联数组
var $url;//接口链接
var $curl_timeout;//curl超时时间
/**
* 作用:设置请求参数
*/
function setParameter($parameter, $parameterValue)
{
$this->parameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 作用设置标配的请求参数生成签名生成接口参数xml
*/
function createXml()
{
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
$this->parameters["appid"] = $WxPayConfPub::$APPID;//公众账号ID
$this->parameters["mch_id"] = $WxPayConfPub::$MCHID;//商户号
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
}
/**
* 作用post请求xml
*/
function postXml()
{
$xml = $this->createXml();
$this->response = $this->postXmlCurl($xml, $this->url, $this->curl_timeout);
return $this->response;
}
/**
* 作用使用证书post请求xml
*/
function postXmlSSL()
{
$xml = $this->createXml();
$this->response = $this->postXmlSSLCurl($xml, $this->url, $this->curl_timeout);
return $this->response;
}
/**
* 作用:获取结果,默认不使用证书
*/
function getResult()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return $this->result;
}
}
/**
* 统一支付接口类
*/
class UnifiedOrderPub extends WxpayClientPub
{
public $project_id;
public $merchant_id;
function __construct($project_id, $merchant_id = null)
{
$this->project_id = $project_id;
$this->merchant_id = $merchant_id;
//设置接口链接
$this->url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
//设置curl超时时间
$this->curl_timeout = WxPayConfPub::CURL_TIMEOUT;
parent::__construct($project_id, $merchant_id);
}
/**
* 生成接口参数xml
*/
function createXml()
{
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
try {
//检测必填参数
if ($this->parameters["out_trade_no"] == null) {
throw new SDKRuntimeException("缺少统一支付接口必填参数out_trade_no" . "<br>");
} elseif ($this->parameters["body"] == null) {
throw new SDKRuntimeException("缺少统一支付接口必填参数body" . "<br>");
} elseif ($this->parameters["total_fee"] == null) {
throw new SDKRuntimeException("缺少统一支付接口必填参数total_fee" . "<br>");
} elseif ($this->parameters["notify_url"] == null) {
throw new SDKRuntimeException("缺少统一支付接口必填参数notify_url" . "<br>");
} elseif ($this->parameters["trade_type"] == null) {
throw new SDKRuntimeException("缺少统一支付接口必填参数trade_type" . "<br>");
} elseif ($this->parameters["trade_type"] == "JSAPI" &&
$this->parameters["openid"] == NULL) {
throw new SDKRuntimeException("统一支付接口中缺少必填参数openidtrade_type为JSAPI时openid为必填参数" . "<br>");
}
$this->parameters["appid"] = $WxPayConfPub::$APPID;//公众账号ID
$this->parameters["mch_id"] = $WxPayConfPub::$MCHID;//商户号
$this->parameters["spbill_create_ip"] = $_SERVER['REMOTE_ADDR'];//终端ip
$this->parameters["nonce_str"] = $this->createNoncestr();//随机字符串
$this->parameters["sign"] = $this->getSign($this->parameters);//签名
return $this->arrayToXml($this->parameters);
} catch (SDKRuntimeException $e) {
die($e->errorMessage());
}
}
/**
* 获取prepay_id
*/
function getPrepayId()
{
$this->postXml();
$this->result = $this->xmlToArray($this->response);
return isset($this->result["prepay_id"]) ? $this->result["prepay_id"] : null;
}
}
/**
* JSAPI支付——H5网页端调起支付接口
*/
class JsApiPub extends CommonUtilPub
{
var $code;//code码用以获取openid
var $openid;//用户的openid
var $parameters;//jsapi参数格式为json
var $prepay_id;//使用统一支付接口得到的预支付id
var $curl_timeout;//curl超时时间
function __construct($project_id, $merchant_id = null)
{
//设置curl超时时间
$this->curl_timeout = WxPayConfPub::CURL_TIMEOUT;
parent::__construct($project_id, $merchant_id);
}
/**
* 作用生成可以获得code的url
*/
function createOauthUrlForCode($redirectUrl)
{
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
$urlObj["appid"] = $WxPayConfPub::$APPID;
$urlObj["redirect_uri"] = "$redirectUrl";
$urlObj["response_type"] = "code";
$urlObj["scope"] = "snsapi_base";
$urlObj["state"] = "STATE" . "#wechat_redirect";
$bizString = $this->formatBizQueryParaMap($urlObj, false);
return "https://open.weixin.qq.com/connect/oauth2/authorize?" . $bizString;
}
/**
* 作用生成可以获得openid的url
*/
function createOauthUrlForOpenid()
{
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
$urlObj["appid"] = $WxPayConfPub::$APPID;
$urlObj["secret"] = $WxPayConfPub::$APPSECRET;
$urlObj["code"] = $this->code;
$urlObj["grant_type"] = "authorization_code";
$bizString = $this->formatBizQueryParaMap($urlObj, false);
return "https://api.weixin.qq.com/sns/oauth2/access_token?" . $bizString;
}
/**
* 作用通过curl向微信提交code以获取openid
*/
function getOpenid()
{
$url = $this->createOauthUrlForOpenid();
//初始化curl
$ch = curl_init();
//设置超时
curl_setopt($ch, CURLOPT_TIMEOUT, $this->curl_timeout);
curl_setopt($ch, CURLOPT_URL, $url);
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);
//运行curl结果以json形式返回
$res = curl_exec($ch);
curl_close($ch);
//取出openid
$data = json_decode($res, true);
$this->openid = $data['openid'];
return $this->openid;
}
/**
* 作用设置prepay_id
*/
function setPrepayId($prepayId)
{
$this->prepay_id = $prepayId;
}
/**
* 作用设置code
*/
function setCode($code_)
{
$this->code = $code_;
}
/**
* 作用设置jsapi的参数
*/
public function getParameters()
{
$WxPayConfPub = new WxPayConfPub($this->project_id, $this->merchant_id);
$jsApiObj["appId"] = $WxPayConfPub::$APPID;
$timeStamp = time();
$jsApiObj["timeStamp"] = "$timeStamp";
$jsApiObj["nonceStr"] = $this->createNoncestr();
$jsApiObj["package"] = "prepay_id=$this->prepay_id";
$jsApiObj["signType"] = "MD5";
$jsApiObj["paySign"] = $this->getSign($jsApiObj);
$this->parameters = json_encode($jsApiObj);
return $this->parameters;
}
}
/**
* 响应型接口基类
*/
class WxpayServerPub extends CommonUtilPub
{
public $data;//接收到的数据,类型为关联数组
var $returnParameters;//返回参数,类型为关联数组
/**
* 将微信的请求xml转换成关联数组以方便数据处理
*/
function saveData($xml)
{
$this->data = $this->xmlToArray($xml);
}
function checkSign()
{
$tmpData = $this->data;
unset($tmpData['sign']);
$sign = $this->getSign($tmpData);//本地签名
if ($this->data['sign'] == $sign) {
return TRUE;
}
return FALSE;
}
/**
* 获取微信的请求数据
*/
function getData()
{
return $this->data;
}
/**
* 设置返回微信的xml数据
*/
function setReturnParameter($parameter, $parameterValue)
{
$this->returnParameters[$this->trimString($parameter)] = $this->trimString($parameterValue);
}
/**
* 生成接口参数xml
*/
function createXml()
{
return $this->arrayToXml($this->returnParameters);
}
/**
* 将xml数据返回微信
*/
function returnXml()
{
$returnXml = $this->createXml();
return $returnXml;
}
}
/**
* 通用通知接口
*/
class NotifyPub extends WxpayServerPub
{
}
?>