编程语言   发布时间:2022-06-22  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了微信小程序商家转账到零钱V3 WeChatPay OpenAPI SDK大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

一、相关参链接

1、微信小程序商家转账到零钱相关API文档:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_3_1.shtml

2、wechatpay-php sdk 安装以及示例介绍:https://github.com/wechatpay-apiv3/wechatpay-php

3、证书相关参链接(商户API证书、平台证书):https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay7_0.shtml

二、微信小程序商家转账到零钱开发示例(V3)

2.1、安装wechatpay-php sdk

composer require wechatpay/wechatpay

2.2、准备微信商家转账到零钱相关参数

  商户API证书:https://kf.qq.com/faq/161222NneAJf161222U7fARv.html

  商户API证书序列号:登录商户平台【API安全】->【API证书】->【查看证书】,可查看商户API证书序列号;也可通过解析相关商户API证书获取,参证书相关链接

  商户号:可通过微信支付后台获取

  平台证书(明细转账金额 >= 2,000是需要此证书进行敏感信息加密):https://pay.weixin.qq.com/wiki/doc/apiv3/apis/wechatpay5_1.shtml

  APIv3秘钥(使用平台证书是才需要):https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay3_2.shtml

  小程序AppID和AppSecret:可通过微信公众平台登录相关公众号获取。【设置与开发】-> 【基本配置】

2.2、构建SDK客户端实例(详情参wechatpay-php示例)

  注:1、需开启curl、openssl等相关扩展;2、可能会遇到请求报错的情况,请根据报错内容自行百度解决方案

  windows下报错:

  

微信小程序商家转账到零钱V3 WeChatPay OpenAPI SDK

 

  解决方案:下载相关证书 https://curl.se/docs/caextract.html 选个最新的即可

  在php.ini中搜索 curl.cainfo 取消注释,配置下载证书的相关路径,重新相关服务即可

  

微信小程序商家转账到零钱V3 WeChatPay OpenAPI SDK

  下面为构建实例以及转账和查询的参代码,框架实例中使用的为thinkphp6。请还是仔细阅读官方文档

<?php
namespace appapiWeChatPay;


use GuzzlehttpExceptionrequestException;
use thinkexceptionhttpException;
use WeChatPayBuilder;
use WeChatPayCryptoRsa;
use WeChatPayUtilPemUtil;

class XcxPay{
    //证书路径 商户api证书和平台证书存放路径
    private static $certificatePath;
    //商户API证书公钥文件
    private static $merchantPublicFile = 'apiclient_cert.pem';
    //商户API证书私钥文件
    private static $merchantPrivateFile = 'apiclient_key.pem';
    //商户API证书序列号
    private static $merchantCertificateserialNo;
    //商户号
    private static $merchantId;
    //商户平台APIv3秘钥
    private static $apiV3Key;
    //平台证书文件
    private static $platformFile = 'wx_public_cert.pem';
    //平台证书序列号
    private static $platformCertificateserialNo;
    //小程序AppId
    private static $appId;
    //小程序Secret
    private static $appSecret;
    //APIv3 客户端实例
    private static $instance;
    //平台证书接口地址
    private static $platformCertificateUrl;
    //商户API私钥
    private static $merchantPrivateKeyInstance;

    /**
     * 初始化相关数据
     * XcxPay constructor.
     */
    public function __construct(){
        //>>证书存放路径
        self::$certificatePath = app_path().'api/WeChatPay/wx_cert/';
        //>>获取商户API证书序列号
        self::$merchantCertificateserialNo = self::getMerchantCertificate(self::$certificatePath.self::$merchantPublicFile);
        //>>商户号
        self::$merchantId = 'xxxxxxxxxx';
        //>>小程序AppId
        self::$appId = 'xxxxxxxxxxxxxxxxx';
        //>>小程序秘钥(此处不会使用,获取小程序用户openid会用到,请参官方文档即可)
        self::$appSecret = 'xxxxxxxxxxxxxxxxxxxxxx';
        //>>平台证书请求地址
        self::$platformCertificateUrl = 'https://api.mch.weixin.qq.com/v3/certificates';
        //>>apiv3秘钥,解密下载的平台证书时使用
        self::$apiV3Key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
        $merchantPrivateKeyFilePath = 'file://'.self::$certificatePath.self::$merchantPrivateFile;
        self::$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
        //>>判断是否存在平台证书,没有则生成
        if(!file_exists(self::$certificatePath.self::$platformFile)){
            try {
                self::downPlatformCertificate();
            }catch (Exception $e){
                throw new httpException('500','下载平台证书异常!');
            }
        }
        //>>构造一个 APIv3 客户端实例
        self::$instance = self::geTinstance();
    }

