微信开发之企业付款到银行卡接口开发的示例代码


Posted in Javascript onSeptember 18, 2018

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

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

微信开发之企业付款到银行卡接口开发的示例代码

二、应用环境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){//加密成功,返回base64编码的字符串
      return base64_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'=>'1234567890',
      'letter'=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
      'string'=>'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789',
      'all'=>'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
    );
  
    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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用Jquery实现可编辑表格并用AJAX提交到服务器修改数据
Dec 27 Javascript
jQuery 1.8 Release版本发布了
Aug 14 Javascript
兼容主流浏览器的iframe自适应高度js脚本
Jan 10 Javascript
JavaScript操作cookie类实例
Mar 31 Javascript
EditPlus中的正则表达式 实战(4)
Dec 15 Javascript
vue 打包后的文件部署到express服务器上的方法
Aug 09 Javascript
jQuery实现表格冻结顶栏效果
Aug 20 jQuery
JS+Canvas绘制动态时钟效果
Nov 10 Javascript
jQuery中内容过滤器简单用法示例
Mar 31 jQuery
对vue v-if v-else-if v-else 的简单使用详解
Sep 29 Javascript
如何用vue-cli3脚手架搭建一个基于ts的基础脚手架的方法
Dec 12 Javascript
vue 实现上传组件
May 31 Vue.js
vue自定义tap指令及tap事件的实现
Sep 18 #Javascript
Vue条件循环判断+计算属性+绑定样式v-bind的实例
Sep 18 #Javascript
浅谈vue同一页面中拥有两个表单时,的验证问题
Sep 18 #Javascript
记React connect的几种写法(小结)
Sep 18 #Javascript
Vue循环组件加validate多表单验证的实例
Sep 18 #Javascript
vue2.0 + ele的循环表单及验证字段方法
Sep 18 #Javascript
JavaScript指定断点操作实例教程
Sep 18 #Javascript
You might like
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
php download.php实现代码 跳转到下载文件(response.redirect)
2009/08/26 PHP
如何设置mysql允许外网访问
2013/06/04 PHP
Laravel中日期时间处理包Carbon的简单使用
2017/09/21 PHP
PHP实现的折半查找算法示例
2017/12/19 PHP
PHP使用openssl扩展实现加解密方法示例
2020/02/20 PHP
10个基于Jquery的幻灯片插件教程
2010/10/29 Javascript
使用js检测浏览器的实现代码
2013/05/14 Javascript
ExtJS4中的requires使用方法示例介绍
2013/12/03 Javascript
基于jquery实现发送文章到手机的代码
2014/12/26 Javascript
Bootstrap中CSS的使用方法
2016/02/17 Javascript
js实现的xml对象转json功能示例
2016/12/24 Javascript
AngularJS中的按需加载ocLazyLoad示例
2017/01/11 Javascript
分享一道关于闭包、bind和this的面试题
2017/02/20 Javascript
SVG描边动画
2017/02/23 Javascript
vue.js实现含搜索的多种复选框(附源码)
2017/03/23 Javascript
JavaScript实现前端实时搜索功能
2020/03/26 Javascript
select自定义小三角样式代码(实用总结)
2017/08/18 Javascript
Angular4集成ng2-file-upload的上传组件
2018/03/14 Javascript
JavaScript实现的前端AES加密解密功能【基于CryptoJS】
2018/08/28 Javascript
微信小程序蓝牙连接小票打印机实例代码详解
2019/06/03 Javascript
vue 实现tab切换保持数据状态
2020/07/21 Javascript
python根据出生年份简单计算生肖的方法
2015/03/27 Python
python编写简易聊天室实现局域网内聊天功能
2018/07/28 Python
python3+opencv3识别图片中的物体并截取的方法
2018/12/05 Python
python修改txt文件中的某一项方法
2018/12/29 Python
Python网络爬虫四大选择器用法原理总结
2020/06/01 Python
解决pytorch 交叉熵损失输出为负数的问题
2020/07/07 Python
详解java调用python的几种用法(看这篇就够了)
2020/12/10 Python
比利时香水网上商店:NOTINO
2018/03/28 全球购物
英国派对礼服和连衣裙购物网站:TFNC London
2018/07/07 全球购物
具有防紫外线功能的高性能钓鱼服装:Hook&Tackle
2018/08/16 全球购物
汽车机修工岗位职责
2014/03/06 职场文书
竞聘演讲稿精彩开头和结尾
2014/05/14 职场文书
大学生迟到检讨书500字
2014/10/17 职场文书
文艺演出主持词
2015/07/01 职场文书