随着微信支付的升级,PHP微信支付类V3接口也来了

 6510

不知不觉微信支付也更新了,接口版本也升级到了V3,跟着微信的升级,将个人使用微信支付类也进行了升级

V3微信支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml

使用方法还和之前的一样(V2微信支付),直接传递参数就可使用:

新版新增了composer安装,便于集成框架使用(Github地址):

  1. composer require fengkui/pay

首先把配置文件填写完整(细心不要填错,否则会导致签名错误):

  1. # 微信支付配置
  2. $wechatConfig = [
  3.     'xcxid'         => '', // 小程序 appid
  4.     'appid'         => '', // 微信支付 appid
  5.     'mchid'         => '', // 微信支付 mch_id 商户收款账号
  6.     'key'           => '', // 微信支付 apiV3key(尽量包含大小写字母,否则验签不通过)
  7.     'appsecret'     => '', // 公众帐号 secert (公众号支付获取 code 和 openid 使用)
  8.  
  9.     'notify_url'    => '', // 接收支付状态的连接  改成自己的回调地址
  10.     'redirect_url'  => '', // 公众号支付,调起支付页面
  11.  
  12.     'serial_no'     => '', // 证书序列号
  13.     'cert_client'   => './cert/apiclient_cert.pem', // 证书(退款,红包时使用)
  14.     'cert_key'      => './cert/apiclient_key.pem', // 商户私钥(Api安全中下载)
  15.     'public_key'    => './cert/public_key.pem', // 平台公钥(调动证书列表,自动生成)
  16. ];

支付类封装相关方法:


method描述
jsJSAPI下单
appAPP支付
h5H5支付
scanNavicat支付
xcx小程序支付
query查询订单
close关闭订单
refund申请退款
notify支付结果通知

使用方法(这里已小程序支付为示例):

  1. <?php
  2. require_once('./vendor/autoload.php');
  3.  
  4. $config = []; // 支付配置
  5. $order = [
  6.     'order_sn' => time(), // 订单编号
  7.     'total_amount' => 1, // 订单金额(分)
  8.     'body' => '测试商品', // 商品名称
  9.     'openid' => '', // 用户openid
  10.     // 'type' => 'Wap',
  11. ];
  12.  
  13. $wechat = new fengkui\Pay\Wechat($config);
  14. $re = $wechat->xcx($order);
  15. die(json_encode($re)); // JSON化直接返回小程序客户端