    /**
     *小程序商户转账到零钱
     * @param $pay_data  -根据文档生成的转账数据
     * @param bool $is_sensitive_info -是否转账明细额度超过2000
     * @return PsrhttpmessageResponseInterface|String|null
     */
    public function xcxTransferAccounts($pay_data,$is_sensitive_info = false){
        //>>示例数据为转账900000
        $tx_last_money = bcmul(900000,100);
        $pay_data = [
            'appid'=>self::$appId,
            'ouT_Batch_no'=>'GS20220613094335CK9LW4804926900X',
            'batch_name'=>"用户提现",
            'batch_REMARK'=>'用户提现',
            '@R_538_10586@l_amount'=>(int)$tx_last_money,
            '@R_538_10586@l_num'=>1,
            'transfer_detail_list'=>[
                [
                    'out_detail_no'=>'GS20220613094335CK9LW4804926900X',
                    'transfer_amount'=>(int)$tx_last_money,
                    'transfer_REMARK'=>'提现',
                    'user_name'=>self::getEncrypt('小寒'),  //转账明细超过2000需要 详情查看文档
                    'openid'=>'oTiAE46JHDmW43b8_PffJo3hKDsA',
                ]
            ]
        ];
        //>>判断传入数据是否需要敏感数据加密
        $transfer_data = [
            'json' => $pay_data
        ];
        if($is_sensitive_info == true){
            $transfer_data = [
                'json' => $pay_data,
                'headers' => [
                    'Wechatpay-serial' => self::$platformCertificateserialNo, //平台证书序列号
                ],
            ];
        }
        //>>发送请求
        try {
            $resp = self::$instance
                ->chain('v3/transfer/batches')
                ->post($transfer_data);
            /*这个位置就算转账成功也不会在这里输出结果*/
            //echo $resp->getStatusCode();
            //echo $resp->getBody();
            //>>返回对象数据
            return $resp;
        }catch (Exception $e){
            /*进行转账错误处理 两种情况 1.交易请求的异常处理  2非交易状态的异常处理*/
            if ($e instanceof requestException && $e->hasResponse()) {
                $resp = $e->getResponse();
                //返回对象数据
                return $resp;
                /*简单判断示例
                echo $resp->getStatusCode();
                echo $resp->getBody();
                if($resp->getStatusCode() == 200){
                    //>>交易成功 请详细阅读接口文档做出相应处理
                }else{
                    //>>交易失败
                }*/
            }else{
                //>>返回字符串数据
                return $e->getmessage();
            }
        }
    }

    /**
     * 商家明细单号查询明细单
     * @param $ouT_Batch_no
     * @param $out_detail_no
     * @return PsrhttpmessageResponseInterface|String|null
     */
    public function SELEctXcxTransferAccountsRe($ouT_Batch_no,$out_detail_no){
        try {
            //>>其他接口可以参此接口参数拼接
            $resp = self::$instance
                ->v3->transfer->batches->outBatchNo->_ouT_Batch_no_->details->outDetailNo->_out_detail_no_
                ->get([
                    'ouT_Batch_no'=>$ouT_Batch_no,       //商家明细单号
                    'out_detail_no'=>$out_detail_no,     //商家批次单号
                ]);
            //>>返回对象数据
            return $resp;
        }catch (Exception $e){
            if ($e instanceof requestException && $e->hasResponse()) {
                $resp = $e->getResponse();
                return $resp;
            }else{
                return $e->getmessage();
            }
        }
    }

