tp5微信开发(五)--- tp5微信企业付款到零钱开发实践
项目需求:客户有一个分销项目,需要对分享获得的奖励直接发放至客户零钱钱包。
开发文档传送门:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1
企业付款到用户零钱开通条件:
1、商户号(或同主体其他非服务商商户号)已入驻90日
2、商户号(或同主体其他非服务商商户号)有30天连续正常交易
3、 登录微信支付商户平台-产品中心,开通企业付款。
企业付款资金来源:
◆ 默认情况下,企业付款到零钱使用商户号基本户(或余额账户)余额。如商户号已开通运营账户,则企业付款到零钱使用运营账户内的资金。
◆ 基本户(或上述其他出款账户)的资金来源,可能是交易结算款项(仅基本户),或给账户充值的资金。当出款账户余额不足时,付款将因余额不足而付款失败。
项目准备:分销关系的建立不做说明,现在假设已建立上下级关系,下级支付后对用户进行佣金奖励。
代码实际上网上已经有很多版本demo,我选了一个并与其他版本进行比对,经过三个小时踩坑和各种调试,总结了一个可运行版本。
代码:
public function sendMoney($amount,$re_openid,$desc='推荐奖励',$check_name=''){
$total_amount = (100) * $amount;
$data=array(
'mch_appid'=> '',//商户账号appid
'mchid'=> '',//商户号
'nonce_str'=> $this->createNoncestr(),//随机字符串
'partner_trade_no'=> date('YmdHis').rand(1000, 9999),//商户订单号
'openid'=> $re_openid,//用户openid
'check_name'=>'NO_CHECK',//校验用户姓名选项,
're_user_name'=> $check_name,//收款用户姓名
'amount'=>$total_amount,//金额
'desc'=> $desc,//企业付款描述信息
'spbill_create_ip'=> '',//Ip地址
);
$secrect_key = '';//API密码
$data = array_filter($data);
ksort($data);
$str ='';
foreach($data as $k=>$v) {
$str.=$k.'='.$v.'&';
}
$str.='key='.$secrect_key;
$data['sign'] = md5($str);
$xml = $this->arraytoxml($data);
$url='https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers'; //调用接口
$res = $this->wx_curl($xml,$url);
$return = $this->xmltoarray($res);
$responseObj = simplexml_load_string($res, 'SimpleXMLElement', LIBXML_NOCDATA);
echo $res= $responseObj->return_code; //SUCCESS 如果返回来SUCCESS,则发生成功,处理自己的逻辑
return $res;
}
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;
}
public function arraytoxml($data){
$str='<xml>';
foreach($data as $k=>$v) {
$str.='<'.$k.'>'.$v.'</'.$k.'>';
}
$str.='</xml>';
return $str;
}
public function xmltoarray($xml) {
//禁止引用外部xml实体
libxml_disable_entity_loader(true);
$xmlstring = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
$val = json_decode(json_encode($xmlstring),true);
return $val;
}
public function wx_curl($vars,$url,$second = 30, $aHeader = array()) {
$isdir = ROOT_PATH."cert/";//证书位置
$ch = curl_init();//初始化curl
curl_setopt($ch, CURLOPT_TIMEOUT, $second);//设置执行最长秒数
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);//要求结果为字符串且输出到屏幕上
curl_setopt($ch, CURLOPT_URL, $url);//抓取指定网页
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);// 终止从服务端进行验证
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, 1);//post提交方式
curl_setopt($ch, CURLOPT_POSTFIELDS, $vars);//全部数据使用HTTP协议中的"POST"操作来发送
$data = curl_exec($ch);//执行回话
if ($data) {
curl_close($ch);
return $data;
} else {
$error = curl_errno($ch);
echo "call faild, errorCode:$error\n";
curl_close($ch);
return false;
}
}
测试方法:新建一个check方法,通过访问域名/index/base/send_check 进行测试:
备注说明:第一个参数是付款金额,第二个参数是收款openid,这个我本身已经存的有所以直接从数据库粘贴过来的。
public function send_check(){
$this->sendMoney(1,'olrMRuIULa1k_GKC7XwLWPlH0IA8','哎咖啡付款到零钱测试');
}
划重点:
1,付款到零钱需要用到证书,现在微信已经在推行新的api证书,新版证书生成说明传送门:https://kf.qq.com/faq/180824BrQnQB180824m6v2yA.html 操作不难,按照指引,就两个复制粘贴的事,最终点击生成会自动下载一个证书压缩包。
我们是php的,所以用得到下面两个证书文件,在tp5项目根目录下创建 cert 文件夹,并上传两个证书文件【我看有的网友总结说,证书必须要放到根目录而且必须是cert命名,这个我没有求证,不知道是否是强制要求,不过我是的确是这么做的】。
2,在测试过程中,总会踩各种各样的坑,我把我大概的调试过程中遇到的错误进行说明:
1),打印curl 返回错误 int(58),查询 curl 对应返回码得知是证书问题,我能确定我证书是最新生成的,所以证书是没问题,查文档要求证书文件必须是绝对路径,改之,成功。对于证书文件路径,可在curl请求中打印具体路径值,确保是绝对路径。
2),提示签名错误,核验方法:一再需要确认商户号,appid以及支付秘钥是否正确,我当时没留意,就这个问题查了一个小时,结果发现是商户号写的是之前测试对应的账户,结果一直都验签不成功,WTF!
3),支付提示账户余额不足,查客户的基本账户,交易类型设置的是T+1的,可能是这个原因,导致商户收到的钱都直接转到客户指定的银行账号了,点击交易中心->充值->选择银行,充值了测试金,返回继续访问路由,OK,成功。
最终效果: