视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
微信开发之企业付款到银行卡接口开发的示例代码
2020-11-27 22:07:34 责编:小采
文档

微信支付已上线企业付款至银行卡功能。商户可以将商户号余额付款至指定的收款银行账户。通过指定收款银行账户户名、卡号,以及收款银行信息即可实现付款。功能目前为灰度开放,已灰度新资金流直连普通商户及普通受理模式子商户,其他商户类型将在后续开放。

一、微信商户号须开通此功能;

二、应用环境TP5+Mysql+Centos

三、编写代码

3.1、Model层,Wechat.php

<?php
namespace app\home\model;
use think\Cache;

class Wechat extends Model
{
 private $appid; //公众号APPID
 private $appsecret; //公众号appsecret
 private $mchid; //商户号
 private $key; //支付密钥
 private $sslcert; //证书保存的绝对路径
 private $sslkey; //证书保存的绝对路径
 
 public function __construct($appid,$appsecret,$mchid,$key,$sslcert,$sslkey)
 {
 parent::__construct();
 $this->appid = $appid;
 $this->appsecret = $appsecret;
 $this->mchid = $mchid;
 $this->key = $key;
 $this->sslcert = $sslcert;
 $this->sslkey = $sslkey;
 }
 /*
 * 企业付款到银行卡接口
 * @params string $out_trade_no : 商户订单号
 * @params int $amount : 付款金额,单位分
 * @params string $enc_bank_no : 收款方银行卡号
 * @params string $enc_true_name : 收款方用户名
 * @params string $bank_name : 收款方开户行,根据银行名称获取银行编号bank_code
 * @params string $desc : 付款备注
 * return string $payment_no :支付成功的订单号
 */
 public function payForBank($out_trade_no,$amount,$enc_bank_no,$enc_true_name,$bank_name,$desc='企业付款到银行卡')
 {
 $data['amount'] = $amount;
 $data['bank_code'] = $this->getBankCode($bank_code);
 $data['desc'] = $desc;
 $data['enc_bank_no'] = $this->publicEncrypt($enc_bank_no);
 $data['enc_true_name'] = $this->publicEncrypt($enc_true_name);
 $data['mch_id'] = $this->mchid;
 $data['nonce_str'] = $this->random(12);
 $data['partner_trade_no'] = $out_trade_no;
 $sign = $this->getParam($data);

 $dataXML="<xml>
 <amount>".$data['amount']."</amount>
 <bank_code>".$data['bank_code']."</bank_code>
 <desc>".$data['desc']."</desc>
 <enc_bank_no>".$data['enc_bank_no']."</enc_bank_no>
 <enc_true_name>".$data['enc_true_name']."</enc_true_name>
 <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>";

 $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank';
 $ret = $this->httpsPost($url,$dataXML,true);
 if($ret['return_code'] == 'SUCCESS' && $ret['result_code'] == 'SUCCESS' && $ret['err_code'] == 'SUCCESS'){
 return $ret['payment_no'];
 }else{
 $this->errorLog('微信付款到银行卡失败,appid:'.$this->appid,$ret);
 return false;
 }
 }
 /*
 * 查询付款到银行卡状态
 * @params string $out_trade_no : 商户订单号
 * return array $ret:查询状态
 * */
 public function queryBank($out_trade_no)
 {
 $data['mch_id'] = $this->mchid;
 $data['nonce_str'] = $this->random(12);
 $data['partner_trade_no'] = $out_trade_no;
 $sign = $this->getParam($data);
 $dataXML="<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>";
 $url = 'https://api.mch.weixin.qq.com/mmpaysptrans/query_bank';
 $ret = $this->httpsPost($url,$dataXML,true);
 if($ret['return_code'] == 'SUCCESS' && $ret['result_code'] == 'SUCCESS' && $ret['err_code'] == 'SUCCESS'){
 return $ret;
 }else{
 $this->errorLog('查询微信付款到银行卡失败,appid:'.$this->appid.',订单号:'.$out_trade_no,$ret);
 return false;
 }
 }
 /*
 * 银行编号列表,详情参考:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4
 * @params string $bank_name : 银行名称,4个汉字
 * return int $bank_code : 银行编码
 * */
 private function getBankCode($bank_name)
 {
 $bank_code = 0;
 switch ($bank_name){
 case '工商银行': $bank_code = 1002; break;
 case '农业银行': $bank_code = 1005; break;
 case '中国银行': $bank_code = 1026; break;
 case '建设银行': $bank_code = 1003; break;
 case '招商银行': $bank_code = 1001; break;
 case '邮储银行': $bank_code = 1066; break;
 case '交通银行': $bank_code = 1020; break;
 case '浦发银行': $bank_code = 1004; break;
 case '民生银行': $bank_code = 1006; break;
 case '兴业银行': $bank_code = 1009; break;
 case '平安银行': $bank_code = 1010; break;
 case '中信银行': $bank_code = 1021; break;
 case '华夏银行': $bank_code = 1025; break;
 case '广发银行': $bank_code = 1027; break;
 case '光大银行': $bank_code = 1022; break;
 case '北京银行': $bank_code = 1032; break;
 case '宁波银行': $bank_code = 1056; break;
 }
 return $bank_code;
 }
 /**
 * 公钥加密,银行卡号和姓名需要RSA算法加密
 * @param string $data 需要加密的字符串,银行卡/姓名
 * @return null|string 加密后的字符串
 */
 private function publicEncrypt($data)
 {
 // 进行加密
 $pubkey = openssl_pkey_get_public(file_get_contents(ROOT_PATH.'data/cert/public_pkcs8.pem'));
 $encrypt_data = '';
 $encrypted = '';
 $r = openssl_public_encrypt($data,$encrypt_data,$pubkey,OPENSSL_PKCS1_OAEP_PADDING);
 if($r){//加密成功,返回base编码的字符串
 return base_encode($encrypted.$encrypt_data);
 }else{
 return false;
 }
 }
 /*
 * 获取公钥,格式为PKCS#1 转PKCS#8
 * openssl rsa -RSAPublicKey_in -in <filename> -out <out_put_filename>
 * */
 private function get_pub_key()
 {
 $rsafile = ROOT_PATH.'data/cert/'.$this->appid.'_publicrsa.pem';
 if(!is_file($rsafile)){
 $data['mch_id'] = $this->mchid;
 $data['nonce_str'] = $this->random(12);
 $sign = $this->getParam($data);
 $dataXML="<xml>
 <mch_id>".$data['mch_id']."</mch_id>
 <nonce_str>".$data['nonce_str']."</nonce_str>
 <sign>".$sign."</sign>
 </xml>";
 $url = 'https://fraud.mch.weixin.qq.com/risk/getpublickey';
 $ret = $this->httpsPost($url,$dataXML,true);
 if($ret['return_code'] == 'SUCCESS' && isset($ret['pub_key'])){
 file_put_contents($rsafile,$ret['pub_key']);
 return $ret['pub_key'];
 }else{
 return null;
 }
 }else{
 return file_get_contents($rsafile);
 }
 }
 /*
 * 发起POST网络请求
 * @params string $url : 请求的url链接地址
 * @params string $data : 数据包
 * @params bool $ssl : 是否加载证书
 * return array $result : 返回的数据结果 
 */
 private function httpsPost($url,$data,$ssl = false)
 {
 $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,$this->sslcert);
 curl_setopt ( $ch,CURLOPT_SSLKEY,$this->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);
 }
 //对参数排序,生成MD5加密签名
 private function getParam($paramArray, $isencode=false)
 {
 $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=".$this->key;
 $sign=strtoupper(md5($stringSignTemp));
 return $sign;
 }
 /*
 * 将xml转换成数组
 * @params xml $xml : xml数据
 * return array $data : 返回数组
 */
 private 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;
 }
 /*
 * 随机字符串
 * @param int $length 长度
 * @param string $type 类型
 * @param int $convert 转换大小写 1大写 0小写
 * @return string
 */
 private function random($length=10,$type='letter',$convert=false)
 {
 $config = array(
 'number'=>'12345670',
 'letter'=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
 'string'=>'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ234567',
 'all'=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345670'
 );
 
 if(!isset($config[$type])) $type = 'letter';
 $string = $config[$type];
 
 $code = '';
 $strlen = strlen($string) -1;
 for($i = 0; $i < $length; $i++){
 $code .= $string{mt_rand(0, $strlen)};
 }
 if(!empty($convert)){
 $code = ($convert > 0)? strtoupper($code) : strtolower($code);
 }
 return $code;
 }
 /*
 * 日志记录
 * @params string $msg : 文字描述
 * @params array $ret : 调用接口返回的数组
 */
 private function errorLog($msg,$ret)
 {
 $path = ROOT_PATH.'runtime/error/';
 if(!is_dir($path)) mkdir($path,0777);
 file_put_contents(ROOT_PATH . 'runtime/error/wxpay.log', "[" . date('Y-m-d H:i:s') . "] ".$msg."," .json_encode($ret).PHP_EOL, FILE_APPEND);
 }
}

