854 行
38 KiB
PHP
可执行文件
854 行
38 KiB
PHP
可执行文件
<?php
|
||
/**
|
||
* Created by PhpStorm.
|
||
* User: Administrator
|
||
* Date: 2019-10-21
|
||
* Time: 13:53
|
||
*/
|
||
|
||
namespace Pay\Library\WeChat;
|
||
|
||
//[1.5.22]
|
||
|
||
/**
|
||
* 微信打款支付
|
||
* Class WxPay
|
||
* @package app\Pay\Library\WeChat
|
||
*/
|
||
class WxRemitPay
|
||
{
|
||
private $config; // 微信支付配置
|
||
// static private $wxQueryUrl = "https://api.mch.weixin.qq.com/sandboxnew/mmpaymkttransfers/gettransferinfo"; // 查询企业付款接口url
|
||
// static private $remitPayUrl = "https://api.mch.weixin.qq.com/sandboxnew/mmpaymkttransfers/promotion/transfers";// 微信企业付款到零钱接口url
|
||
static private $wxQueryUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo"; // 查询企业付款接口url
|
||
static private $remitPayUrl = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 微信企业付款到零钱接口url
|
||
static private $publicKeyUrl = "https://fraud.mch.weixin.qq.com/risk/getpublickey"; // 获取RSA加密公钥API
|
||
static private $payBankUrl = "https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank"; // 企业付款到银行卡API
|
||
static private $queryBankUrl = "https://api.mch.weixin.qq.com/mmpaysptrans/query_bank"; // 查询企业付款到银行卡API
|
||
static private $payModel = "1";
|
||
static private $errmsg = "";
|
||
static private $wxReturnData = "";
|
||
static private $cancelMsg = "订单超时,并已撤销";
|
||
static private $cancelerrMsg = "订单超时,撤销失败,请勿支付,请检查微信支付证书配置";
|
||
|
||
protected $reTestNum = 0;
|
||
/**
|
||
* 构造方法
|
||
* WxPay constructor.
|
||
* @param $config
|
||
*/
|
||
public function __construct($config)
|
||
{
|
||
$this->config = $config;
|
||
}
|
||
|
||
/** 企业付款到零钱API接口
|
||
* @param $order_no
|
||
* @param $code
|
||
* @param $total_fee
|
||
*/
|
||
public function remitPay($order_no, $openid, $total_fee, $body)
|
||
{
|
||
$url = self::$remitPayUrl;
|
||
//创建需要的参数
|
||
$params = [
|
||
"mch_appid" => $this->config['mch_appid'], // 商户账号appid
|
||
"mchid" => $this->config['mch_id'], // 商户号
|
||
"nonce_str" => $this->createNoncestr(), // 随机字符串
|
||
"partner_trade_no" => $order_no, // 商户订单号
|
||
"openid" => $openid, // 用户openid
|
||
"check_name" => "NO_CHECK", // NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
|
||
"amount" => $total_fee, // 企业付款金额,单位为分
|
||
"desc" => $body, // 企业付款备注,必填。注意:备注中的敏感词会被转成字符*
|
||
"spbill_create_ip" => $_SERVER['REMOTE_ADDR'], // Ip地址
|
||
];
|
||
|
||
$params["sign"] = $this->makeWxSign($params); // 签名
|
||
$result = $this->postXmlCurlCancel($this->toXml($params), $url);
|
||
|
||
$prepay = $this->fromXml($result);
|
||
|
||
if (!empty($prepay)) {
|
||
// 微信通信标识
|
||
if (isset($prepay['return_code']) && $prepay['return_code'] == 'SUCCESS') {
|
||
// 业务结果
|
||
if (isset($prepay['result_code'])) {
|
||
// 业务结果返回成功
|
||
if ($prepay['result_code'] == 'SUCCESS') {
|
||
self::$wxReturnData = array(
|
||
'partner_trade_no' => $prepay['partner_trade_no'],
|
||
'payment_no' => $prepay['payment_no'],
|
||
'payment_time' => $prepay['payment_time'],
|
||
);
|
||
return true;
|
||
} else {
|
||
$this->doLogs($this->toXml($prepay));
|
||
if ($prepay['err_code'] == 'SYSTEMERROR') {
|
||
// 请务必用原商户订单号重试,或通过查询接口确认此次付款的结果
|
||
return $this->getPayState($order_no);// 此处使用的是查询订单接口
|
||
} else {
|
||
self::$errmsg = isset($prepay['err_code_des']) ? $prepay['err_code_des'] : '付款失败!';
|
||
return false;
|
||
}
|
||
}
|
||
} else {
|
||
self::$errmsg = '微信api接口返回值已修改';
|
||
return false;
|
||
}
|
||
} else if (isset($prepay['return_code']) && $prepay['return_code'] == 'FAIL') {
|
||
$this->doLogs($this->toXml($prepay));
|
||
$this->doLogs($this->toXml($this->config));
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '付款失败。';
|
||
return false;
|
||
} else {
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '付款失败';
|
||
return false;
|
||
}
|
||
}
|
||
self::$errmsg = "付款失败,微信api接口无任何返回值";
|
||
return false;
|
||
}
|
||
|
||
/** 查询订单是否支付
|
||
* @param $order_no
|
||
*/
|
||
public function getPayState($order_no)
|
||
{
|
||
//暂停一秒查询
|
||
$res = $this->searchWxOrder($order_no);
|
||
// 微信通信标识
|
||
if (isset($res['return_code'])) {
|
||
if ($res['return_code'] == "FAIL") {
|
||
self::$errmsg = isset($res['return_msg']) ? $res['return_msg'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
} else {
|
||
// 微信通信标识SUCCESS
|
||
if (isset($res['result_code']) && $res['result_code'] == 'SUCCESS') {
|
||
// 业务结果 SUCCESS
|
||
if (isset($res['status'])) {
|
||
switch ($res['status']) {
|
||
case "SUCCESS":
|
||
// 转账成功
|
||
self::$wxReturnData = array(
|
||
'partner_trade_no' => $res['partner_trade_no'],
|
||
'payment_no' => $res['detail_id'],
|
||
'payment_time' => $res['payment_time'],
|
||
);
|
||
return true;
|
||
break;
|
||
case "FAILED":
|
||
// 转账失败
|
||
self::$errmsg = isset($res['reason']) ? $res['reason'] : "打款失败";
|
||
return false;
|
||
break;
|
||
case "PROCESSING":
|
||
// 处理中
|
||
// 继续查询
|
||
$time = get_runtime();
|
||
if ($time < 20) {
|
||
sleep(2);
|
||
return $this->getPayState($order_no);
|
||
} else {
|
||
// 将订单标记为处理中订单(让商家或者客户刷新订单重试)
|
||
self::$errmsg = "微信打款处理中,请稍后查询打款结果";
|
||
return 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
if (isset($res['err_code'])) {
|
||
switch ($res['err_code']) {
|
||
case "CA_ERROR":
|
||
self::$errmsg = "请求未携带证书,或请求携带的证书出错";
|
||
return false;
|
||
break;
|
||
case "SIGN_ERROR":
|
||
self::$errmsg = "商户签名错误";
|
||
return false;
|
||
break;
|
||
case "FREQ_LIMIT":
|
||
self::$errmsg = "受频率限制";
|
||
return false;
|
||
break;
|
||
case "XML_ERROR":
|
||
self::$errmsg = "请求的xml格式错误,或者post的数据为空";
|
||
return false;
|
||
break;
|
||
case "SYSTEMERROR":
|
||
$time = get_runtime();
|
||
if ($time < 20) {
|
||
sleep(2);
|
||
return $this->getPayState($order_no);
|
||
} else {
|
||
// 将订单标记为处理中订单(让商家或者客户刷新订单重试)
|
||
self::$errmsg = "微信打款处理中,请稍后查询打款结果";
|
||
return 0;
|
||
}
|
||
break;
|
||
case "NOT_FOUND":
|
||
self::$errmsg = "1、指定单号数据不存在 2、指定单号数据不存在,单据查询超过有效期";
|
||
return false;
|
||
break;
|
||
}
|
||
}
|
||
self::$errmsg = isset($res['err_code_des']) ? $res['err_code_des'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
self::$errmsg = isset($res['err_code_des']) ? $res['err_code_des'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* @param $out_trade_no
|
||
*/
|
||
public function searchWxOrder($out_trade_no)
|
||
{
|
||
$payModel = self::$payModel;
|
||
$params = [
|
||
"nonce_str" => $this->createNoncestr(),
|
||
"partner_trade_no" => $out_trade_no
|
||
];
|
||
if ($payModel) {
|
||
$params['appid'] = $this->config['mch_appid'];
|
||
$params['mch_id'] = $this->config['mchid'];
|
||
} else {
|
||
$params['appid'] = $this->config['xcx_appid'];
|
||
$params['mch_id'] = $this->config['xcx_mer_id'];
|
||
}
|
||
$params['sign'] = $this->makeWxSign($params);
|
||
$url = self::$queryBankUrl; // 微信企业付款查询url
|
||
$xml = $this->toXml($params);//转换为xml
|
||
$response = $this->postXmlCurlCancel($xml, $url, 30);
|
||
$response = json_decode(json_encode(simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||
return $response;
|
||
}
|
||
|
||
/**
|
||
* 获取微信企业付款到银行卡公钥
|
||
* @return bool
|
||
*/
|
||
public function getWxPublicKey()
|
||
{
|
||
$url = self::$publicKeyUrl;
|
||
//创建需要的参数
|
||
$params = [
|
||
"mch_id" => $this->config['mchid'], // 商户号
|
||
"nonce_str" => $this->createNoncestr(), // 随机字符串
|
||
"sign_type" => 'MD5' // 商户订单号
|
||
];
|
||
$params["sign"] = $this->makeWxSign($params); // 签名
|
||
$result = $this->postXmlCurlCancel($this->toXml($params), $url);
|
||
$prepay = $this->fromXml($result);
|
||
//pre($prepay);
|
||
if (!empty($prepay)) {
|
||
// 微信通信标识
|
||
if (isset($prepay['return_code']) && $prepay['return_code'] == 'SUCCESS') {
|
||
// 业务结果
|
||
if (isset($prepay['result_code'])) {
|
||
// 业务结果返回成功
|
||
if ($prepay['result_code'] == 'SUCCESS') {
|
||
self::$wxReturnData = array(
|
||
'pub_key' => $prepay['pub_key']
|
||
);
|
||
return true;
|
||
} else {
|
||
$this->doLogs($this->toXml($prepay));
|
||
self::$errmsg = isset($prepay['err_code_des']) ? $prepay['err_code_des'] : '公钥获取失败';
|
||
return false;
|
||
}
|
||
} else {
|
||
self::$errmsg = '微信api接口返回值已修改';
|
||
return false;
|
||
}
|
||
} else if (isset($prepay['return_code']) && $prepay['return_code'] == 'FAIL') {
|
||
$this->doLogs($this->toXml($prepay));
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '公钥获取失败。';
|
||
return false;
|
||
} else {
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '公钥获取失败';
|
||
return false;
|
||
}
|
||
}
|
||
self::$errmsg = "微信企业付款公钥获取失败";
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 调取微信付款到银行卡
|
||
* @param $order_no
|
||
* @param $userData
|
||
* @param $total_fee
|
||
* @param $body
|
||
* @return bool
|
||
*/
|
||
public function bankCardPay($order_no, $userData, $total_fee, $body)
|
||
{
|
||
$url = self::$payBankUrl;
|
||
//创建需要的参数
|
||
$enc_bank_no = $this->publicEncrypt($userData['enc_bank_no']);
|
||
$enc_true_name = $this->publicEncrypt($userData['enc_true_name']);
|
||
$bank_code = $userData['bank_code'];
|
||
$params = [
|
||
"mch_id" => $this->config['mchid'], // 商户号
|
||
"nonce_str" => $this->createNoncestr(), // 随机字符串
|
||
"partner_trade_no" => $order_no, // 商户订单号
|
||
"enc_bank_no" => $enc_bank_no, // 收款方银行卡号(采用标准RSA算法,公钥由微信侧提供)
|
||
"enc_true_name" => $enc_true_name, // 收款方用户名(采用标准RSA算法,公钥由微信侧提供)
|
||
"bank_code" => $bank_code, // 银行卡所在开户行编号
|
||
"amount" => $total_fee, // 企业付款金额,单位为分
|
||
"desc" => $body, // 企业付款备注,必填。注意:备注中的敏感词会被转成字符*
|
||
];
|
||
$params["sign"] = $this->makeWxSign($params); // 签名
|
||
$result = $this->postXmlCurlCancel($this->toXml($params), $url);
|
||
$prepay = $this->fromXml($result);
|
||
return $this->bankCardPayHandle($prepay, $params, $url);
|
||
}
|
||
|
||
/*
|
||
* 处理微信付款到银行卡返回值
|
||
* */
|
||
private function bankCardPayHandle($prepay, $params, $url)
|
||
{
|
||
$order_no = $params['partner_trade_no'];
|
||
if (!empty($prepay)) {
|
||
// 微信通信标识
|
||
if (isset($prepay['return_code']) && $prepay['return_code'] == 'SUCCESS') {
|
||
// 业务结果
|
||
if (isset($prepay['result_code'])) {
|
||
// 业务结果返回成功
|
||
if ($prepay['result_code'] == 'SUCCESS') {
|
||
self::$wxReturnData = array(
|
||
'partner_trade_no' => $prepay['partner_trade_no'],
|
||
'payment_no' => $prepay['payment_no'],
|
||
'cmms_amt' => $prepay['cmms_amt'],
|
||
);
|
||
return true;
|
||
} else {
|
||
$this->doLogs($this->toXml($prepay));
|
||
if (isset($prepay['err_code_des'])) {
|
||
$err_code_des = $prepay['err_code_des'] . 'err_code:' . $prepay['err_code'];
|
||
} else {
|
||
$err_code_des = '';
|
||
}
|
||
switch ($prepay['err_code']) {
|
||
case 'SYSTEMERROR':
|
||
// 请务必用原商户订单号重试,或通过查询接口确认此次付款的结果
|
||
return $this->getCardPayState($order_no); // 此处使用的是查询订单接口
|
||
break;
|
||
case 'INVALID_REQUEST':
|
||
// 无效的请求,商户系统异常导致,商户权限异常、证书错误、频率限制等
|
||
// 使用原单号以及原请求参数重试
|
||
if($this->reTestNum>3){
|
||
self::$errmsg = $err_code_des ? $err_code_des : '无效的请求!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
}else{
|
||
$this->reTestNum++;
|
||
}
|
||
return $this->retryBankCardPay($params, $url, 1, 0);
|
||
break;
|
||
case 'FREQUENCY_LIMITED':
|
||
// 超过每分钟600次的频率限制 稍后用原单号重试
|
||
return $this->retryBankCardPay($params, $url, 5, 1);
|
||
break;
|
||
case 'SIGNERROR':
|
||
// 签名错误
|
||
self::$errmsg = $err_code_des ? $err_code_des : '签名错误!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'AMOUNT_LIMIT':
|
||
// 超额;已达到今日付款金额上限或已达到今日银行卡收款金额上限
|
||
self::$errmsg = $err_code_des ? $err_code_des : '超额;已达到今日付款金额上限或已达到今日银行卡收款金额上限!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'ORDERPAID':
|
||
// 超过付款重入有效期
|
||
self::$errmsg = $err_code_des ? $err_code_des : '超过付款重入有效期!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'FATAL_ERROR':
|
||
// 已存在该单,并且订单信息不一致;或订单太老
|
||
self::$errmsg = $err_code_des ? $err_code_des : '已存在该单,并且订单信息不一致;或订单太老!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'NOTENOUGH':
|
||
// 账号余额不足
|
||
self::$errmsg = $err_code_des ? $err_code_des : '账号余额不足!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'SUCCESS':
|
||
// Wx侧受理成功
|
||
self::$errmsg = $err_code_des ? $err_code_des : 'Wx侧受理成功!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'RECV_ACCOUNT_NOT_ALLOWED':
|
||
// 收款账户不在收款账户列表
|
||
self::$errmsg = $err_code_des ? $err_code_des : '收款账户不在收款账户列表!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'PAY_CHANNEL_NOT_ALLOWED':
|
||
// 本商户号未配置API发起能力
|
||
self::$errmsg = $err_code_des ? $err_code_des : '本商户号未配置API发起能力!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'SENDNUM_LIMIT':
|
||
// 已达到付款给此用户次数上限
|
||
self::$errmsg = $err_code_des ? $err_code_des : '已达到付款给此用户次数上限!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
case 'NO_AUTH':
|
||
// 此IP地址不允许调用接口
|
||
self::$errmsg = $err_code_des ? $err_code_des : '此IP地址不允许调用接口!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
default:
|
||
// 新的错误码
|
||
self::$errmsg = $err_code_des ? $err_code_des : '请联系客服确认付款情况!err_code:' . $prepay['err_code'];
|
||
return false;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
self::$errmsg = '微信api接口返回值已修改';
|
||
return false;
|
||
}
|
||
} else if (isset($prepay['return_code']) && $prepay['return_code'] == 'FAIL') {
|
||
$this->doLogs($this->toXml($prepay));
|
||
$this->doLogs($this->toXml($this->config));
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '付款失败。';
|
||
return false;
|
||
} else {
|
||
self::$errmsg = isset($prepay['return_msg']) ? $prepay['return_msg'] : '付款失败';
|
||
return false;
|
||
}
|
||
}
|
||
self::$errmsg = "付款失败,微信api接口无任何返回值";
|
||
return false;
|
||
}
|
||
|
||
// 重试 微信付款到银行卡
|
||
private function retryBankCardPay($params, $url, $t, $type)
|
||
{
|
||
if ($t) {
|
||
sleep($t);
|
||
}
|
||
unset($params["sign"]);
|
||
if ($type) {
|
||
$params["nonce_str"] = $this->createNoncestr();
|
||
}
|
||
$params["sign"] = $this->makeWxSign($params); // 签名
|
||
$result = $this->postXmlCurlCancel($this->toXml($params), $url);
|
||
$prepay = $this->fromXml($result);
|
||
return $this->bankCardPayHandle($prepay, $params, $url);
|
||
}
|
||
|
||
/*
|
||
* 循环查询微信付款到银行卡
|
||
* */
|
||
public function getCardPayState($order_no)
|
||
{
|
||
//暂停一秒查询
|
||
$res = $this->searchCardPayOrder($order_no);
|
||
// 微信通信标识
|
||
if (isset($res['return_code'])) {
|
||
if ($res['return_code'] == "FAIL") {
|
||
self::$errmsg = isset($res['return_msg']) ? $res['return_msg'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
} else {
|
||
// 微信通信标识SUCCESS
|
||
if (isset($res['result_code']) && $res['result_code'] == 'SUCCESS') {
|
||
// 业务结果 SUCCESS
|
||
if (isset($res['status'])) {
|
||
switch ($res['status']) {
|
||
case "SUCCESS":
|
||
// 付款成功
|
||
self::$wxReturnData = array(
|
||
'partner_trade_no' => $res['partner_trade_no'],
|
||
'payment_no' => $res['payment_no'], // 即为微信内部业务单号
|
||
'pay_succ_time' => $res['payment_time'], // 微信侧付款成功时间(依赖银行的处理进度,可能出现延迟返回,甚至被银行退票的情况)
|
||
'cmms_amt' => $res['cmms_amt'], // 手续费订单金额 RMB:分
|
||
);
|
||
return true;
|
||
break;
|
||
case "FAILED":
|
||
// 付款失败,需要替换付款单号重新发起付款
|
||
self::$errmsg = isset($res['reason']) ? $res['reason'] : "付款到银行卡失败";
|
||
return false;
|
||
break;
|
||
case "BANK_FAIL":
|
||
// 银行退票,订单状态由付款成功流转至退票,退票时付款金额和手续费会自动退还
|
||
self::$errmsg = isset($res['reason']) ? $res['reason'] : "付款到银行卡失败,银行退票,订单状态由付款成功流转至退票,退票时付款金额和手续费会自动退还";
|
||
return false;
|
||
break;
|
||
case "PROCESSING":
|
||
// 处理中
|
||
// 继续查询
|
||
$time = get_runtime();
|
||
if ($time < 20) {
|
||
sleep(2);
|
||
return $this->getCardPayState($order_no);
|
||
} else {
|
||
// 将订单标记为处理中订单(让商家或者客户刷新订单重试)
|
||
self::$errmsg = "微信付款到银行卡处理中,请稍后查询打款结果";
|
||
return 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
if (isset($res['err_code'])) {
|
||
switch ($res['err_code']) {
|
||
case "INVALID_REQUEST":
|
||
// 无效的请求,商户系统异常导致,商户权限异常、证书错误、频率限制等 //使用原单号以及原请求参数重试
|
||
self::$errmsg = "无效的请求,商户系统异常导致,商户权限异常、证书错误、频率限制等";
|
||
return false;
|
||
break;
|
||
case "PARAM_ERROR":
|
||
// 参数错误 //按照err_msg指定参数错误信息,修改相应参数
|
||
self::$errmsg = "参数错误";
|
||
return false;
|
||
break;
|
||
case "SIGNERROR":
|
||
// 签名错误 //签名前没有按照要求进行排序。没有使用商户平台设置的密钥进行签名,参数有空格或者进行了encode后进行签名
|
||
self::$errmsg = "签名错误";
|
||
return false;
|
||
break;
|
||
case "ORDERNOTEXIST":
|
||
// 订单不存在 //确认订单号是否发起过请求
|
||
self::$errmsg = "订单不存在";
|
||
return false;
|
||
break;
|
||
case "SYSTEMERROR":
|
||
// 系统繁忙,请稍后重试 //使用原单号以及原请求参数重试
|
||
self::$errmsg = "系统繁忙,请稍后重试";
|
||
return false;
|
||
break;
|
||
case "SUCCESS":
|
||
// Wx侧查询成功 //使用原单号以及原请求参数重试
|
||
self::$errmsg = "微信侧查询成功";
|
||
return false;
|
||
break;
|
||
case "NOT_FOUND":
|
||
// 1、指定单号数据不存在 2、指定单号数据不存在,单据查询超过有效期
|
||
self::$errmsg = "1、指定单号数据不存在 2、指定单号数据不存在,单据查询超过有效期";
|
||
return false;
|
||
break;
|
||
|
||
}
|
||
}
|
||
self::$errmsg = isset($res['err_code_des']) ? $res['err_code_des'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
self::$errmsg = isset($res['err_code_des']) ? $res['err_code_des'] : "查询微信企业付款订单失败";
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* 查询微信付款到银行卡
|
||
* */
|
||
public function searchCardPayOrder($out_trade_no)
|
||
{
|
||
$params = [
|
||
"nonce_str" => $this->createNoncestr(),
|
||
"partner_trade_no" => $out_trade_no,
|
||
"mch_id" => $this->config['mchid']
|
||
];
|
||
$params['sign'] = $this->makeWxSign($params);
|
||
$url = self::$wxQueryUrl; // 微信企业付款查询url
|
||
$xml = $this->toXml($params);//转换为xml
|
||
$response = $this->postXmlCurlCancel($xml, $url, 30);
|
||
$response = json_decode(json_encode(simplexml_load_string($response, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||
return $response;
|
||
}
|
||
|
||
/**
|
||
* @uses 获取公钥
|
||
* @return bool|resource
|
||
*/
|
||
private function _getPublicKey()
|
||
{
|
||
$publicKeyUrl = $this->config['wx_public_key'];
|
||
$public_key = file_get_contents('.' . $publicKeyUrl);
|
||
|
||
return $public_key;
|
||
$GetPublicKey = openssl_pkey_get_public($public_key);
|
||
if ($GetPublicKey) {
|
||
echo 1;
|
||
} else if ($GetPublicKey === false) {
|
||
echo 2;
|
||
} else {
|
||
echo 3;
|
||
}
|
||
}
|
||
|
||
|
||
function publicEncrypt($source)
|
||
{
|
||
$pub_key_string = file_get_contents('.' . $this->config['wx_public_key']);
|
||
$key_resource = openssl_pkey_get_public($pub_key_string);
|
||
$crypttext = '';
|
||
openssl_public_encrypt($source, $crypttext, $key_resource,OPENSSL_PKCS1_OAEP_PADDING);
|
||
return base64_encode($crypttext);
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
public function getMsg()
|
||
{
|
||
return self::$errmsg;
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
public function getReturnData()
|
||
{
|
||
return self::$wxReturnData;
|
||
}
|
||
|
||
/** 生成签名
|
||
* @param $Obj
|
||
* @return string
|
||
*/
|
||
private function makeWxSign($Obj)
|
||
{
|
||
|
||
if (self::$payModel == 1) {
|
||
$key = $this->config["wx_secret_key"];
|
||
} else {
|
||
$key = $this->config["xcx_secret_key"];
|
||
}
|
||
foreach ($Obj as $k => $v) {
|
||
$Parameters[$k] = $v;
|
||
}
|
||
//签名步骤一:按字典序排序参数
|
||
ksort($Parameters);
|
||
$String = $this->formatBizQueryParaMap($Parameters, false);
|
||
//echo '【string1】'.$String.'</br>';
|
||
//签名步骤二:在string后加入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_;
|
||
}
|
||
|
||
/**
|
||
* 作用:格式化参数,签名过程需要使用
|
||
*/
|
||
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;
|
||
}
|
||
|
||
/**
|
||
* 作用:产生随机字符串,不长于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;
|
||
}
|
||
|
||
/**
|
||
* 写入日志记录
|
||
* @param $values
|
||
* @return bool|int
|
||
*/
|
||
private function doLogs($values)
|
||
{
|
||
$years = date('Y-m');
|
||
$url = './Uploads/log/'.$years.'/'.date('Ymd').'remit.txt';
|
||
$dir_name = dirname($url);
|
||
//目录不存在就创建
|
||
if(!file_exists($dir_name)) {
|
||
//iconv防止中文名乱码
|
||
$res = mkdir(iconv("UTF-8", "GBK", $dir_name),0777,true);
|
||
}
|
||
$fp = fopen($url, "a");//打开文件资源通道 不存在则自动创建
|
||
fwrite($fp,date("Y-m-d H:i:s").var_export($values, true)."\r\n");//写入文件
|
||
fclose($fp);//关闭资源通道
|
||
|
||
// return write_log($values, __DIR__);
|
||
// return file_put_contents($values, __DIR__);
|
||
}
|
||
|
||
|
||
/**
|
||
* 将xml转为array
|
||
* @param $xml
|
||
* @return mixed
|
||
*/
|
||
private function fromXml($xml)
|
||
{
|
||
// 禁止引用外部xml实体
|
||
libxml_disable_entity_loader(true);
|
||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
|
||
}
|
||
|
||
/**
|
||
* 以post方式提交xml到对应的接口url
|
||
* @param $xml
|
||
* @param $url
|
||
* @param int $second
|
||
* @return mixed
|
||
*/
|
||
private 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
|
||
$data = curl_exec($ch);
|
||
curl_close($ch);
|
||
return $data;
|
||
}
|
||
|
||
//带证书的
|
||
private function postXmlCurlCancel($xml, $url, $second = 30)
|
||
{
|
||
$Domain = $_SERVER['DOCUMENT_ROOT'];
|
||
if (self::$payModel == 1) {
|
||
// $apiclient_cert = $Domain . "/" . $this->config['apiclient_cert'];
|
||
// $apiclient_key = $Domain . "/" . $this->config['apiclient_key'];
|
||
$apiclient_cert = $this->config['apiclient_cert'];
|
||
$apiclient_key = $this->config['apiclient_key'];
|
||
} else {
|
||
$apiclient_cert = $Domain . $this->config['xcx_apiclient_cert'];
|
||
$apiclient_key = $Domain . $this->config['xcx_apiclient_key'];
|
||
}
|
||
|
||
// $apiclient_cert = "/www/wwwroot/oem.ocean666.top/Uploads/Wx/2020-08-07/5f2d26d741e989.437773624617.pem";
|
||
// $apiclient_key ="/www/wwwroot/oem.ocean666.top/Uploads/Wx/2020-08-07/5f2d26db1b3ee1.661954136040.pem";
|
||
|
||
//初始化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);
|
||
//设置证书
|
||
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
|
||
curl_setopt($ch, CURLOPT_SSLCERT, $apiclient_cert);
|
||
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
|
||
curl_setopt($ch, CURLOPT_SSLKEY, $apiclient_key);
|
||
//设置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 {
|
||
|
||
curl_close($ch);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成签名
|
||
* @param $values
|
||
* @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值
|
||
*/
|
||
private function makeSign($values)
|
||
{
|
||
//签名步骤一:按字典序排序参数
|
||
ksort($values);
|
||
$string = $this->toUrlParams($values);
|
||
//签名步骤二:在string后加入KEY
|
||
$string = $string . '&key=' . $this->config['xcx_secret_key'];
|
||
//签名步骤三:MD5加密
|
||
$string = md5($string);
|
||
//签名步骤四:所有字符转为大写
|
||
$result = strtoupper($string);
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 格式化参数格式化成url参数
|
||
* @param $values
|
||
* @return string
|
||
*/
|
||
private function toUrlParams($values)
|
||
{
|
||
$buff = '';
|
||
foreach ($values as $k => $v) {
|
||
if ($k != 'sign' && $v != '' && !is_array($v)) {
|
||
$buff .= $k . '=' . $v . '&';
|
||
}
|
||
}
|
||
return trim($buff, '&');
|
||
}
|
||
|
||
/**
|
||
* 输出xml字符
|
||
* @param $values
|
||
* @return bool|string
|
||
*/
|
||
private function toXml($values)
|
||
{
|
||
if (!is_array($values)
|
||
|| count($values) <= 0
|
||
) {
|
||
return false;
|
||
}
|
||
|
||
$xml = "<xml>";
|
||
foreach ($values as $key => $val) {
|
||
if (is_numeric($val)) {
|
||
$xml .= "<" . $key . ">" . $val . "</" . $key . ">";
|
||
} else {
|
||
$xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
|
||
}
|
||
}
|
||
$xml .= "</xml>";
|
||
return $xml;
|
||
}
|
||
} |