微信官方文档
微信企业付款到零钱:官方文档链接地址
前期准备
- 准备一个商户号,商户号已入驻90日且截止今日回推30天商户号保持连续不间断的交易。商户号还需要开通付款到零钱功能。
- 准备商户号的appid。
- 准备商户号的支付证书,商户证书的获取方法可以看官方文档。
- 用户提现前需要确保用户登录,获取用户的openid。用户登录所用的公众号或者小程序的appid需要绑定到商户号下。
实际应用示例
- 提现接口
//支付到用户零钱
public function transfers()
{
//调用付款到零钱接口的参数
$data = [
'mch_appid' =>'XXXXXXXXXX', //商户账号appid
'mchid' => 'XXXXXXXXXX', //商户号
'nonce_str' => 'XXXXXXXXXX', //随机字符串,小于32
'partner_trade_no' => 'no123456789', //商户订单号
'openid' => 'XXXXXXXXX', //用户openid
'check_name' => 'NO_CHECK', //NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
'amount' => 1 * 100, //付款金额,单位分,测试提现1元
'desc' => '这是付款的备注', //付款备注
];
//微信接口地址
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers';
//获取签名
$data['sign'] = $this->makeSign($data,'md5');
//发送数据转为xml格式
$xml = $this->arrayToXml($data);
//发送参数到微信接口
$res = self::curl_post_ssl($url,$xml);
$result = $this->xmlToArray($res);
//判断支付结果
if ($result['result_code'] == 'SUCCESS'){ //支付成功
return $result;
}else{ //支付失败,返回错误代码和错误描述
$result = [
'err_code' => $result['err_code'],
'err_code_des' => $result['err_code_des'],
];
$this->setErrmsg($result);
return false;
}
}
- 提现接口中的签名算法
protected function makeSign($data, $signType = 'HMAC-SHA256')
{
//签名步骤一:按字典序排序参数
ksort($data);
$string = self::toUrlParams($data);
//签名步骤二:在string后加入KEY
$string = $string . "&key=" . self::$key;
//签名步骤三:MD5加密或者HMAC-SHA256
if ($signType == 'md5') {
//如果签名小于等于32个,则使用md5验证
$string = md5($string);
} else {
//是用sha256校验
$string = hash_hmac("sha256", $string, self::$key);
}
//签名步骤四:所有字符转为大写
$result = strtoupper($string);
return $result;
}
- 提现接口中的数组转XML格式
public function arrayToXml($arr) {
$xml = "<xml>";
foreach ($arr as $key => $val){
if (is_numeric($val)){
$xml.="<$key>$val</$key>";
}
else
$xml.="<$key><![CDATA[$val]]></$key>";
}
$xml.="</xml>";
return $xml;
}
- 提现接口中的使用curl的方式发送数据到微信接口
public static function curl_post_ssl($url, $vars, $second = 30, $aHeader = array())
{
$isdir = $path = Yii::$app->basePath;//证书位置;
$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, false);// 终止从服务端进行验证
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//
curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');//证书类型
curl_setopt($ch, CURLOPT_SSLCERT, $isdir .'/certificate/apiclient_cert.pem');//证书位置
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');//CURLOPT_SSLKEY中规定的私钥的加密类型
curl_setopt($ch, CURLOPT_SSLKEY, $isdir . '/certificate/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;
}
}
- 提现接口中的XML格式转数组
public function xmlToArray($xml) {
$arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
return $arr;
}
可能需要注意的点
- 确保商户号,商户号appid,支付证书等信息的正确。尤其确保支付证书的路径没错误,使用证书的绝对路径。
- 确保使用的小程序之类的appid绑定到商户号中。
- 订单编号需要始终保证是唯一的,用户的提现记录包含订单编号最好保存在数据库中,出现问题时,可以使用订单编号对这个提现进行查询。
- 用户的提现金额需要换算成分,传入微信的接口的金额单位为分。
可能遇到的问题及解决办法
1.使用沙箱环境的时候。在使用curl发送数据到微信接口时,需要加上一下代码。否则会一直提示"请确认请求参数是否正确 param mch_id invalid"。非沙箱环境不需要。
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
后续补充
1、查询支付信息接口
直接代码展示
public function getTransferInfo($order_no)
{
//调用查询接口需要的参数
$data = [
'nonce_str' => 'XXXXXXX', //随机字符串
'partner_trade_no' => $order_no, //商户订单号
'mch_id' => 'XXXXX', //商户号
'appid' => 'XXXXX', //商户号的appid
];
$url = 'https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo';
//获取签名
$data['sign'] = $this->makeSign($data,'md5');
//发送数据转为xml格式
$xml = $this->arrayToXml($data);
//发送参数到微信接口
$res = self::curl_post_ssl($url,$xml);
$result = $this->xmlToArray($res);
if ($result['result_code'] == 'SUCCESS'){ //查询成功
if ($result['status'] == 'FAILED'){ //转账失败
$this->setErrmsg($result['reason']); //失败原因
return false;
}else{
return $result;
}
}else{
$this->setErrmsg($result['err_code_des']);
return false;
}
}
2、获取沙箱环境商户密钥接口
直接代码展示
public function getSignKey()
{
$url = 'https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey';
$data = [
'mch_id' => self::$mch_id,
'nonce_str' => time(),
];
$sign = $this->makeSign($data,'md5');
$data['sign'] = $sign;
//发送数据转为xml格式
$xml = $this->arrayToXml($data);
//发送参数到微信接口
$res = self::post_remote_result($url,$xml);
$result = $this->xmlToArray($res);
if ($result['return_code'] == 'SUCCESS'){
return $result['sandbox_signkey']; //沙箱密钥
}else{
$this->setErrmsg($result['return_msg']);
return false;
}
}
通过curl的方式发送数据到微信接口,代码如下
public static function post_remote_result($url, $data, $second = 20, $setoptArr=array(), $http_code = 200)
{
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$url);
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_TIMEOUT,$second);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
if (preg_match("/^(http:\/\/|https:\/\/).*$/", $url)) {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
foreach($setoptArr as $key=>$value){
curl_setopt($ch,constant($key),$value);
}
$content = curl_exec($ch);
$httpCode = curl_getinfo($ch,CURLINFO_HTTP_CODE);
if($httpCode == $http_code) {
//成功
} else {
//失败
$content = json_encode(array('httpErr'=>1,'httpCode'=>$httpCode));
}
curl_close($ch);
return $content;
}
注意: 下面的代码必须加到curl方法中,不然会提示"请确认请求参数是否正确 param mch_id invalid"。
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));