3.2、Controller控制层调用,Wxpay.php

<?php
namespace app\home\controller;
use app\home\model\Wechat;
class Wxpay extends Controller
{
 //企业付款到银行卡
 public function payBank()
 {
 $appid = 'wx****d4';
 $appsecret = '37***f0';
 $mchid = '13***2';
 $key = '53***e8';
 $sslcert = ROOT_PATH.'data/cert/apiclient_cert.pem';
 $sslkey = ROOT_PATH.'data/cert/apiclient_key.pem';
 $out_trade_no = date('Ymdhis', time()).substr(floor(microtime()*1000),0,1).rand(0,9);
 $money = 100;
 $enc_bank_no = '62***44';
 $enc_true_name = '张**';
 $bank_name = '中国银行';
 $desc = '企业付款到银行卡测试';
 $wxapi = new Wechat($appid,$appsecret,$mchid,$key,$sslcert,$sslkey);
 $payment_no = $wxapi->payForBank($out_trade_no,$money,$enc_bank_no,$enc_true_name,$bank_name,$desc);
 if($payment_no){
 echo '微信付款到银行卡操作成功,微信订单号:'.$payment_no;
 } else {
 echo '微信付款到银行卡操作失败,请检查日记';
 }
 }
}

四、付款截图

五、注意事项

1、获取的rsa为PKCS1格式需要转成PKCS8,需要执行以下命令

$filename : 从微信服务器上获取的公钥数组

$out_put_filename :转换成PKCS8后的文件保存路径

openssl rsa -RSAPublicKey_in -in <filename> -out <out_put_filename>

下载本文
显示全文
专题