Thinkphp5.1微信小程序支付

Thinkphp5.1微信小程序支付,第1张

研究了好几天,坑也遇到了,也百度了很久现在终于做完了,给大家分享出来,

我这个也是参考别人写的。有不明白的朋友可以问我

public function unifiedorder($order_no, $openid, $total_fee, $attach, $order_id, $user_id){

    // 当前时间

    $time = time()

    // 生成随机字符串

    $nonceStr = md5($time . $openid)

    // API参数

    $params = [

        'appid' =>$this->appid,                                  //微信分配的小程序id

        'attach' =>$attach,                                      //附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。

        'body' =>'会员卡',                                      //募捐描述

        'mch_id' =>$this->mchid,                        //微信支付分配的商户号

        'nonce_str' =>$nonceStr,                                  //随机字符串,32位以内

        'notify_url' =>$this->notify_url,                    //            base_url() . 'notice.php?s=/task/notify/order/wxapp_id/'.$wxapp_id, // 异步通知地址

        'openid' =>$openid,                                      //用户标识;trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。

        'out_trade_no' =>$order_no,                              //商户账单号

        'spbill_create_ip' =>\request()->ip(),                    //终端IP;支持IPV4和IPV6两种格式的IP地址。调用微信支付API的机器IP

        'total_fee' =>(int)$total_fee * 100, // 价格:单位分              // 价格:单位分

        'trade_type' =>'JSAPI',                                  //交易类型

    ]

    // 生成签名

    $params['sign'] = $this->makeSign($params)  //这个地方最坑,需要的是配置 1、appid和商户号必须是绑定的状态

    // 请求API

    $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder'

    $result = $this->post($url, $this->toXml($params))

    $prepay = $this->fromXml($result)

    //添加preapay_id

    $data = [

        'user_id' =>$user_id,

        'order_id' =>$order_id,

        'attach' =>json_encode($attach),

        'prepay_id' =>$prepay['prepay_id'],

    ]

    (new AppleWxPrepay())->addInfo($data)

    // 请求失败

    if ($prepay['return_code'] === 'FAIL') {

        return [API_CODE_NAME =>2000004, API_MSG_NAME =>$prepay['return_msg']]

    }

    if ($prepay['result_code'] === 'FAIL') {

        return [API_CODE_NAME =>2000004, API_MSG_NAME =>$prepay['err_code_des']]

    }

    // 生成 nonce_str 供前端使用

    $paySign = $this->makePaySign($params['nonce_str'], $prepay['prepay_id'], $time)

    return [

        'prepay_id' =>$prepay['prepay_id'],

        'nonceStr' =>$nonceStr,

        'timeStamp' =>(string)$time,

        'paySign' =>$paySign

    ]

}

/**

* 生成签名

* @param $values

* @return string 本函数不覆盖sign成员变量,如要设置签名需要调用SetSign方法赋值

*/

private function makeSign($values)

{

    //签名步骤一:按字典序排序参数

    ksort($values)

    $string = $this->toUrlParams($values)

    //签名步骤二:在string后加入KEY

    $string = $string . '&key=' . $this->apikey

    //签名步骤三:MD5加密

    $string = md5($string)

    //签名步骤四:所有字符转为大写

    $result = strtoupper($string)

    return $result

}

/**

* 格式化参数格式化成url参数

* @param $values

* @return string

*/

private function toUrlParams($values)

{

    $buff = ''

    foreach ($values as $k =>$v) {

        if ($k != 'sign' &&$v != '' &&!is_array($v)) {

            $buff .= $k . '=' . $v . '&'

        }

}

    return trim($buff, '&')

}

/**

* 模拟POST请求

* @param $url

* @param array $data

* @param bool $useCert

* @param array $sslCert

* @return mixed

*/

public function post($url, $data = [], $useCert = false, $sslCert = [])

