当前位置:首页 > PHP >

PHP实现微信商户平台打款到银行卡功能

时间:2023-11-01 23:02:07浏览:3579 转载

这种对接功能最重要和最基础的东西是什么?官方接口!

https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay_yhk.php?chapter=25_2


1、在微信商户平台开通企业打款到银行卡,添加服务器IP

2、在微信商户平台下载软件生成证书,得到apiclient_cert.pem和apiclient_key.pem,本处为了方便区别,假定商户号为:1600112233

证书文件分别改名为1600112233_apiclient_cert.pem和1600112233_apiclient_key.pem,都放到/www/wwwroot/Demo/data/Cert/目录下面


3、下面的类保存到Pay.php



class WxPayToBank {

    /*
    使用说明:
    第一步:调用
    $obj = new WxPayToBank();
    $Re = $obj->get_pub_key();
    print_r($Re);
    生成1600112233_apiclient_public_rsa.pem

    第二步:
    openssl rsa -RSAPublicKey_in -in /www/wwwroot/Demo/data/Cert/1600112233_apiclient_public_rsa.pem -pubout
    生成1600112233_apiclient_public.pem
    */

    /**
        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/Demo/data/Cert/1600112233_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/Demo/data/Cert/1600112233_apiclient_public.pem";
        $this->publicKeyRsaPath = "/www/wwwroot/Demo/data/Cert/1600112233_apiclient_public_rsa.pem";
        $this->apiclientCert = "/www/wwwroot/Demo/data/Cert/1600112233_apiclient_cert.pem";
        $this->apiclientKey = "/www/wwwroot/Demo/data/Cert/1600112233_apiclient_key.pem";
        $this->mchId = '1600112233';
        $this->apiKey = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    }


    /*
     * 企业付款到银行卡接口
     * @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 = floatval($amount) * 100;
        $data['amount'] = $amount;
        $data['bank_code'] = $this->getBankCode($bank_name);
        if($desc!=""){
            $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);
        //print_r($dataXML);
        $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 );
        } 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;
        }
    }
}


4、调用上面的类,然后用这点代码先生成1600112233_apiclient_public_rsa.pem


$obj = new WxPayToBank();
$Re = $obj->get_pub_key();
print_r($Re);
5、在linux的ssh下用下面的命令生成出1600112233_apiclient_public.pem,需要手动复制去保存到/www/wwwroot/Demo/data/Cert/1600112233_apiclient_public.pem,或加上 > 1600112233_apiclient_public.pem
openssl rsa -RSAPublicKey_in -in /www/wwwroot/Demo/data/Cert/1600112233_apiclient_public_rsa.pem -pubout

6、调用类文件之后用下面的代码就可以发起向银行卡付款

//支付到银行卡
$obj = new WxPayToBank();
$out_trade_no = "OToB".date('YmdHis');  //商户付款单号
$enc_bank_no = "62220231000XXXXXXXX";      //收款方银行卡号
$enc_true_name  = "XXX";  //收款方用户名
$bank_name = "工商银行";    //收款方开户行
$amount = 2;    //付款金额
$desc = '';     //付款说明

$res = $obj->payBank( $enc_bank_no , $amount , $out_trade_no , $bank_name , $enc_true_name , $desc);
print_r($res);

7、实测这个执行比较慢,几个小时都没有结果,调用类后用下面的代码就可以查询到结果

$obj = new WxPayToBank();
$res = $obj->queryBank("OToB202311011xxx"); //之前发起付款到银行卡的订单号
print_r($res);

来源连接:https://blog.csdn.net/lorraine_40t/article/details/125102174
https://www.cnblogs.com/bluealine/p/11015081.html



上一篇:长图切割后前端显示有空行的解决办法
下一篇:mysql的expire_logs_days不重启怎么设置有效

发表评论

昵称:  验证码:

关于博主

博主

博主:BlueCode

职业:web程序

简介:2002年开始一直从事Web制作,网站运营,会PHP+MYSQL ASP+MSSQL,微信开发