官方文档:企业付款
<?php
namespace App\Helpers\Unit\Behaviour;
use App\Helpers\Traits\Logs;
use App\Models\PayTakeoutLogModels AS mPayTakeoutLog;
/**
* 组件 - 微信提现
*
* @version 1.0.0
* @author Yunuo <ciwdream@gmail.com>
* @link https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
*/
class WeChaTtakeoutUnit
{
use Logs;
/**
* 商户账号appid
*
* @var string $_appid
*/
private $_appid = '';
/**
* ApiKey密码
*
* @var string $_apiKey
*/
private $_apiKey = '';
/**
* 商户号
*
* @var int $_mchId
*/
private $_mchId = '';
/**
* 授权用户openid
*
* @var int $_openid
*/
private $_openid = '';
private $_param = [];
private $_models = null;
private $_orderInfo = [];
/**
* 实例化
*
* @param string $trade_no 提现订单号
*
* @return void
*/
public function __construct($trade_no)
{
$this->_models = new mPayTakeoutLog();
$this->_isOrderExist($trade_no);
$this->_param['mch_appid'] = $this->_appid;
$this->_param['mchid'] = $this->_mchId;
$this->_param['nonce_str'] = $this->_nonceStr();
$this->_param['partner_trade_no'] = $trade_no;
$this->_param['check_name'] = 'NO_CHECK';
}
/**
* 发起提现
*
* @param string $clientIP 客户端IP
* @param integer $money 提现金额(单位:分)
* @param string $desc 提现备注信息
*
* @return bool
*/
public function transfers($clientIP,$money,$desc=null)
{
if(empty($this->_orderInfo))return false;
$_allow = false;
$this->_param['openid'] = $this->_openid;
$this->_param['amount'] = $money; //付款金额,单位为分
$this->_param['desc'] = empty($desc)?'':$desc;
$this->_param['spbill_create_ip'] = $clientIP; // 获取服务器的ip
$this->_param['sign'] = $this->_createSign($this->_param);
if( $_return = $this->_curlPostSsl($this->_param) ){
if ($_return['return_code'] == 'SUCCESS' && $_return['result_code'] == 'SUCCESS') {
$_orderInfo = $this->_models
->where('trade_no',$this->_param['partner_trade_no'])
->update(['pay_state'=>1,'pay_time'=>date('Y-m-d H:i:s')]);
if( $_orderInfo ){
$this->_isOrderExist($this->_param['partner_trade_no']); // 更新类属性中的订单信息
self::logRecord('info','[微信提现 -> transfers]订单处理成功'.var_export($this->_param,true));
$_allow = true;
}else{
self::logRecord('error','[微信提现 -> transfers]订单处理失败'.var_export($this->_param,true));
}
}else{
self::logRecord('error','[微信提现 -> transfers]请求处理失败'.var_export($_return,true));
}
}else{
self::logRecord('error','[微信提现 -> transfers]请求处理失败'.var_export($_return,true));
}
return $_allow;
}
/**
* 设置Openid
*
* @param string $openid 用户openid
*
* @return void
*/
public function setOpenid($openid) : void
{
$this->_openid = $openid;
}
/**
* 生成随机字符串
*
* @return string 随机字符串,不长于32位
*/
private function _nonceStr() : string
{
return md5(uniqid(microtime(true),true));
}
/**
* 生成校验Sign
*
* @param array $param 待校验数据
*
* @return string 签名
*/
private function _createSign($param) : string
{
$param = array_filter($param);
ksort($param);
$string="";
foreach ($param as $k => $v) {
if($k != "sign" && $v != "" && !is_array($v)){
$string .= $k . "=" . $v . "&";
}
}
$string = $string . "key=".$this->_apiKey;
$string = md5($string);
$result = strtoupper($string);
return $result;
}
/**
* CURL微信
*
* @param array $param 待发送数据
*
* @return string|bool
*/
private function _curlPostSsl($param)
{
header("Content-Type: text/html; charset=utf-8");
$_isdir = resource_path('cert').DIRECTORY_SEPARATOR; // 证书位置
$_apiUrl = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
$_aHeader = [];
$_sendData = $this->_toXml($param);
$ch = curl_init();
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $_apiUrl);
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, $_isdir . 'apiclient_cert.pem');//证书位置
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
curl_setopt($ch, CURLOPT_SSLKEY, $_isdir . 'apiclient_key.pem');//证书位置
if (count($_aHeader) >= 1) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $_aHeader);//设置头部
}
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $_sendData);
$data = curl_exec($ch);
if ($data) {
curl_close($ch);
if( !is_string($data) ){
self::logRecord('error','[微信提现 -> _curlPostSsl]curl返回数据::'.var_export($data,true));
return false;
}
return $this->_fromXml($data);
} else {
$error = curl_errno($ch);
self::logRecord('error','[微信提现 -> _curlPostSsl]curl出错,错误码::'.$error);
curl_close($ch);
return false;
}
}
/**
* 输出xml字符
*
* @param array $param 待转化数组
*
* @return string Xml
**/
private function _toXml($param) : string
{
$xml = "<xml>";
foreach ($param as $key=>$val)
{
if (is_numeric($val)){
$xml.="<".$key.">".$val."</".$key.">";
}else{
$xml.="<".$key."><![CDATA[".$val."]]></".$key.">";
}
}
$xml.="</xml>";
return $xml;
}
/**
* 将xml转为array
*
* @param string $xml 待转化XML数据
*
* @return array
*/
private function _fromXml($xml) : array
{
//将XML转为array
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
}
/**
* 判断订单是否存在
*
* @param string $trade_no 待处理订单号
*
* @return bool
*/
private function _isOrderExist($trade_no) : bool
{
$_orderInfo = $this->_models
->where('trade_no',$trade_no)
->first();
if( $_orderInfo ){
$this->_orderInfo = $_orderInfo->toArray();
if( 1 === $this->_orderInfo['pay_state'] ){
$this->_orderInfo = [];
return false;
}
return true;
}else{
$this->_orderInfo = [];
return false;
}
}
}