一、情景简述
之前做了微信付款到企业银行卡的功能,但是,给客户打款后,次日才能到账,为及时进行反馈,于是又做了"查询给企业付款是否到账"的功能。这块,我直接写了一个类,进行处理,下面,就是整个类的代码!
微信文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_3
二、相关注意
1、这里涉及到一些敏感配置的,都在配置文件里,还有,"查询给企业付款是否到账"需要用到证书,这个需要提前准备,切记!
2、partner_trade_no参数,是在打款给客户银行卡账号时生成的一串字符,你可以自己存储,也可以从微信端返回的结果数据中获取。
3、查询是否到账,到账的情况是return_code、result_code、err_code全为true时,pay_succ_time不为空,表示已到账!
三、核心代码
<?php defined('BASEPATH') OR exit('No direct script access allowed');
/**
* 查询给企业付款是否到账 - Service
*
* @desc 查看打款到银行卡的钱是否到账,如果,到账,更新distribute_log表 is_received 为 1
* @author NangongYi
* @time 2019/09/28 01:38:59
*
*/
class Check_account_service extends FIT_Service
{
/**
* 字符池
*/
const STING_POOL = '23456789ABCDEFGHJKLMNPQRSTUVWXYabcdefghijkmnpqrstuvwxy';
/**
* 字符长度
*/
const STR_LENGTH = 18;
/**
* 构造方法 - 继承父类
*/
public function __construct ()
{
parent::__construct();
}
/**
* 微信 API - 配置参数
*/
private function get_api_config_param()
{
$this->load->config('dict/dict_distribute.php');
return $this->config->item('API_CONFIG');
}
/**
* 查看订单分账是否到账
* @param string $partner_trade_no : 分账时生成的唯一码
* @return bool
*/
private function check_order_distribute_is_received($partner_trade_no)
{
// 配置参数
$api_config = $this->get_api_config_param();
// 随机字符串
$nonce_str = $this->product_random_str();
// 准备数据
$data = ['mch_id'=>$api_config['mch_id'], 'partner_trade_no'=>$partner_trade_no, 'nonce_str'=>$nonce_str];
// 签名
$sign = $this->product_sign_str($data);
// xml数据整合
$xmlData = $this->xml($data, $sign);
// 数据请求
$result = $this->curl_post_we_chat($api_config['query_bank'], $xmlData, true);
// 结果处理
if($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS' && $result['err_code'] == 'SUCCESS'){
$success = ['is_received'=>1, 'received_time'=>$result['pay_succ_time']]; // 成功,返回到账时间,状态为 1
return !empty($result['pay_succ_time']) ? $success : false;
}else{
$error = ['is_received'=>-1, 'rec_error_reason'=>$result['reason']]; // 失败,返回失败原因,状态为 -1,异常
return $error;
}
}
/**
* xml数据 - 整合
* @param array $data : 微信配置参数
* @param string $sign : 生成签名
* @return string
*/
private function xml($data, $sign)
{
$xmlData="<xml>
<mch_id>".$data['mch_id']."</mch_id>
<nonce_str>".$data['nonce_str']."</nonce_str>
<partner_trade_no>".$data['partner_trade_no']."</partner_trade_no>
<sign>".$sign."</sign>
</xml>";
return $xmlData;
}
/**
* 遍历所有的分账日志表中未付款记录,并进行标记
* @desc 判断,如果到账,更新 distribute_log 表 is_received = 1
*/
public function process_no_received_orders()
{
$this->load->dao("order_dao");
$no_received_orders = $this->order_dao->get_no_received_orders();
if(!$no_received_orders){
return [];
}
$no_received_orders = $this->process_partner_trade_no($no_received_orders);
$w_orderids = array_column($no_received_orders, null, 'partner_trade_no');
$flag = 1;
foreach ($w_orderids as $key=>$val) {
// 过滤掉微信订单id为空的数据
if(empty($val['partner_trade_no'])) continue;
// 查看分账是否到账
$res = $this->check_order_distribute_is_received($key);
if(!$res) continue;
// 如果到账,更改分账订单的状态为已分账 1
$up_log = $this->order_dao->update_distribute_log_received($val['orderid'], $res);
if(!$up_log) $flag --;
}
return $flag;
}
/**
* 处理返回数据,返回 partner_trade_no
* @param array $data : 订单数据
* @return array
*/
private function process_partner_trade_no($data)
{
foreach ($data as $key=>$val) {
$data[$key]['partner_trade_no'] = $this->get_partner_trade_no($val['plg_log_info']);
}
return $data;
}
/**
* 从json对象字符串中,返回 partner_trade_no
* @param string $string : json对象
* @return string
*/
private function get_partner_trade_no($string)
{
$result = json_decode(substr($string,strrpos($string, '=')+1),true);
return $result['partner_trade_no'];
}
/**
* cURL方式POST数据到微信
* @param string $url : 请求地址
* @param array $data : 发送数据
* @return mixed
*/
private function curl_post_we_chat($url, $data, $ssl = false)
{
$api_config = $this->get_api_config_param();
$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $url );
curl_setopt ( $ch, CURLOPT_CUSTOMREQUEST, "POST" );
curl_setopt ( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt ( $ch, CURLOPT_SSL_VERIFYHOST, FALSE );
if($ssl) {
curl_setopt ( $ch,CURLOPT_SSLCERT,$api_config['sslcert']);
curl_setopt ( $ch,CURLOPT_SSLKEY,$api_config['sslkey']);
}
curl_setopt ( $ch, CURLOPT_FOLLOWLOCATION, 1 );
curl_setopt ( $ch, CURLOPT_AUTOREFERER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $data );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
$result = curl_exec($ch);
if (curl_errno($ch)) {
return 'Errno: '.curl_error($ch);
}
curl_close($ch);
return $this->xmlToArray($result);
}
/**
* 生成签名
* @param array $paramArray : 微信请求参数
* @param bool $isencode
* @return mixed
*/
private function product_sign_str($paramArray, $isencode=false)
{
$api_config = $this->get_api_config_param();
$paramStr = '';
ksort($paramArray);
$i = 0;
foreach ($paramArray as $key => $value)
{
if ($key == 'Signature'){
continue;
}
if ($i == 0){
$paramStr .= '';
}else{
$paramStr .= '&';
}
$paramStr .= $key . '=' . ($isencode?urlencode($value):$value);
++$i;
}
$stringSignTemp=$paramStr."&key=".$api_config['key'];
$sign=strtoupper(md5($stringSignTemp));
return $sign;
}
/**
* 生成的随机字符串(小于32位)
* @return string $randStr : 返回32位的字符
*/
private function product_random_str()
{
$string = self::STING_POOL;
$length = self::STR_LENGTH;
$randStr = '';
for($i = 0; $i < $length; $i++){
$randStr .= $string[mt_rand(0, strlen($string)-1)];
}
return $randStr;
}
/**
* 将xml转换成数组
* @params xml $xml : xml数据
* return array $data : 返回数组
*/
private function xmlToArray($xml)
{
libxml_disable_entity_loader(true);//禁止引用外部xml实体
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
return json_decode(json_encode($xmlstring),true);
}
}
四、写在最后
一开始看文档的时候,感觉微信的文档,写的还是不那么尽人意,所以,读了好几遍,才有所理解,其关键点,在于“签名的生成”,这里需要注意,另外,通过Curl函数传输数据的时候,会用到证书,这里文档有提到,但还是在这个地方,纠结了好久!总之,涉及到支付这块的,微信都比较严谨,需要细心理解、处理!