微信开发 -- 企业付款 PHP代码实现
写这篇文章的目的主要是由于在微信公众平台提供的SDK中并没有提供此功能的SDK实现,
其实最后实现还是借助 微信公众平台开发文档 和 SDK 。
企业付款的应用场景: 公众号向已关注用户付款,比如处理退款、财务结算等
先说一下实现思路:
在SDK中自带类库的基础上扩展WxMchPay组件, 实现企业付款功能的扩展。
话不多说,上代码, 下面是继承SDK,实现企业付款的组件:
$parameters参数参考: 企业付款API的文档
<?php
// 引入SDK
import('Common.Util.WxPay');
/**
* 微信企业付款操作类
* Author : Max.wen
* DateTime: <15/9/16 11:00>
*/
class WxMchPay extends Wxpay_client_pub
{
/**
* API 参数
* @var array
* 'mch_appid' # 公众号APPID
* 'mchid' # 商户号
* 'device_info' # 设备号
* 'nonce_str' # 随机字符串
* 'partner_trade_no' # 商户订单号
* 'openid' # 收款用户openid
* 'check_name' # 校验用户姓名选项 针对实名认证的用户
* 're_user_name' # 收款用户姓名
* 'amount' # 付款金额
* 'desc' # 企业付款描述信息
* 'spbill_create_ip' # Ip地址
* 'sign' # 签名
*/
public $parameters = [];
public function __construct()
{
$this->url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
$this->curl_timeout = WxPayConf_pub::CURL_TIMEOUT;
}
/**
* 生成请求xml数据
* @return string
*/
public function createXml()
{
$this->parameters['mch_appid'] = WxPayConf_pub::APPID;
$this->parameters['mchid'] = WxPayConf_pub::MCHID;
$this->parameters['nonce_str'] = $this->createNoncestr();
$this->parameters['sign'] = $this->getSign($this->parameters);
return $this->arrayToXml($this->parameters);
}
/**
* 作用:使用证书,以post方式提交xml到对应的接口url
*/
function postXmlSSLCurl($xml,$url,$second=30)
{
$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);
//设置证书
curl_setopt($ch,CURLOPT_CAINFO, WxPayConf_pub::SSLROOTCA_PATH);
//使用证书:cert 与 key 分别属于两个.pem文件
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLCERTTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLCERT, WxPayConf_pub::SSLCERT_PATH);
//默认格式为PEM,可以注释
curl_setopt($ch,CURLOPT_SSLKEYTYPE,'PEM');
curl_setopt($ch,CURLOPT_SSLKEY, WxPayConf_pub::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;
}
}
}
Controller层功能实现:
<?php
/**
* Author : Max.wen
* DateTime: <15/9/20 16:47>
*/
namespace Home\Controller;
class TestController extends CommonController
{
/**
* 企业付款测试
*/
public function rebate()
{
import('Common.Util.WxMchPay');
$mchPay = new \WxMchPay();
// 用户openid
$mchPay->setParameter('openid', 'oy2lbszXkgvlEKThrzqEziKEBzqU');
// 商户订单号
$mchPay->setParameter('partner_trade_no', 'test-'.time());
// 校验用户姓名选项
$mchPay->setParameter('check_name', 'NO_CHECK');
// 企业付款金额 单位为分
$mchPay->setParameter('amount', 100);
// 企业付款描述信息
$mchPay->setParameter('desc', '开发测试');
// 调用接口的机器IP地址 自定义
$mchPay->setParameter('spbill_create_ip', '127.0.0.1'); # getClientIp()
// 收款用户姓名
// $mchPay->setParameter('re_user_name', 'Max wen');
// 设备信息
// $mchPay->setParameter('device_info', 'dev_server');
$response = $mchPay->postXmlSSL();
if( !empty($response) ) {
$data = simplexml_load_string($response, null, LIBXML_NOCDATA);
echo json_encode($data);
}else{
echo json_encode( array('return_code' => 'FAIL', 'return_msg' => 'transfers_接口出错', 'return_ext' => array()) );
}
}
}
完成上述两部分代码,基本就可以成功调用企业付款API了。
返回结果的数据结构示例:
{ "return_code": "SUCCESS", "return_msg": { }, "mch_appid": "wx519cae424099ed6b", "mchid": "1228636402", "device_info": { }, "nonce_str": "qjupk84q4iqxkb578hb5h2qiatgcwxwg", "result_code": "SUCCESS", "partner_trade_no": "test-1442801966", "payment_no": "1000018301201509210739170397", "payment_time": "2015-09-21 10:19:26" }
可能遇到的问题:
1、CA证书错误
在WxMchPay中大家可以看到,我重写了SDK中 Wxpay_client_pub 的 postXmlSSLCurl()方法
因为默认在SDK中的这个方法在CURL POST请求的时候没有附带CA证书。
相比之下就多了
curl_setopt($ch,CURLOPT_CAINFO, WxPayConf_pub::SSLROOTCA_PATH); 这么一行代码。
作用就是请求时附带CA证书。
2、对同一用户转账操作过于频繁,请稍候重试.
这个错误属于微信服务端的限制,具体限制频率也没有找到那里有说明,不过经过实际测试大概在1分钟左右。
所以在开发的时候还需要多注意。
有任何疑问欢迎共同探讨。
P.S. 搬家至博客园的第一篇随笔, 送给组织的见面礼。希望再次广交猿友。