1 在商户平台开通企业打款到银行卡,添加服务器ip
2 获取公钥,格式为PKCS#1 转PKCS#8
PKCS#1 转 PKCS#8:
openssl rsa -RSAPublicKey_in -in <filename> -pubout
PKCS#8 转 PKCS#1:
openssl rsa -pubin -in <filename> -RSAPublicKey_out
* openssl rsa -RSAPublicKey_in -in /www/wwwroot/xx/extend/wxcert/apiclient_public_rsa.pem -pubout
3 拿到pkcs8的公钥放到public.pem文件
4 类文件
<?php
declare (strict_types = 1);
namespace app\services\tool;
use app\services\BaseServices;
use think\facade\Db;
/**
* 微信零钱打款到银行卡
* Class JobServices
* @package app\services\tool
*/
class WxBankServices extends BaseServices {
/**
* PKCS#1 转 PKCS#8:
openssl rsa -RSAPublicKey_in -in <filename> -pubout
PKCS#8 转 PKCS#1:
openssl rsa -pubin -in <filename> -RSAPublicKey_out
* openssl rsa -RSAPublicKey_in -in /www/wwwroot/xx/extend/wxcert/apiclient_public_rsa.pem -pubout
*/
protected $publicKeyPath; // public key 路径
protected $publicKeyRsaPath; // public rsa key 路径
protected $apiKey; // 密钥
protected $apiclientCert; // 公钥
protected $apiclientKey; // 私钥
protected $mchId; // 商户号
public function __construct()
{
$this->publicKeyPath = "/www/wwwroot/xx/extend/wxcert/apiclient_public.pem";
$this->publicKeyRsaPath = "/www/wwwroot/xx/extend/wxcert/apiclient_public_rsa.pem";
$this->apiclientCert = "/www/wwwroot/xx/extend/wxcert/apiclient_cert.pem";
$this->apiclientKey = "/www/wwwroot/xx/extend/wxcert/apiclient_key.pem";
$this->mchId = sysconfig('pay','wx_mach_id');
$this->apiKey = sysconfig('pay', 'wx_key');
$this->accessKeyId = sysconfig('sms','alisms_access_key_id');
$this->accessKeySecret = sysconfig('sms','alisms_access_key_secret');
}
/*
* 企业付款到银行卡接口
* @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 payBank( $enc_bank_no , $amount , $out_trade_no , $bank_name , $enc_true_name , $desc="" ) {
$amount = (int)$amount * 100;
$data['amount'] = $amount;
$data['bank_code'] = $this->getBankCode($bank_name);
$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'] = md5( (string)time() );
$data['partner_trade_no'] = $out_trade_no;
$sign = $this->getParam( $data );
$data['sign'] = $sign;
$dataXML = $this->arraytoxml($data);
$url = 'https://api.mch.weixin.qq.com/mmpaysptrans/pay_bank';
$ret = $this->httpsPost($url, $dataXML, true);
// echo "<pre>";var_dump($ret);die;
if ($ret['return_code'] == 'SUCCESS' && $ret['result_code'] == 'SUCCESS' && $ret['err_code'] == 'SUCCESS') {
return array( "code"=>1 , "data"=>$ret['payment_no'] );
} else {
return array( "code"=>0 , "data"=>$ret );
}
}
/*
* 获取公钥,格式为PKCS#1 转PKCS#8
* openssl rsa -RSAPublicKey_in -in pubkey.pem -pubout > newpubkey.pem
* */
public function get_pub_key() {
$data['mch_id'] = $this->mchId;
$data['nonce_str'] = md5( (string)time() );
$sign = $this->getParam($data);
$data['sign'] = $sign;
$dataXML = $this->arraytoxml($data);
$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( $this->publicKeyRsaPath , $ret['pub_key']);
return $ret['pub_key'];
} else {
return null;
}
}
/**
* 公钥加密,银行卡号和姓名需要RSA算法加密
* @param string $data 需要加密的字符串,银行卡/姓名
* @return null|string 加密后的字符串
*/
public function publicEncrypt($data) {
$public_key = file_get_contents( $this->publicKeyPath );
$encrypt_data = '';
$r = openssl_public_encrypt($data, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING);
$encrypted = base64_encode($encrypted);
return $encrypted;
// $public_key = "-----BEGIN PUBLIC KEY-----\n" . wordwrap($public_key, 64, "\n", true) . "\n-----END PUBLIC KEY-----";
// $public_key = "-----BEGIN PUBLIC KEY-----\n" . $public_key . "\n-----END PUBLIC KEY-----";
// $public_key = openssl_pkey_get_public( $public_key );
// $r = openssl_public_encrypt( $data, $encrypted, $public_key );
// echo "<pre>";var_dump( $r , $encrypted );die;
}
/**
* [arraytoxml 将数组转换成xml格式(简单方法):]
* @param [type] $data [数组]
* @return [type] [array 转 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;
}
/*
* 发起POST网络请求
* @params string $url : 请求的url链接地址
* @params string $data : 数据包
* @params bool $ssl : 是否加载证书
* return array $result : 返回的数据结果
*/
public 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->apiclientCert );
curl_setopt($ch, CURLOPT_SSLKEY, $this->apiclientKey );
}
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);
}
public 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->apiKey;
$sign = strtoupper(md5($stringSignTemp));
return $sign;
}
/*
* 将xml转换成数组
* @params xml $xml : xml数据
* return array $data : 返回数组
*/
public 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;
}
/*
* 查询付款到银行卡状态
* @params string $out_trade_no : 商户订单号
* return array $ret:查询状态
*/
public function queryBank($out_trade_no) {
$data['mch_id'] = $this->mchId;
$data['nonce_str'] = md5( (string)time() );
$data['partner_trade_no'] = $out_trade_no;
$sign = $this->getParam($data);
$data['sign'] = $sign;
$dataXML = $this->arraytoxml($data);
$url = 'https://api.mch.weixin.qq.com/mmpaysptrans/query_bank';
$ret = $this->httpsPost($url, $dataXML, true);
// echo "<pre>";var_dump($ret);die;
if ($ret['return_code'] == 'SUCCESS' && $ret['result_code'] == 'SUCCESS' && $ret['err_code'] == 'SUCCESS') {
return $ret;
return array( "code"=>1 , "data"=>$ret );
} else {
return array( "code"=>0 , "data"=>$ret );
}
}
/**
* @param $string
* @return string|string[]
* url安全解码
*/
public static function url_safe_decode($string)
{
$data = str_replace(['-','_'], ['+','/'], $string);
$mod4 = strlen($data) % 4;
if ($mod4)
{
$data .= substr('====', $mod4);
}
return $data;
}
/**
* @param $string
* @return string|string[]
* url安全转码
*/
public static function url_safe_encode($string)
{
return str_replace(['+','/','='], ['-','_',''], $string);
}
/*
* 银行编号列表,详情参考:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=25_4
* @params string $bank_name : 银行名称,4个汉字
* return int $bank_code : 银行编码
*/
public function getBankCode( $bank_name ) {
$BankArr = [];
$BankArr["工商银行"] = 1002;
$BankArr["农业银行"] = 1005;
$BankArr["建设银行"] = 1003;
$BankArr["中国银行"] = 1026;
$BankArr["交通银行"] = 1020;
$BankArr["招商银行"] = 1001;
$BankArr["邮储银行"] = 1066;
$BankArr["民生银行"] = 1006;
$BankArr["平安银行"] = 1010;
$BankArr["中信银行"] = 1021;
$BankArr["浦发银行"] = 1004;
$BankArr["兴业银行"] = 1009;
$BankArr["光大银行"] = 1022;
$BankArr["广发银行"] = 1027;
$BankArr["华夏银行"] = 1025;
$BankArr["上海银行"] = 1024;
$BankArr["宁波银行"] = 1056;
$BankArr["北京银行"] = 4836;
$BankArr["南京银行"] = 1054;
$BankArr["长子县融汇村镇银行"] = 4755;
$BankArr["长沙银行"] = 4216;
$BankArr["浙江泰隆商业银行"] = 4051;
$BankArr["中原银行"] = 4753;
$BankArr["企业银行(中国)"] = 4761;
$BankArr["顺德农商银行"] = 4036;
$BankArr["衡水银行"] = 4752;
$BankArr["长治银行"] = 4756;
$BankArr["大同银行"] = 4767;
$BankArr["河南省农村信用社"] = 4115;
$BankArr["宁夏黄河农村商业银行"] = 4150;
$BankArr["山西省农村信用社"] = 4156;
$BankArr["安徽省农村信用社"] = 4166;
$BankArr["甘肃省农村信用社"] = 4157;
$BankArr["天津农村商业银行"] = 4153;
$BankArr["广西壮族自治区农村信用社"] = 4113;
$BankArr["陕西省农村信用社"] = 4108;
$BankArr["深圳农村商业银行"] = 4076;
$BankArr["宁波鄞州农村商业银行"] = 4052;
$BankArr["浙江省农村信用社联合社"] = 4764;
$BankArr["江苏省农村信用社联合社"] = 4217;
$BankArr["江苏紫金农村商业银行股份有限公司"] = 4072;
$BankArr["北京中关村银行股份有限公司"] = 4769;
$BankArr["星展银行(中国)有限公司"] = 4778;
$BankArr["枣庄银行股份有限公司"] = 4766;
$BankArr["海口联合农村商业银行股份有限公司"] = 4758;
$BankArr["南洋商业银行(中国)有限公司"] = 4763;
if( isset( $BankArr[ $bank_name ] ) ) {
return $BankArr[ $bank_name ];
}else{
return 0;
}
}
}
5 调用
public function t1() {
$WxBankServices = app()->make( WxBankServices::class );
// 先生成证书
// $ret = $WxBankServices->get_pub_key();
// echo "<pre>";var_dump( $ret );die;
// 查询
$out_trade_no1 = "202206";
$out_trade_no2 = "202206";
$ret1 = $WxBankServices->queryBank( $out_trade_no1 );
$ret2 = $WxBankServices->queryBank( $out_trade_no2 );
echo "<pre>";var_dump( $ret1,$ret2 );die;
// 转账
$enc_bank_no = "622";
$bank_name = "建设银行";
$enc_true_name = "aa";
$desc = "测试微信商户打款到银行卡";
$amount = 20;
$out_trade_no = date('Ymdhis', time()) . rand(100000, 999999);
$ret = $WxBankServices->payBank( $enc_bank_no , $amount , $out_trade_no , $bank_name , $enc_true_name , $desc );
echo "<pre>";var_dump( $ret );die;
}
6 查询案例
<pre>array(16) {
["return_code"]=>
string(7) "SUCCESS"
["return_msg"]=>
string(12) "查询成功"
["result_code"]=>
string(7) "SUCCESS"
["err_code"]=>
string(7) "SUCCESS"
["err_code_des"]=>
string(12) "查询成功"
["mch_id"]=>
string(10) "1618"
["partner_trade_no"]=>
string(20) "20220"
["payment_no"]=>
string(35) "1000138826773"
["bank_no_md5"]=>
string(32) "24745DC562E013"
["true_name_md5"]=>
string(32) "68EC6B22123C0E"
["amount"]=>
string(4) "2000"
["status"]=>
string(10) "PROCESSING"
["cmms_amt"]=>
string(3) "100"
["create_time"]=>
string(19) "2022-06-02 15:42:16"
["pay_succ_time"]=>
array(0) {
}
["reason"]=>
array(0) {
}
}