{

    $header = [

        'Content-type: application/jsoncharset=UTF8'

    ]

    $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_SSL_VERIFYPEER, false)

    curl_setopt($curl, CURLOPT_POST, TRUE)

    curl_setopt($curl, CURLOPT_POSTFIELDS, $data)

    if ($useCert == true) {

        // 设置证书:cert 与 key 分别属于两个.pem文件

        curl_setopt($curl, CURLOPT_SSLCERTTYPE, 'PEM')

        curl_setopt($curl, CURLOPT_SSLCERT, $sslCert['certPem'])

        curl_setopt($curl, CURLOPT_SSLKEYTYPE, 'PEM')

        curl_setopt($curl, CURLOPT_SSLKEY, $sslCert['keyPem'])

    }

    $result = curl_exec($curl)

    curl_close($curl)

    return $result

}

/**

* 输出xml字符

* @param $values

* @return bool|string

*/

private function toXml($values)

{

    if (!is_array($values) || count($values) <= 0) {

        return false

    }

    $xml = "<xml>"

    foreach ($values as $key =>$val) {

        if (is_numeric($val)) {

            $xml .= "<" . $key . ">" . $val . "</" . $key . ">"

        } else {

            $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">"

        }

}

    $xml .= "</xml>"

    return $xml

}

/**

* 将xml转为array

* @param $xml

* @return mixed

*/

private function fromXml($xml)

{

    // 禁止引用外部xml实体

    libxml_disable_entity_loader(true)

    return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true)

}

/**

* 生成paySign

* @param $nonceStr

* @param $prepay_id

* @param $timeStamp

* @return string

*/

private function makePaySign($nonceStr, $prepay_id, $timeStamp)

{

    $data = [

        'appId' =>$this->appid,

        'nonceStr' =>$nonceStr,

        'package' =>'prepay_id=' . $prepay_id,

        'signType' =>'MD5',

        'timeStamp' =>$timeStamp,

    ]

    // 签名步骤一:按字典序排序参数

    ksort($data)

    $string = $this->toUrlParams($data)

    // 签名步骤二:在string后加入KEY

    $string = $string . '&key=' . $this->apikey

    // 签名步骤三:MD5加密

    $string = md5($string)

    // 签名步骤四:所有字符转为大写

    $result = strtoupper($string)

    return $result

}

/*********************************微信回调**********************/

public function getNotify()

{

    if (!$xml = file_get_contents('php://input')) {

        $this->returnCode(50000001, 'Not found DATA')

    }

    // 将服务器返回的XML数据转化为数组

    $data = $this->fromXml($xml)

    $payLog = new ApplePayLog()

    // 记录日志

    $payLog->addInfo(['content'=>json_encode($xml)])

    $payLog->addInfo(['content'=>json_encode($data)])

    // 实例化账单模型

    $OrderModel = new AppleOrder()

    // 账单信息

    $orderInfo = $OrderModel->getInfo(['id'=>$data['attach']],'*')

    if (empty($orderInfo)) {

        $this->returnCode(50000001, '账单不存在')

    }

    if($orderInfo['pay_status'] != 1 || !empty($orderInfo['pay_time'])){

        $this->returnCode(50000001,'订单已支付,请勿再次支付')

    }

    // 保存微信服务器返回的签名sign

    $dataSign = $data['sign']

    $return_code = $data['return_code']

    $result_code = $data['result_code']

    $data['body'] = '会员卡'

    $data['spbill_create_ip'] = \request()->ip()

    $data['notify_url'] = $this->notify_url

    // sign 与 s 参数 不参与签名算法

    unset($data['sign'])

    unset($data['transaction_id'])

    unset($data['coupon_id'])

    unset($data['coupon_type'])

    unset($data['coupon_count'])

    unset($data['coupon_fee'])

    unset($data['time_end'])

    unset($data['return_code'])

    unset($data['result_code'])

    unset($data['is_subscribe'])

    unset($data['fee_type'])

    unset($data['bank_type'])

    unset($data['bank_type'])

    // 生成签名

    $sign = $this->makeSign($data)

    // 判断签名是否正确  判断支付状态

    if (($sign === $dataSign) &&($return_code == 'SUCCESS') &&($result_code == 'SUCCESS')) {

        $OrderModel->startTrans()

        try {

            // 账单支付成功业务处理

            $appleOrderInfo = $OrderModel->where(['id'=>$orderInfo['id']])->lock(true)->find()

            $result = $appleOrderInfo->addInfo(['pay_status'=>2,'pay_time'=>time()],['id'=>$orderInfo['id']])

            if(!$result){

                $OrderModel->rollback()

                $this->returnCode(5000003, '修改订单失败,失败原因:'.$OrderModel->getError())

            }

            $appleUserModel = new AppleUser()

            $appleUserInfo =  $appleUserModel->where(['openid'=>$orderInfo['openid']])->lock(true)->find()

            $appleUser = $appleUserInfo->where(['openid'=>$orderInfo['openid']])->setInc('moxibustion',$orderInfo['moxibustion'])

            if(!$appleUser){

                $OrderModel->rollback()

                $this->returnCode(5000003, '添加会员针灸次数失败,失败原因:'.$appleUserModel->getError())

            }

        }catch (\Exception $exception){

            $OrderModel->rollback()

            $this->returnCode(5000003, ' *** 作失败,失败原因:'.$exception->getMessage())

        }

            $OrderModel->commit()

        // 返回状态

        die(json(['code'=>0,'支付成功']))

    }

    // 返回状态

    $this->returnCode(2000003, '签名失败')

}

