您的当前位置:首页>全部文章>文章详情

【PHP】微信JSAPI支付V3版本

CrazyPanda发表于:2023-12-01 20:46:35浏览:675次TAG:

定义微信配置

protected $sp_appid = '';//服务商APPID(目前只有公众号)
protected $sp_mchid = '';//服务商商户号
protected $sub_appid = '';//小程序APPID
protected $sub_mchid = '';//商户号
protected $apiV3Key = '';//支付秘钥(服务商V3级别)
protected $privateKeyPath = '';//证书物理路径(私钥)
protected $serialNo = '';//证书序列号
protected $app_secret = '';//小程序通信秘钥
protected $notify_url = '';//支付回调地址

初始化加载配置

public function _initialize()
{
    parent::_initialize();
    $wechatconf = config('wechat');//可以写在配置文件,也可写在数据库
    $this->sp_appid = $wechatconf['sp_appid'];
    $this->sp_mchid = $wechatconf['sp_mchid'];
    $this->sub_appid = $wechatconf['sub_appid'];
    $this->sub_mchid = $wechatconf['sub_mchid'];
    $this->apiV3Key = $wechatconf['apiV3Key'];
    $this->privateKeyPath = $wechatconf['privateKeyPath'];
    $this->serialNo = $wechatconf['serialNo'];
    $this->app_secret = $wechatconf['app_secret'];
    $this->notify_url = $wechatconf['notify_url'];
}

统一下单(注意signType不参与签名,package必须是下面写的prepay_id=)

public function unifyPay()
{
    $param = $this->request->param();
    if(!$param['uid'] || !$param['orderno'])
    {
        return $this->json_result(400,'缺少参数');
    }
    $orderinfo = Db::name('service_order')->where(['orderno'=>$param['orderno'],'uid'=>$param['uid']])->find();
    if(empty($orderinfo))
    {
        return $this->json_result(400,'订单不存在');
    }
    $userinfo = Db::name('user')->where('id',$param['uid'])->find();
    if(empty($userinfo))
    {
        return $this->json_result(400,'用户不存在');
    };
    //这个数组里所有的数据都是必填的
    $unifydata = [
        'sp_appid' => $this->sp_appid,
        'sp_mchid' => $this->sp_mchid,
        'sub_appid' => $this->sub_appid,
        'sub_mchid' => $orderinfo['sub_mchid'],
        'description' => '服务',
        'out_trade_no' => $param['orderno'],//订单号
        'notify_url' => $this->notify_url,//回调地址
        'settle_info'=>['profit_sharing'=>true],
        'amount'=>['total'=>(int)bcmul(0.01, '100',0),
        'currency'=>'CNY'],
        'payer'=>['sub_openid'=>$userinfo['username']],
        'scene_info'=>[
            'payer_client_ip'=>$_SERVER["REMOTE_ADDR"]
        ],
    ];
    $orderResult = $this->create_pay_order($unifydata);
    $timeStamp = time();
    $paydata = [
        'appId' => $this->sub_appid,
        'timeStamp' => "$timeStamp",
        'nonceStr' => md5(rand(100000,999999)),
        'package' => 'prepay_id=' . $orderResult['prepay_id'],
    ];
    $sign = $this->getSign($paydata);//给小程序生成验证签名
    $paydata['signType'] = 'RSA';
    $paydata['paySign'] = $sign;
    return $this->json_result(200,'统一下单成功',$paydata);
}

调用微信下单接口

public function create_pay_order($inputData)
{
    $inputData = json_encode($inputData);//v3微信传递参数需要json格式
    $headers = $this->getV3Sign('https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi','POST',$inputData);//将签名放在报头里
    $result = $this -> post_data('https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi',$inputData,$headers);//下单
    return json_decode($result,true);
}

获取签名

public function getV3Sign($url,$http_method,$body) 
{
    //商户号
    $mchid = $this->sp_mchid;
    //随机字符串
    $nonce = strtoupper($this -> getNoncestr());
    //商户序列号
    $serialNo = $this->serialNo;
    //时间戳
    $timestamp = time();
    //url
    $url_parts = parse_url($url);
    //获取绝对路径
    $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));
    //密钥
    key$private_key = $this->getPrivateKey($this->privateKeyPath);
    //拼接参数$message = $http_method."\n".
                            $canonical_url."\n".
                            $timestamp."\n".
                            $nonce."\n".
                            $body."\n";
    //计算签名值
    openssl_sign($message, $raw_sign, $private_key, 'sha256WithRSAEncryption');
    $sign   = base64_encode($raw_sign);
    //        $token  = sprintf('mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',$mchid, $nonce, $timestamp, $serial_no, $sign);
    $token = sprintf('WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',$mchid, $nonce, $timestamp, $serialNo, $sign);
    $headers = [
        'Accept: application/json',
        'User-Agent: */*',
        'Content-Type: application/json; charset=utf-8',
        'Authorization: '.$token,
    ];
    return $headers;
}