    /**
     * 敏感数据加密 明细金额超过2000时使用 详情参文档
     * @param $str
     * @return String
     */
    private static function getEncrypt($str) {
        $public_key_path = "file://".self::$certificatePath.self::$platformFile;
        $public_key = file_get_contents($public_key_path);
        $encrypted = '';
        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
            //base64编码
            $sign = base64_encode($encrypted);
        } else {
            throw new httpException('500','敏感数据加密异常!');
        }
        return $sign;
    }
    /**
     * 构造一个 APIv3 客户端实例
     * @return WeChatPayBuilderChainable
     */
    private function geTinstance(){
        // 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名(生成签名会使用到 所以放前面了)
        //$merchantPrivateKeyFilePath = 'file://'.self::$certificatePath.self::$merchantPrivateFile;
        //self::$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        // 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
        $platformCertificateFilePath =  'file://'.self::$certificatePath.self::$platformFile;
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
        // 从「微信支付平台证书」中获取「证书序列号」
        self::$platformCertificateserialNo = PemUtil::parseCertificateserialNo($platformCertificateFilePath);
        // 构造一个 APIv3 客户端实例
        $instance = Builder::factory([
            'mchid'      => self::$merchantId,                  // 商户号
            'serial'     => self::$merchantCertificateserialNo, // 「商户API证书」的「证书序列号」
            'privateKey' => self::$merchantPrivateKeyInstance,
            'certs'      => [
                self::$platformCertificateserialNo => $platformPublicKeyInstance,
            ],
        ]);
        return $instance;
    }

    /**
     * 下载以及保存平台证书
     * @return false|String
     * @throws SodiumException
     */
    private static function downPlatformCertificate(){
        $certificate = self::getPlatformCertificate();
        //>>判断是否存在相关证书数据
        if (!isset($certificate['data'])) {
            $error_msg = isset($certificate['message'])?$certificate['message']:'平台证书下载失败!';
            throw new httpException('500',$error_msg);
        }
        $ciphertext = $certificate['data'][0]['encrypt_certificate']['ciphertext'];
        $associatedData = $certificate['data'][0]['encrypt_certificate']['associated_data'];
        $nonceStr = $certificate['data'][0]['encrypt_certificate']['nonce'];
        $data = self::decryptToString($ciphertext, $associatedData, $nonceStr);
        if (!$data) {
           throw new httpException('500','获取证书解密失败');
        }
        //保存平台证书 (https://myssl.com/cert_decode.html)获取证书序列号
        file_put_contents(self::$certificatePath . 'wx_public_cert.pem', $data);
        return $data;
    }

    /**
     * 解密证书
     * @param $ciphertext
     * @param $associatedData
     * @param $nonceStr
     * @return false|String
     * @throws SodiumException
     */
    private static function decryptToString($ciphertext, $associatedData, $nonceStr){
        $str = base64_decode($ciphertext);
        if (strlen($str) <= 16) {
            return '';
        }
        // ext-sodium (default installed on >= php 7.2) 如果没有该函数需要安装sodium扩展
        return sodium_crypto_aead_aes256gcm_decrypt($str, $associatedData, $nonceStr, self::$apiV3Key);
    }
    /**
     * 获取平台证书列表
     * @return mixed
     */
    private static function getPlatformCertificate(){
        //当前时间戳
        $timestamp = time();
        //随机字符串,无要求符合微信生成随机数算法即可
        $nonce_str = self::get32Str();
        //报文主体,此接口为空
        $body = '';
        //>>生成平台证书请求签名的数据
        $sign_param = [
            'http_method'=>'GET',
            'timestamp'=>$timestamp,
            'body'=>$body,
            'nonce_str'=>$nonce_str,
        ];
        $sign = self::sign($sign_param);
        $header = [
            'Authorization:'.$sign,
            'Accept:application/json',
            'User-Agent:'.self::$merchantId,
        ];
        $result = self::wxCurl(self::$platformCertificateUrl,$header, '', 'GET');
        $result = json_decode($result, true);
        return $result;
    }

    /**
     * 生成签名
     * @param $sign_param
     * @return String
     */
    private static function sign($sign_param){
        //解析url
        $url_parts = parse_url(self::$platformCertificateUrl);
        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
        $message = $sign_param['http_method']."n".
            $canonical_url."n".
            $sign_param['timestamp']."n".
            $sign_param['nonce_str']."n".
            $sign_param['body']."n";
        openssl_sign($message, $raw_sign, self::$merchantPrivateKeyInstance, 'sha256WithRSAEncryption');
        $sign = base64_encode($raw_sign);
        $scheR_41_11845@a = 'WECHATPAY2-SHA256-RSA2048 ';
        $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            self::$merchantId, $sign_param['nonce_str'], $sign_param['timestamp'], self::$merchantCertificateserialNo, $sign);
        return $scheR_41_11845@a.$token;
    }

    /**
     *发送
     * @param $url
     * @param $header
     * @param array $data
     * @param String $method
     * @param int $time_out
     * @return bool|String
     */
    private static function wxCurl($url, $header, $data = [], $method = 'POST', $time_out = 3){
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_httpHEADER, $header);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_TIMEOUT, $time_out);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        if ($method == 'POST') {
            curl_setopt($curl, CURLOPT_POST, true);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        // 执行操作
        $result = curl_exec($curl);
        curl_close($curl);
        return $result;
    }

    /**
     * 根据商户api证书获取商户api证书序列号
     * @param $filepath
     * @return mixed
     */
    private static function getMerchantCertificate($filepath) {
        try {
            $resource = openssl_x509_read(file_get_contents($filepath));
            $resource_arr = openssl_x509_parse($resource);
            return strtolower($resource_arr['serialnumberHex']);
        }catch (Exception $e){
            throw new httpException('500','解析商户API证书序列号异常!');
        }
    }

    /**
     * 生成随机18位字符串加上当前日期14位字符串 共32位随机字符串数据
     * @return String
     */
    private static function get32Str(){
        $str = 'ABCDEFGHIJKLMNOPQLstuVWXYZ0123456789';
        return mb_substr(str_shuffle($str),0,18).date('ymdHis',time());
    }
}

 

 

 

 

  

 

 

 

 

 

 

 

  

 

大佬总结

以上是大佬教程为你收集整理的微信小程序商家转账到零钱V3 WeChatPay OpenAPI SDK全部内容,希望文章能够帮你解决微信小程序商家转账到零钱V3 WeChatPay OpenAPI SDK所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。