小程序

第一个web项目-微信小程序后端开发

第一个web项目-微信小程序后端开发

前言

需求分析

团队分工

总体设计

开发工具及编码实现

小程序前端

后端

数据库

接口代码

管理系统前端1.0

管理系统前端2.0

测试

后端本地测试

前后端联合测试

部署

总结

第一个web项目-微信小程序后端开发

前言

去年暑假一个偶然的机会我和几位同学加入了学院一位老师主持的教改项目,需求是开发一个基于SPOC与翻转课堂的计算机组成原理课程的学习app(类似慕课、知到),后来经过讨论决定降低难度,先做一个微信小程序,附带一个后台管理系统,于是我的第一个web项目就开始了~

需求分析

这里简单介绍下SPOC和翻转课堂的意思

翻转课堂

“翻转课堂”(Flipping Classroom)是一种颠覆传统教学由“课堂授课听讲 + 课后作业练习”转变为“课前自主学习 + 课堂协作探究”的新型教学模式。

SPOC

SPOC(Small Private Online Course)一般被译为小规模限制性在线课程或者小规模私有型网络课程,音译为“私播课”。

这次项目的需求是开发一个学习类型的小程序,用户分为学生和教师,其中学生可以观看视频、课件、动画,完成作业、考试以及发布评论、点赞、回复,而教师可以上传教学视频、课件、动画和发布作业、考试、通知,以及查看学生的学习情况,也可以查看评论回复,及时解答学生的疑惑。

团队分工

团队一共有四个人,总体工作分为产品设计、前端开发、后端开发三部分,然后每部分由两人负责。其中我是负责后端开发的,同时兼任项目负责人(其实也没有听上去那么高大上,只是需要承担更多决策、协调、沟通的角色)。

总体设计

这里分为小程序和管理系统

首先是小程序,放几张使用墨刀制作的原型图,这里多说两句,市面上的小程序基本都是微信授权直接登录,最多绑定手机号,我们这个由于要统计学生的学习情况才设置了注册和登录功能

至于管理系统,由于是10月份才开始做的,而且是我和另一位做后端的同学负责的,时间比较紧,我们作为前端小白没有十分系统的方法去做开发,只是大概确定了需要做哪些模块,每个模块对哪些表的增删改查,这里原型图就不放了(较简陋)

开发工具及编码实现

小程序前端