生成32位随机字符串

public function getNoncestr($length = 32) 
{
    $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    $str ="";
    for ( $i = 0; $i < $length; $i++ )  
    {    
        $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
    }
    return $str;
}

获取私钥

public function getPrivateKey($filepath) 
{
    return openssl_get_privatekey(file_get_contents($filepath));
}

POST调用API

public function post_data($url,$data=[],$headers=[])
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
    //设置header头
    curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
    // POST数据
    curl_setopt($ch, CURLOPT_POST, 1);
    // 把post的变量加上
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    $output = curl_exec($ch);
    curl_close($ch);
    return $output;
}
给小程序生成验证签名(V3不再用MD5用SHA256)
protected function getSign($data) 
{
    $tmpstr = $data['appId'] . "\n" . $data['timeStamp'] . "\n" . $data['nonceStr'] . "\n" . $data['package'] . "\n";
    $privateKey = file_get_contents($this->privateKeyPath);
    $binary_signature = "";
    $algo = "SHA256";
    openssl_sign($tmpstr, $binary_signature, $privateKey, $algo);
    $sign = base64_encode($binary_signature);
    return $sign;
}

支付回调 v3必须大于php7.2

public function notify()
{
    $result = $this->request->param();
    if($result)
    {
        $text = base64_decode($result['resource']['ciphertext']); 
        //解密/* =========== 使用V3支付需要PHP7.2.6安装sodium扩展才能进行解密参数  ================ */
        $str = sodium_crypto_aead_aes256gcm_decrypt($text, $result['resource']['associated_data'], $result['resource']['nonce'], $this->apiV3Key);
        $res = json_decode($str, true);
        //如果成功返回了
        if($res['trade_state'] == 'SUCCESS')
        {
            Db::startTrans();
            try {
                Db::name('service_order')->where('orderno',$res['out_trade_no'])->update(['pay_state'=>1,'transaction_id'=>$res['transaction_id'],'pay_time'=>time(),'status'=>1]);
                $lid = Db::name('service_order')->where('orderno',$res['out_trade_no'])->value('lid');
                if($lid)
                {
                    Db::name('service_list')->where('id',$lid)->setInc('sales',1);
                }
                Db::commit();
                return $this->json_result(200,'支付成功');
            }
            catch (DbException $e)
            {
                return $this->json_result(400,$e->getMessage());
            }
        }
    }
    return $this->json_result(400,'支付失败');
}


猜你喜欢