如下代码是封装好的完整支付类文件(Wechat.php),可以根据自己需求随意修改,详细的使用方法后期会有文档:

  1. <?php
  2. /**
  3.  * @Author: [FENG] <1161634940@qq.com>
  4.  * @Date:   2019-09-06 09:50:30
  5.  * @Last Modified by:   [FENG] <1161634940@qq.com>
  6.  * @Last Modified time: 2021-07-12 18:24:18
  7.  */
  8. namespace fengkui\Pay;
  9.  
  10. use Exception;
  11. use RuntimeException;
  12. use fengkui\Supports\Http;
  13.  
  14. /**
  15.  * Wechat 微信支付
  16.  * 新版(V3)接口(更新中)
  17.  */
  18. class Wechat
  19. {
  20.     const AUTH_TAG_LENGTH_BYTE = 16;
  21.  
  22.     // 新版相关接口
  23.     // GET 获取平台证书列表
  24.     private static $certificatesUrl = 'https://api.mch.weixin.qq.com/v3/certificates';
  25.     // 统一下订单管理
  26.     private static $transactionsUrl = 'https://api.mch.weixin.qq.com/v3/pay/transactions/';
  27.     // 申请退款
  28.     private static $refundUrl = 'https://api.mch.weixin.qq.com/v3/refund/domestic/refunds';
  29.     // 静默授权,获取code
  30.     private static $authorizeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize';
  31.     // 通过code获取access_token以及openid
  32.     private static $accessTokenUrl = 'https://api.weixin.qq.com/sns/oauth2/access_token';
  33.  
  34.     // 支付完整配置
  35.     private static $config = array(
  36.         'xcxid'         => '', // 小程序appid
  37.         'appid'         => '', // 微信支付appid
  38.         'mchid'         => '', // 微信支付 mch_id 商户收款账号
  39.         'key'           => '', // 微信支付 apiV3key(尽量包含大小写字母,否则验签不通过)
  40.         'appsecret'     => '', // 公众帐号 secert (公众号支付获取code 和 openid使用)
  41.  
  42.         'notify_url'    => '', // 接收支付状态的连接  改成自己的回调地址
  43.         'redirect_url'  => '', // 公众号支付,调起支付页面
  44.  
  45.         'serial_no'     => '', // 证书序列号
  46.         'cert_client'   => './cert/apiclient_cert.pem', // 证书(退款,红包时使用)
  47.         'cert_key'      => './cert/apiclient_key.pem', // 商户私钥(Api安全中下载)
  48.         'public_key'    => './cert/public_key.pem', // 平台公钥(调动证书列表,自动生成)
  49.     );
  50.  
  51.     /**
  52.      * [__construct 构造函数]
  53.      * @param [type] $config [传递微信支付相关配置]
  54.      */
  55.     public function __construct($config=NULL, $referer=NULL){
  56.         $config && self::$config = array_merge(self::$config, $config);
  57.     }
  58.  
  59.     /**
  60.      * [unifiedOrder 统一下单]
  61.      * @param  [type]  $order [订单信息(必须包含支付所需要的参数)]
  62.      * @param  boolean $type  [区分是否是小程序,是则传 true]
  63.      * @return [type]         [description]
  64.      * $order = array(
  65.      *      'body'         => '', // 产品描述
  66.      *      'order_sn'     => '', // 订单编号
  67.      *      'total_amount' => '', // 订单金额(分)
  68.      * );
  69.      */
  70.     public static function unifiedOrder($order, $type=false)
  71.     {
  72.         $config = array_filter(self::$config);
  73.  
  74.         // 获取配置项
  75.         $params = array(
  76.             'appid'         => $type ? $config['xcxid'] : $config['appid'], // 由微信生成的应用ID
  77.             'mchid'         => $config['mchid'], // 直连商户的商户号
  78.             'description'   => $order['body'], // 商品描述
  79.             'out_trade_no'  => (string)$order['order_sn'], // 商户系统内部订单号
  80.             'notify_url'    => $config['notify_url'], // 通知URL必须为直接可访问的URL
  81.             'amount'        => ['total' => (int)$order['total_amount'], 'currency' => 'CNY'], // 订单金额信息
  82.         );
  83.  
  84.         !empty($order['attach']) && $params['attach'] = $order['attach']; // 附加数据
  85.         if (!empty($order['time_expire'])) { // 订单失效时间
  86.             preg_match('/[年\/-]/', $order['time_expire']) && $order['time_expire'] = strtotime($order['time_expire']);
  87.             $time = $order['time_expire'] > time() ? $order['time_expire'] : $order['time_expire'] + time();
  88.             $params['time_expire'] = date(DATE_ATOM, $time);
  89.         }
  90.  
  91.         if (!in_array($order['type'], ['native'])) {
  92.             !empty($order['openid']) && $params['payer'] = ['openid' => $order['openid']];
  93.             $params['scene_info'] = ['payer_client_ip' => self::get_ip()];
  94.         }
  95.  
  96.         if (in_array($order['type'], ['iOS', 'Android', 'Wap'])) {
  97.             $params['scene_info']['h5_info'] = ['type' => $order['type']];
  98.             $url = self::$transactionsUrl . 'h5'; // 拼接请求地址
  99.         } else {
  100.             $url = self::$transactionsUrl . strtolower($order['type']); // 拼接请求地址
  101.         }
  102.  
  103.         $header = self::createAuthorization($url, $params, 'POST');
  104.         $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);
  105.         $result = json_decode($response, true);
  106.         if (isset($result['code']) && isset($result['message'])) {
  107.             throw new \Exception("[" . $result['code'] . "] " . $result['message']);
  108.         }
  109.  
  110.         return $result;
  111.     }
  112.  
  113.     /**
  114.      * [query 查询订单]
  115.      * @param  [type]  $orderSn [订单编号]
  116.      * @param  boolean $type    [微信支付订单编号,是否是微信支付订单号]
  117.      * @return [type]           [description]
  118.      */
  119.     public static function query($orderSn, $type = false)
  120.     {
  121.         $config = self::$config;
  122.         $url = self::$transactionsUrl . ($type ? 'id/' : 'out-trade-no/') . $orderSn . '?mchid=' . $config['mchid'];
  123.         $params = '';
  124.  
  125.         $header = self::createAuthorization($url, $params, 'GET');
  126.         $response = Http::get($url, $params, $header);
  127.         $result = json_decode($response, true);
  128.  
  129.         return $result;
  130.     }
  131.  
  132.     /**
  133.      * [close 关闭订单]
  134.      * @param  [type] $orderSn [微信支付订单编号]
  135.      * @return [type]          [description]
  136.      */
  137.     public static function close($orderSn)
  138.     {
  139.         $config = self::$config;
  140.         $url = self::$transactionsUrl . 'out-trade-no/' . $orderSn . '/close';
  141.         $params['mchid'] = $config['mchid'];
  142.  
  143.         $header = self::createAuthorization($url, $params, 'POST');
  144.         $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);
  145.         $result = json_decode($response, true);
  146.  
  147.         return true;
  148.     }
  149.  
  150.     /**
  151.      * [js 获取jssdk需要用到的数据]
  152.      * @param  [type] $order [订单信息数组]
  153.      * @return [type]        [description]
  154.      */
  155.     public static function js($order=[]){
  156.         $config = self::$config;
  157.         if (!is_array($order) || count($order) < 3)
  158.             die("订单数组信息缺失!");
  159.         if (count($order) == 4 && !empty($order['openid'])) {
  160.             $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息)
  161.             return $data;
  162.         }
  163.         $code = !empty($order['code']) ? $order['code'] : ($_GET['code'] ?? '');
  164.         $redirectUri = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . rtrim($_SERVER['REQUEST_URI'], '/') . '/'; // 重定向地址
  165.  
  166.         $params = ['appid' => $config['appid']];
  167.         // 如果没有get参数没有code;则重定向去获取code;
  168.         if (empty($code)) {
  169.             $params['redirect_uri'] = $redirectUri; // 返回的url
  170.             $params['response_type'] = 'code';
  171.             $params['scope'] = 'snsapi_base';
  172.             $params['state'] = $order['order_sn']; // 获取订单号
  173.  
  174.             $url = self::$authorizeUrl . '?'. http_build_query($params) .'#wechat_redirect';
  175.         } else {
  176.             $params['secret'] = $config['appsecret'];
  177.             $params['code'] = $code;
  178.             $params['grant_type'] = 'authorization_code';
  179.  
  180.             $response = Http::get(self::$accessTokenUrl, $params); // 进行GET请求
  181.             $result = json_decode($response, true);
  182.  
  183.             $order['openid'] = $result['openid']; // 获取到的openid
  184.             $data = self::xcx($order, false, false); // 获取支付相关信息(获取非小程序信息)
  185.  
  186.             if (!empty($order['code'])) {
  187.                 return $data;
  188.             }
  189.  
  190.             $url = $config['redirect_url'] ?? $redirectUri;
  191.             $url .= '?data=' . json_encode($data, JSON_UNESCAPED_UNICODE);
  192.         }
  193.         header('Location: '. $url);
  194.         die;
  195.     }
  196.  
  197.     /**
  198.      * [app 获取APP支付需要用到的数据]
  199.      * @param  [type]  $order [订单信息数组]
  200.      * @return [type]         [description]
  201.      */
  202.     public static function app($order=[], $log=false)
  203.     {
  204.         if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body'])){
  205.             die("订单数组信息缺失!");
  206.         }
  207.  
  208.         $order['type'] = 'app'; // 获取订单类型,用户拼接请求地址
  209.         $result = self::unifiedOrder($order, true);
  210.         if (!empty($result['prepay_id'])) {
  211.             $data = array (
  212.                 'appId'     => self::$config['appid'], // 微信开放平台审核通过的移动应用appid
  213.                 'timeStamp' => (string)time(),
  214.                 'nonceStr'  => self::get_rand_str(32, 0, 1), // 随机32位字符串
  215.                 'prepayid'  => $result['prepay_id'],
  216.             );
  217.             $data['paySign'] = self::makeSign($data);
  218.             $data['partnerid'] = $config['mchid'];
  219.             $data['package'] = 'Sign=WXPay';
  220.             return $data; // 数据小程序客户端
  221.         } else {
  222.             return $log ? $result : false;
  223.         }
  224.     }
  225.  
  226.     /**
  227.      * [h5 微信H5支付]
  228.      * @param  [type] $order [订单信息数组]
  229.      * @return [type]        [description]
  230.      */
  231.     public static function h5($order=[], $log=false)
  232.     {
  233.         if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body']) || empty($order['type']) || !in_array(strtolower($order['type']), ['ios', 'android', 'wap'])){
  234.             die("订单数组信息缺失!");
  235.         }
  236.         $result = self::unifiedOrder($order);
  237.         if (!empty($result['h5_url'])) {
  238.             return $result['h5_url']; // 返回链接让用户点击跳转
  239.         } else {
  240.             return $log ? $result : false;
  241.         }
  242.     }
  243.  
  244.     /**
  245.      * [xcx 获取jssdk需要用到的数据]
  246.      * @param  [type]  $order [订单信息数组]
  247.      * @param  boolean $log   [description]
  248.      * @param  boolean $type  [区分是否是小程序,默认 true]
  249.      * @return [type]         [description]
  250.      */
  251.     public static function xcx($order=[], $log=false, $type=true)
  252.     {
  253.         if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body']) || empty($order['openid'])){
  254.             die("订单数组信息缺失!");
  255.         }
  256.  
  257.         $order['type'] = 'jsapi'; // 获取订单类型,用户拼接请求地址
  258.         $config = self::$config;
  259.         $result = self::unifiedOrder($order, $type);
  260.         if (!empty($result['prepay_id'])) {
  261.             $data = array (
  262.                 'appId'     => $type ? $config['xcxid'] : $config['appid'], // 由微信生成的应用ID
  263.                 'timeStamp' => (string)time(),
  264.                 'nonceStr'  => self::get_rand_str(32, 0, 1), // 随机32位字符串
  265.                 'package'   => 'prepay_id='.$result['prepay_id'],
  266.             );
  267.             $data['paySign'] = self::makeSign($data);
  268.             $data['signType'] = 'RSA';
  269.             return $data; // 数据小程序客户端
  270.         } else {
  271.             return $log ? $result : false;
  272.         }
  273.     }
  274.  
  275.     /**
  276.      * [scan 微信扫码支付]
  277.      * @param  [type] $order [订单信息数组]
  278.      * @return [type]        [description]
  279.      */
  280.     public static function scan($order=[], $log=false)
  281.     {
  282.         if(empty($order['order_sn']) || empty($order['total_amount']) || empty($order['body'])){
  283.             die("订单数组信息缺失!");
  284.         }
  285.         $order['type'] = 'native'; // Native支付
  286.         $result = self::unifiedOrder($order);
  287.  
  288.         if (!empty($result['code_url'])) {
  289.             return urldecode($result['code_url']); // 返回链接让用户点击跳转
  290.         } else {
  291.             return $log ? $result : false;
  292.         }
  293.     }
  294.  
  295.     /**
  296.      * [notify 回调验证]
  297.      * @return [array] [返回数组格式的notify数据]
  298.      */
  299.     public static function notify($server = [], $response = [])
  300.     {
  301.         $config = self::$config;
  302.         $server = $server ?? $_SERVER;
  303.         $response = $response ?? file_get_contents('php://input', 'r');
  304.         if (empty($response) || empty($server['HTTP_WECHATPAY_SIGNATURE'])) {
  305.             return false;
  306.         }
  307.         $body = [
  308.             'timestamp' => $server['HTTP_WECHATPAY_TIMESTAMP'],
  309.             'nonce' => $server['HTTP_WECHATPAY_NONCE'],
  310.             'data' => $response,
  311.         ];
  312.         // 验证应答签名
  313.         $verifySign = self::verifySign($body, trim($server['HTTP_WECHATPAY_SIGNATURE']), trim($server['HTTP_WECHATPAY_SERIAL']));
  314.         if (!$verifySign) {
  315.             die("签名验证失败!");
  316.         }
  317.         $result = json_decode($response, true);
  318.         if (empty($result) || $result['event_type'] != 'TRANSACTION.SUCCESS' || $result['summary'] != '支付成功') {
  319.             return false;
  320.         }
  321.         // 加密信息
  322.         $associatedData = $result['resource']['associated_data'];
  323.         $nonceStr = $result['resource']['nonce'];
  324.         $ciphertext = $result['resource']['ciphertext'];
  325.         $data = $result['resource']['ciphertext'] = self::decryptToString($associatedData, $nonceStr, $ciphertext);
  326.  
  327.         return json_decode($data, true);
  328.     }
  329.  
  330.     /**
  331.      * [refund 微信支付退款]
  332.      * @param  [type] $order [订单信息]
  333.      * @param  [type] $type  [是否是小程序]
  334.      */
  335.     public static function refund($order)
  336.     {
  337.         $config = self::$config;
  338.         if(empty($order['refund_sn']) || empty($order['refund_amount']) || (empty($order['order_sn']) && empty($order['transaction_id']))){
  339.             die("订单数组信息缺失!");
  340.         }
  341.  
  342.         $params = array(
  343.             'out_refund_no' => (string)$order['refund_sn'], // 商户退款单号
  344.             'funds_account' => 'AVAILABLE', // 退款资金来源
  345.             'amount' => [
  346.                     'refund' => $order['refund_amount'],
  347.                     'currency' => 'CNY',
  348.                 ]
  349.         );
  350.  
  351.         if (!empty($order['transaction_id'])) {
  352.             $params['transaction_id'] = $order['transaction_id'];
  353.             $orderDetail = self::query($order['transaction_id'], true);
  354.         } else {
  355.             $params['out_trade_no'] = $order['order_sn'];
  356.             $orderDetail = self::query($order['order_sn']);
  357.         }
  358.         $params['amount']['total'] = $orderDetail['amount']['total'];
  359.         !empty($order['reason']) && $params['reason'] = $order['reason'];
  360.  
  361.         $url = self::$refundUrl;
  362.         $header = self::createAuthorization($url, $params, 'POST');
  363.         $response = Http::post($url, json_encode($params, JSON_UNESCAPED_UNICODE), $header);
  364.         $result = json_decode($response, true);
  365.  
  366.         return $result;
  367.     }
  368.  
  369.     /**
  370.      * [queryRefund 查询退款]
  371.      * @param  [type] $refundSn [退款单号]
  372.      * @return [type]           [description]
  373.      */
  374.     public static function queryRefund($refundSn, $type = false)
  375.     {
  376.         $url = self::$refundUrl . '/' . $refundSn;
  377.         $params = '';
  378.  
  379.         $header = self::createAuthorization($url, $params, 'GET');
  380.         $response = Http::get($url, $params, $header);
  381.         $result = json_decode($response, true);
  382.  
  383.         return $result;
  384.     }
  385.  
  386.     /**
  387.      * [success 通知支付状态]
  388.      */
  389.     public static function success()
  390.     {
  391.         $str = ['code'=>'SUCCESS', 'message'=>'成功'];
  392.         die(json_encode($str, JSON_UNESCAPED_UNICODE));
  393.     }
  394.  
  395.     /**
  396.      * [createAuthorization 获取接口授权header头信息]
  397.      * @param  [type] $url    [请求地址]
  398.      * @param  array  $data   [请求参数]
  399.      * @param  string $method [请求方式]
  400.      * @return [type]         [description]
  401.      */
  402.     //生成v3 Authorization
  403.     protected static function createAuthorization($url, $data=[], $method='POST'){
  404.         $config = self::$config;
  405.         //商户号
  406.         $mchid = $config['mchid'];
  407.         // 证书序列号
  408.         if (empty($config['serial_no'])) {
  409.             $certFile = @file_get_contents($config['cert_client']);
  410.             $certArr = openssl_x509_parse($publicStr);
  411.             $serial_no = $certArr['serialNumberHex'];
  412.         } else {
  413.             $serial_no = $config['serial_no'];
  414.         }
  415.  
  416.         // 解析url地址
  417.         $url_parts = parse_url($url);
  418.         //生成签名
  419.         $body = [
  420.             'method' => $method,
  421.             'url'   => ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : "")),
  422.             'time'  => time(), // 当前时间戳
  423.             'nonce' => self::get_rand_str(32, 0, 1), // 随机32位字符串
  424.             'data'  => (strtolower($method) == 'post' ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data), // POST请求时 需要 转JSON字符串
  425.         ];
  426.         $sign = self::makeSign($body);
  427.         //Authorization 类型
  428.         $schema = 'WECHATPAY2-SHA256-RSA2048';
  429.         //生成token
  430.         $token = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"', $mchid, $body['nonce'], $body['time'], $serial_no, $sign);
  431.  
  432.         $header = [
  433.             'Content-Type:application/json',
  434.             'Accept:application/json',
  435.             'User-Agent:*/*',
  436.             'Authorization: '.  $schema . ' ' . $token
  437.         ];
  438.         return $header;
  439.     }
  440.  
  441.     /**
  442.      * [makeSign 生成签名]
  443.      * @param  [type] $data [加密数据]
  444.      * @return [type]       [description]
  445.      */
  446.     public static function makeSign($data)
  447.     {
  448.         $config = self::$config;
  449.         if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
  450.             throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
  451.         }
  452.         // 拼接生成签名所需的字符串
  453.         $message = '';
  454.         foreach ($data as $value) {
  455.             $message .= $value . "\n";
  456.         }
  457.         // 商户私钥
  458.         $private_key = self::getPrivateKey($config['cert_key']);
  459.         // 生成签名
  460.         openssl_sign($message, $sign, $private_key, 'sha256WithRSAEncryption');
  461.         $sign = base64_encode($sign);
  462.         return $sign;
  463.     }
  464.  
  465.     /**
  466.      * [verifySign 验证签名]
  467.      * @param  [type] $data   [description]
  468.      * @param  [type] $sign   [description]
  469.      * @param  [type] $serial [description]
  470.      * @return [type]         [description]
  471.      */
  472.     public static function verifySign($data, $sign, $serial)
  473.     {
  474.         $config = self::$config;
  475.         if (!in_array('sha256WithRSAEncryption', \openssl_get_md_methods(true))) {
  476.             throw new \RuntimeException("当前PHP环境不支持SHA256withRSA");
  477.         }
  478.         $sign = \base64_decode($sign);
  479.         // 拼接生成签名所需的字符串
  480.         $message = '';
  481.         foreach ($data as $value) {
  482.             $message .= $value . "\n";
  483.         }
  484.         // 获取证书相关信息
  485.         self::certificates($serial);
  486.         // 平台公钥
  487.         $public_key = self::getPublicKey($config['public_key']); //平台公钥
  488.         // 验证签名
  489.         $recode = \openssl_verify($message, $sign, $public_key, 'sha256WithRSAEncryption');
  490.         return $recode == 1 ? true : false;
  491.     }
  492.  
  493.     //获取私钥
  494.     public static function getPrivateKey($filepath)
  495.     {
  496.         return openssl_pkey_get_private(file_get_contents($filepath));
  497.     }
  498.  
  499.     //获取公钥
  500.     public static function getPublicKey($filepath)
  501.     {
  502.         return openssl_pkey_get_public(file_get_contents($filepath));
  503.     }
  504.  
  505.     /**
  506.      * [certificates 获取证书]
  507.      * @return [type] [description]
  508.      */
  509.     public static function certificates($serial)
  510.     {
  511.         $config = self::$config;
  512.  
  513.         $publicStr = @file_get_contents($config['public_key']);
  514.         if ($publicStr) { // 判断证书是否存在
  515.             $openssl = openssl_x509_parse($publicStr);
  516.             if ($openssl['serialNumberHex'] == $serial) { // 是否是所需证书
  517.                 // return self::getPublicKey($config['public_key']); //平台公钥
  518.                 return '';
  519.             }
  520.         }
  521.  
  522.         $url = self::$certificatesUrl;
  523.         $params = '';
  524.  
  525.         $header = self::createAuthorization($url, $params, 'GET');
  526.         $response = Http::get($url, $params, $header);
  527.         $result = json_decode($response, true);
  528.         if (empty($result['data'])) {
  529.             throw new RuntimeException("[" . $result['code'] . "] " . $result['message']);
  530.         }
  531.         foreach ($result['data'] as $key => $certificate) {
  532.             if ($certificate['serial_no'] == $serial) {
  533.                 $publicKey = self::decryptToString(
  534.                     $certificate['encrypt_certificate']['associated_data'],
  535.                     $certificate['encrypt_certificate']['nonce'],
  536.                     $certificate['encrypt_certificate']['ciphertext']
  537.                 );
  538.                 file_put_contents($config['public_key'], $publicKey);
  539.                 break; // 终止循环
  540.             }
  541.             // self::$publicKey[$certificate['serial_no']] = $publicKey;
  542.         }
  543.         // return self::getPublicKey($config['public_key']); //平台公钥
  544.     }
  545.  
  546.     /**
  547.      * [decryptToString 证书和回调报文解密]
  548.      * @param  [type] $associatedData [附加数据包(可能为空)]
  549.      * @param  [type] $nonceStr       [加密使用的随机串初始化向量]
  550.      * @param  [type] $ciphertext     [Base64编码后的密文]
  551.      * @return [type]                 [description]
  552.      */
  553.     public static function decryptToString($associatedData, $nonceStr, $ciphertext)
  554.     {
  555.         $config = self::$config;
  556.         $ciphertext = base64_decode($ciphertext);
  557.         if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
  558.             return false;
  559.         }
  560.  
  561.         // ext-sodium (default installed on >= PHP 7.2)
  562.         if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
  563.             \sodium_crypto_aead_aes256gcm_is_available()) {
  564.             return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $config['key']);
  565.         }
  566.  
  567.         // ext-libsodium (need install libsodium-php 1.x via pecl)
  568.         if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
  569.             \Sodium\crypto_aead_aes256gcm_is_available()) {
  570.             return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $config['key']);
  571.         }
  572.  
  573.         // openssl (PHP >= 7.1 support AEAD)
  574.         if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
  575.             $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
  576.             $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
  577.  
  578.             return \openssl_decrypt($ctext, 'aes-256-gcm', $config['key'], \OPENSSL_RAW_DATA, $nonceStr,
  579.                 $authTag, $associatedData);
  580.         }
  581.  
  582.         throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
  583.     }
  584.  
  585.     /** fengkui.net
  586.      * [get_rand_str 获取随机字符串]
  587.      * @param  integer $randLength    [长度]
  588.      * @param  integer $addtime       [是否加入当前时间戳]
  589.      * @param  integer $includenumber [是否包含数字]
  590.      * @return [type]                 [description]
  591.      */
  592.     public static function get_rand_str($randLength=6, $addtime=0, $includenumber=1)
  593.     {
  594.         if ($includenumber)
  595.             $chars='abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQEST123456789';
  596.         $chars='abcdefghijklmnopqrstuvwxyz';
  597.  
  598.         $len = strlen($chars);
  599.         $randStr = '';
  600.         for ($i=0; $i<$randLength; $i++){
  601.             $randStr .= $chars[rand(0, $len-1)];
  602.         }
  603.         $tokenvalue = $randStr;
  604.         $addtime && $tokenvalue = $randStr . time();
  605.         return $tokenvalue;
  606.     }
  607.  
  608.     /** fengkui.net
  609.      * [get_ip 定义一个函数get_ip() 客户端IP]
  610.      * @return [type] [description]
  611.      */
  612.     public static function get_ip()
  613.     {
  614.         if (getenv("HTTP_CLIENT_IP"))
  615.             $ip = getenv("HTTP_CLIENT_IP");
  616.         else if(getenv("HTTP_X_FORWARDED_FOR"))
  617.             $ip = getenv("HTTP_X_FORWARDED_FOR");
  618.         else if(getenv("REMOTE_ADDR"))
  619.             $ip = getenv("REMOTE_ADDR");
  620.         else $ip = "Unknow";
  621.  
  622.         if(preg_match('/^((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1 -9]?\d))))$/', $ip))
  623.             return $ip;
  624.         else
  625.             return '';
  626.     }
  627. }


本文网址:https://www.zztuku.com/detail-9012.html
站长图库 - 随着微信支付的升级,PHP微信支付类V3接口也来了
申明:如有侵犯,请 联系我们 删除。

评论(0)条

您还没有登录,请 登录 后发表评论!

提示:请勿发布广告垃圾评论,否则封号处理!!

    编辑推荐