据我了解,做前端的同学先去微信公众平台注册账号,然后做一些开发设置,具体步骤自行百度。前端用的是微信开发者工具,有不会的基本上在微信开放文档都可以找到,包括许多实用的API。

后端

这里分为数据库、接口代码两部分

数据库

用的是mysql数据库,之前是跟着学堂在线的一个小程序入门教程做的,它推荐的本地开发环境是phpstudy,里面集成了php、mysql、apache、FTP、Nginx以及数据库管理工具phpMyAdmin,关于phpMyAdmin使用请看https://blog.csdn.net/u012767761/article/details/78238487

原本的数据库设计得不好,存在较多冗余数据,后来学习了数据库系统这门课,我进行了大改,先确定有哪些实体以及实体之间的联系,然后画er图,最后再建模,通过外码约束大量减少了冗余,也减少了表的数量。

接口代码

教程使用的是php语言,框架是thinkphp5,开发手册看https://www.kancloud.cn/manual/thinkphp5/118003,我当时是去b站找视频学了下php基础语法,然后就去学原生php以及框架如何 *** 作数据库。然后根据业务逻辑开始编码,其实每个接口(或者叫类里面的一个函数)结构都差不多,主要是三部分:接收前端传来的数据、增/删/改/查、返回结果给前端。

顺便说下代码编辑用的是sublime text3,教程看https://blog.csdn.net/sam976/article/details/75333079/,这个不是ide,没有那么多的功能比如调试、运行,单纯是只有编辑、加注释、格式化等等,这里吐槽下自带的格式化代码功能(先选择代码,再Edit ->Line ->Reindent),有点辣鸡。而且如果有语法错误不会像eclipse那样自动检测出来,之前被坑了几次,肉眼找不到的话只能用postman去测试了。

管理系统前端1.0

一开始我们是不知道还要做个管理系统的,以为所有功能都放在小程序,后来老师跟我们讨论聊到这个问题,我们才知道原来还有这回事,其实就是管理系统应该具有一切功能,即对数据库所有表的增删改查,而小程序只需要有些轻量的功能即可,至于上传大容量文件、查看学习情况这些不够轻量的功能全部放在管理系统。好吧,凡事总有第一次,我们就开始学习基本的前端三件套html,css,javascript。

开始做的时候我们希望先实现功能,界面难看点没有太多关系,于是学了部分三件套的基础后又学了ajax技术(因为要与后端通信),这里最开始用的是创建XMLHttpRequest 对象,用open()方法设置请求类型和url,用send()方法发送数据到后端,直到遇到了jquery,后面的请求统一都用$.ajax()了。

接下来又遇到了一个难点,因为基本都用表格来展示数据,那获取数据后如何动态地加入表格呢?查找资料后用每一条数据拼接成由tr标签包含的字符串,然后用jquery获取表格标签后调用append()方法加入表格中。

除此之外,我们想在每行末尾设置按钮进行事件处理,于是我们append数据的同时也把button标签放入刚才的字符串中,然后给每个button设置id属性,比如用于修改数据的就叫fixi,最后这个i是代表表格第几行,然后添加事件监听,点击button时获取id,然后查看最后一位是多少从而确定是第几行。

这些做法实现起来是挺繁琐的,而且感觉在重复造轮子,我们也做得有点郁闷,因为每个页面基本都要这样做,但是当时没有那么多的时间精力去学习框架,只是想先实现功能(u1s1,上学期的课多到我快吐了)。

放两张界面图

管理系统前端2.0

之前放假,总算有较多空余时间了,我们决定要改下界面,但毕竟自身水平不高,因此需要用一点第三方的东西了。

在跟小程序前端测试了部分功能后,有一天后端同学找到了一个开源的框架然后我们一起看了下说明文档,最后决定:就用它了。

有请layui登场,经典模块化前端框架、低门槛开箱即用。

真正使用之前可以先看看文档https://www.layui.com/doc/,个人感觉上手还是挺快的。layui提供了许多实用的组件包括d出层、表格、表单、文件上传、流加载等等。

就拿表格来说,之前我们用append动态添加数据,现在直接table.render(),设置好参数就行了;之前我们给button设置id进行事件处理,现在绑定工具条,直接table.on()就行了;而且之前我们没实现的分页,现在设置分页参数就行了,然后查询数据库时分页读取。

另外,layui提供了一个页面布局的模板,包括logo、用户名、退出按钮、导航栏以及一些css动画。我们要做的就是按照它的模板来,页面元素的样式也参考它提供的。

有了layui的助攻,我们可以将更多注意力放在业务逻辑上,更多关注用户体验。

测试

后端本地测试

工具:postman

使用:打开一个新窗口,选择请求类型,输入url,设置参数,点击send

这种测试我认为是模拟前端发送数据然后运行后端代码,看结果是否正确,属于白盒测试,但是我们不是专业测试人员,目前这样测试不是做得很规范,只能尽可能想到不同的测试用例。

前后端联合测试

由于放假回家了没办法面对面,只能借助腾讯会议线上测了。

在部署工作完成之后,一般是我们写好接口代码,然后把url和需要的参数告诉前端同学(这里注意下,微信小程序的请求api只允许https开头的url,而且前端必须在微信公众平台配置好合法域名,不然会报错),前端把这些东西填入那个wx.request的api然后运行,他们会查看返回的数据是否正确,我们会查看数据库的情况,如果没问题会测试多几个数据,都可以的话就到下一个功能,这种方式应该是属于软工讲到的V模型的单元测试。

部署

用的是新浪云,实名认证、学生认证后会送一些云豆(新浪云的计费单位,1RMB=100云豆)

跟着之前说的教程把整个thinkphp项目部署到新浪云,具体步骤看https://www.kancloud.cn/cnzxo/sae_thinkphp/1423806

代码

在代码管理那里可上传压缩包,或者在线编辑(跟记事本差不多),改动大的最好在本地写好再贴上去

数据库

开启共享型mysql服务,目前用了phpmyadmin4.9版本,然后建表或导入sql文件

缓存

开启memcached服务,设置容量16MB(省点钱),其实这个服务我不是很清楚干什么的,但如果不打开访问接口时会报致命错误?

文件存储

我们需要保存许多类型的文件包括视频、课件、动画、作业、考试、头像,因此需要存放在服务端。这里开启storage服务,使用方法看https://www.sinacloud.com/doc/sae/php/storage.html#cyberduck,普通用户配额5个bucket,每个容量10G,然后直接当作本地磁盘那样用就行了,控制台或写代码都可上传文件,上传后获得url,然后就可以通过网络访问,关于新浪云环境下php如何 *** 作看官方文档http://apidoc.sinaapp.com/source-class-sinacloud.sae.Storage.html#。

域名

应用信息可查看二级域名,独立域名需要购买且备案

日志

日志中心可查看每次请求的接口、时间、请求方设备等信息

其它

控制台还可以实时查看流量统计、资源使用情况,以及消费情况

总结

这个项目我也算前后端都做了一遍,感觉前端不太适合自己,可能是对页面元素样式、用户体验不够敏感,不过必须承认前端是挺有意思的。至于后端是更加注重逻辑,目前我对后端的了解只停留在数据库、网络、部署层面,其实如果用户数量非常多还要考虑高并发的问题,也就要使用多线程、负载均衡、消息队列等技术了,所以还有很多技术需要学习

converter。sh技术。

简而言之就是个脚本,目前采用thinkPHP 5作为服务端系统,在小程序端把音频文件上传到后台,TP5已有文件上传功能的封装,具体上传代码不细说;我们只需要读取上传的文件,通过shell命令进行转码,然后把转码后的文件发送给语音接口即可得到语音识别结果。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/11338644.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-15
下一篇2023-05-15

发表评论

登录后才能评论

评论列表(0条)

    保存