【PHP】php二进制转换函数
在进行某些数据处理时,我们可能需要将十进制数字转换成二进制或将二进制数字转换为十进制。在PHP中,我们可以通过一些内置函数来完成这些转换操作。一、十进制转二进制在PHP中,我们可以使用decbin()函数将十进制数字转换成二进制。例如,在下面的示例中,我们将十进制数字29转换成二进制数字:$decimal_number&nbsp;=&nbsp;29; $binary_number&nbsp;=&nbsp;decbin($decimal_number); echo&nbsp;$binar
发表于:2023-12-29 浏览:278 TAG:
【PHP】php删除数组中的重复值
随着互联网技术的快速发展,各种编程语言也在不断更新和发展。其中,PHP作为一门开发Web应用程序的强大语言,受到了广泛的关注和使用。在PHP编程中,数组是非常常用的数据类型之一,而处理数组中重复值的问题也是PHP开发人员经常遇到的问题之一。本文将介绍PHP中删除数组中重复值的方法。方法一:array_uniquePHP提供了一个内置函数array_unique(),可以用来删除数组中的重复值。array_unique()函数将返回一个新数组,该数组包含输入数组中所有的唯一值。使用arr
发表于:2023-12-19 浏览:322 TAG:
【PHP】php开发的办公软件都有哪些
php开发的办公软件有WordPress、Drupal、Joomla、ownCloud、SuiteCRM、EspoCRM、Feng Office、LimeSurvey、phpMyAdmin、InvoicePlane等等常用办公软件。详细介绍:1、WordPress,一款开源的内容管理系统,用于创建和管理博客、网站和在线商店;2、Drupal,适用于构建复杂的网站和应用程序等等。本教程操作系统:windows10系统、PHP8.1.3版本、Dell G3电脑。PHP作为一种流行的服务器端
发表于:2023-12-28 浏览:331 TAG:
【PHP】PHP函数array_map()
在PHP的函数库中,有一款非常实用的函数,那就是array_map()函数。它可以将一个数组中的数据传递给某个函数进行处理,最终返回一个新的数组。array_map()函数可以极大地方便我们数据的处理,下面我们来详细介绍一下它的使用。一、array_map()函数的基本用法array_map()的基本语法格式为:array_map(callable $callback, array ...$arr)其中,$callback参数表示将要被调用的函数或方法,它和数组中的每一个元素一一对应。而$arr
发表于:2024-07-31 浏览:260 TAG:
【PHP】制作自己的Composer插件并与其他开发者共享
如何编写自己的Composer插件并分享给其他开发者在现代的PHP开发领域,Composer已经成为了一个不可或缺的工具。它可以帮助开发者管理项目依赖和自动加载类,大大简化了项目的构建过程。除了使用Composer来安装第三方的扩展包之外,我们也可以使用Composer来编写自己的插件,并将其分享给其他开发者。本文将逐步介绍如何编写自己的Composer插件,并提供具体的代码示例。首先,我们需要创建一个空的Composer插件项目。在命令行中进入项目根目录,然后执行以下命令:compo
发表于:2023-12-27 浏览:354 TAG:
【PHP】Thinkphp8 配置异常全局捕捉处理
封装异常处理配置先创建自己的 BaseException 类&lt;?php namespace&nbsp;app\exception; use&nbsp;app\enums\StatusCodeEnum; class&nbsp;BaseException&nbsp;extends&nbsp;\Exception { &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;$success&nbsp;=&nbsp;false; &nbsp;&nbsp;&nbsp;&amp;nbs
发表于:2024-01-04 浏览:315 TAG:
【PHP】Your requirements could not be resolved to an installable set of packages.
在执行composer install或composer update的时候,出现Your requirements could not be resolved to an installable set of packages.这个错误,提示的是版本不兼容的问题执行一下命令即可composer install --ignore-platform-reqs 或&nbsp;composer update --ignore-platform-reqs
发表于:2024-08-09 浏览:319 TAG:
【PHP】使用ThinkPHP6实现分布式系统
随着互联网的发展,越来越多的企业和组织开始使用分布式系统来支持其业务需求。分布式系统是指由多个互相独立的计算机系统集成在一起,共同完成一些任务或处理一些数据,整个系统看起来就像是一个单一的计算机系统。在Web应用程序中,ThinkPHP是一个非常流行的PHP框架。ThinkPHP6是其最新的版本,提供了更多的功能和性能优化。如果你想要使用ThinkPHP6来构建分布式系统,下面是一些实现的步骤:第一步:搭建应用程序框架首先,你需要在你的服务器上安装PHP。然后,你需要安装Composer,这是一
发表于:2024-05-29 浏览:336 TAG:
【PHP】php7弃用的函数有哪些
本教程操作系统:windows10系统、PHP 8.1.3版本、DELL G3电脑。PHP 7是PHP编程语言的一个重要版本,引入了许多新特性和改进。同时,为了提高代码的质量和安全性,PHP 7还废弃了一些旧的函数。下面是一些在PHP 7中被弃用的函数的例子:1. mysql_ 系列函数:在PHP 7中,mysql_ 系列函数(如mysql_connect、mysql_query等)被弃用。这些函数是用于与MySQL数据库进行交互的旧API,而在PHP 5.5版本中已经引入了更现代化和安全的my
发表于:2024-08-02 浏览:283 TAG: #php
【PHP】PHP依赖注入
PHP依赖注入(Dependency Injection,DI)是一种软件设计模式,可以减少代码耦合,使得类鱼类之间的依赖关系更加清晰。以下是一个简单的PHP依赖注入的例子:&lt;?php &nbsp; interface&nbsp;StorageInterface&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;function&nbsp;store($data); } &nbsp; class&nbsp;DatabaseStorage&nbsp;im
发表于:2024-06-17 浏览:232 TAG: