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

【PHP】微信JSAPI支付V3版本

CrazyPanda发表于:2023-12-01 20:46:35浏览:666次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】REST API是什么
API的分类REST API(restful)REST是Representational State Transfer(表现层状态转移)的缩写,它是由罗伊·菲尔丁(Roy Fielding)提出的非 REST API(restless)REST API发送请求进行 CRUD 哪个操作由请求方式来决定同一个请求路径可以进行多个操作请求方式会用到 GET/POST/PUT/DELETE非 REST API请求方式不决定请求的 CRUD 操作一个请求路径只对应一个操作一般只有 GET/POST使用js
发表于:2024-05-23 浏览:298 TAG:
【PHP】php如何创建关联数组表格
随着互联网技术的不断发展,Web 应用程序的开发变得越来越重要。其中,关联数组表格是 Web 应用程序中常用的一种数据结构,它可以将数据按照列与行的方式分组存储,并且可以方便地在前端界面中进行展示。那么,如何使用 PHP 创建关联数组表格呢?本文将为您一一介绍。一、创建关联数组在 PHP 中,我们可以使用关联数组来存储数据,关联数组是以字符串为索引的数组。相较于索引数组(使用数字作为索引的数组),关联数组更加灵活,可以通过索引来直接访问每个元素。下面的示例代码创建了一个关联数组,包含了
发表于:2023-12-18 浏览:324 TAG:
【PHP】ThinkPHP 集成 jwt 技术 token 验证
ThinkPHP 集成 jwt 技术 token 验证一、思路流程二、安装 firebase/php-jwt三、封装token类四、创建中间件,检验Token校验时效性五、配置路由中间件六、写几个测试方法,通过postman去验证一、思路流程客户端使用用户名和密码请求登录服务端收到请求,验证用户名和密码验证成功后,服务端会签发一个token,再把这个token返回给客户端客户端收到token后可以把它存储起来,比如放到cookie中客户端每次向服务端请求资源时需要携带服务端签发的token,可以
发表于:2024-08-08 浏览:275 TAG: #php #jwt #token
【PHP】nginx负载均衡器的部署(5层\7层)
目录什么是负载均衡?实验:实现Web业务的负载均衡(http协议负载均衡)一、实验规划图:如何实现克隆虚拟机呢?二、实验步骤:1、准备4台服务器,一台做负载均衡器,另外的3台做web服务器,每台都需要安装nginx,建议都编译安装nginx,统一安装配置2、安装nginx成功后配置nginx里的网页,使之呈现对应的效果3、修改四台linux机器的名字:4、检查配置是否成功5、对n
发表于:2024-05-29 浏览:359 TAG:
【PHP】TP6 Think-Swoole构建的RPC服务与微服务架构
引言:随着互联网的快速发展以及业务规模的扩大,传统的单体架构已经无法满足大规模业务场景的需求。因此,微服务架构应运而生。在微服务架构中,RPC(Remote Procedure Call)服务是实现服务间通信的一种重要方式。通过RPC服务,各个微服务之间可以方便、高效地互相调用。在本篇文章中,我们将介绍如何使用Think-Swoole框架构建RPC服务,实现微服务架构中的服务间通信,并提供具体的代码示例。一、TP6 Think-Swoole简介TP6 Think-Swoole是一个基于Think
发表于:2024-05-27 浏览:253 TAG:
【PHP】关于bug率计算和它的实际意义
问题1:bug率有什么作用?my opion:用处有很多,需要具体情况具体分析,不过主要作用一般是来评价工作产品的质量。如果bug率较高,说明系统质量较差,需要大量的返工。项目经理就需要做好缺陷分析(缺陷的类型、分布、严重程度等),找出原因,以便做好下一阶段的缺陷预防工作。除此之外,还可以结合其它方面的信息,判断是否一些工作不充分。譬如,如果缺陷密度过低,有两个原因:可能工作产品质量确实高;也可能评审或测试不充分,更多的缺陷没有发现。在某些公司,bug率也作为项目度量考核的一项指标。问题2:bu
发表于:2024-06-22 浏览:254 TAG:
【PHP】进程、线程、协程的关系
进程: 是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态的概念,竞争计算机系统资源的基本单位; 线程: 是进程的一个执行单元,是进程内的调度实体,比进程更小的独立运行的基本单位,线程也被称为轻量级进程。 协程: 是一种轻量级的线程,因此又称微线程。它不是由操作系统内核调度,而是由程序员自己控制调度的执行流程。
发表于:2025-03-21 浏览:40 TAG: #php
【PHP】PHP 8.3 重磅发布:创新与增强,开创未来
PHP 8.3 于 2023 年 11 月 23 日发布,标志着该语言的发展又迈出了重要的一步。该版本引入了许多新功能、性能改进和弃用,旨在增强 PHP 开发体验。在这份综合指南中,我们将深入探讨这些更新,提供见解、技巧和创造性的代码示例,以帮助您适应并充分利用 PHP 8.3。只读类的增强PHP 8.3 对只读类的克隆行为进行了修改,允许在克隆期间重新初始化只读属性。这一更改解决了深度克隆中的特定边缘情况。参考以下示例:12345678910class Article {&nbsp;
发表于:2023-12-04 浏览:640 TAG:
【PHP】TP上传图片到OSS
准备:php版本:7.4tp版本:6.0阿里云accessKeyId;阿里云accessKeySecret;阿里云存储bucket名称,如&nbsp;examplebucket;地域节点endpoint,如:oss-cn-hangzhou.aliyuncs.com;1. 安装SDK参考官网https://help.aliyun.com/zh/oss/developer-reference/installation-13?spm=a2c4g.11186623.0.0.568f6583XllxTu
发表于:2024-03-23 浏览:392 TAG:
【PHP】PHP8.1 Fiber交叉执行多任务
span style="text-wrap: wrap;">大家的电脑应该都是大等于2核的了,但是大家电脑上同时运行的程序大多远远多于cpu的核心数量。这是因为操作系统在任务处理上采取了宏观上并行,微观上串行的做法。也就是cpu每个程序都执行了一点点时间然后就切换去执行别的程序。使得大家看上去都执行了很多。现在 php8.1 。推出了 fiber 。把调度权利赋予给了各位 php 开发。那么我们有 fiber 我们可以实现什么样的新操作呢。(本文给大家抛个砖,欢迎大家补充更有意思的使用)</span
发表于:2024-01-06 浏览:313 TAG: