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


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 相关文章推荐
如何用ajax来创建一个XMLHttpRequest对象
Dec 10 Javascript
javascript时间自动刷新实现原理与步骤
Jan 06 Javascript
js阻止事件追加的具体实现
Oct 15 Javascript
JavaScript仿百度图片浏览效果
Nov 23 Javascript
canvas绘制的直线动画
Jan 23 Javascript
js 实现复选框只能选择一项的示例代码
Jan 23 Javascript
用vscode开发vue应用的方法步骤
May 06 Javascript
基于js实现抽红包并分配代码实例
Sep 19 Javascript
微信小程序返回箭头跳转到指定页面实例解析
Oct 08 Javascript
jquery添加div实现消息聊天框
Feb 08 jQuery
JavaScript队列结构Queue实现过程解析
Mar 07 Javascript
原生js实现表格翻页和跳转
Sep 29 Javascript
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
PHP6 先修班 JSON实例代码
2008/08/23 PHP
php中判断字符串是否全是中文或含有中文的实现代码
2011/09/16 PHP
php使用ICQ网关发送手机短信
2013/10/30 PHP
php上传文件并显示上传进度的方法
2015/03/24 PHP
php生成验证码函数
2015/10/20 PHP
详解Yii2.0使用AR联表查询实例
2017/06/16 PHP
PHP unlink与rmdir删除目录及目录下所有文件实例代码
2018/02/07 PHP
在JavaScript中遭遇级联表达式陷阱
2007/03/08 Javascript
JavaScript设计模式之代理模式介绍
2014/12/28 Javascript
JavaScript中闭包的写法和作用详解
2016/06/29 Javascript
js实现文字超出部分用省略号代替实例代码
2016/09/01 Javascript
vue自定义指令实现v-tap插件
2016/11/03 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
Angularjs 双向绑定时字符串的转换成数字类型的问题
2017/06/12 Javascript
BootStrap Validator 根据条件在JS中添加或移除校验操作
2017/10/12 Javascript
Angular开发实践之服务端渲染
2018/03/29 Javascript
微信小程序如何获取手机验证码
2018/11/04 Javascript
使用pm2部署node生产环境的方法步骤
2019/03/09 Javascript
前端Vue项目详解--初始化及导航栏
2019/06/24 Javascript
jquery实现弹窗(系统提示框)效果
2019/12/10 jQuery
Vue路由管理器Vue-router的使用方法详解
2020/02/05 Javascript
js实现文字头像的生成代码
2020/03/07 Javascript
[50:15]VP vs Mineski 2018国际邀请赛淘汰赛BO3 第二场 8.22
2018/08/23 DOTA
Python面向对象思想与应用入门教程【类与对象】
2019/04/12 Python
DRF框架API版本管理实现方法解析
2020/08/21 Python
python如何遍历指定路径下所有文件(按按照时间区间检索)
2020/09/14 Python
HTML5 Web存储方式的localStorage和sessionStorage进行数据本地存储案例应用
2012/12/09 HTML / CSS
银行实习自我鉴定
2013/10/12 职场文书
房地产开发计划书
2014/01/10 职场文书
鲜花方阵解说词
2014/02/13 职场文书
高中生家长寄语大全
2014/04/03 职场文书
硕士毕业论文导师评语
2014/12/31 职场文书
委托书的样本
2015/01/28 职场文书
消夏晚会主持词
2015/06/30 职场文书
2021年pycharm的最新安装教程及基本使用图文详解
2021/04/03 Python
利用Sharding-Jdbc进行分库分表的操作代码
2022/01/22 Java/Android