微信小程序支付(完整版)-ThinkPHP/Uniapp

技术说明

1.前端:uniapp、vue3

2.接口:PHP8、ThinkPHP8、MySQL8.0

3.微信支付- PHP,官方示例文档

4.示例代码的模型及业务自己进行调整,不要一味的复制粘贴!!!

流程说明

1.小程序调用接口--获取拉起支付所用参数,生成订单

2.拉起微信支付

3.支付完成-更改订单状态

参数说明

1.appid - 小程序id

2.mchid -- 商户号ID

3.certificate_serial -- 证书序列号

4.api_v3_key -- 支付密钥(v3)

5.apiclient_key.pem -- 商户API私钥文件,根据微信支付下载器下载即可

6.cert.pem -- 微信支付平台证书文件(注意:此文件必须是手动下载的,具体下载方式下方有说明!!!

其他说明

1.本示例采用微信支付sdk

2.实际情况根据业务进行调整;

3.通知回调(未能正确返回)

4.其他没毛病。

项目示例

1.安装微信支付 wechatpay -- sdk

composer require wechatpay/wechatpay

2.下载微信支付平台证书文件

(1)下载微信支付平台证书下载器

(2)进行详情页(微信支付平台证书下载器)

(3)下载CertificateDownloader.php,点击下方红框,直接下载文件就行,文件位置随便放,只要能用php命令运行就行

(4)下载证书,直接复制下面命令,改参数即可。

        -k 支付密钥(上方参数4)

        -m 商户号(上方参数2)

        -f 商户密钥(上方参数5,需要完整路径)

        -s 证书序列号(上方参数3)

        -o 生成证书地址(需要本地完整路径)

php -f ./CertificateDownloader.php --  -k 4202c8***** -m 16***** -f /****/apiclient_key.pem -s 25***** -o /*****/cert/

3.封装支付类(完整示例如下)

<?phpnamespace app\common\controller;use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;/*** @note 微信支付操作*/
class WechatPay
{protected string $spAppid;  //  小程序appidprotected string $spAppSecret;  //  小程序密钥protected string $merchantId;  //  商户号protected string $certificateSerial;  //  证书序列号protected string $apiV3Key;  //  APIv3密钥protected object $instance;  //  实例protected string $merchantPrivateKeyFilePath;public function __construct(){$this->spAppid = config('wechat.sp.appid');$this->spAppSecret = config('wechat.sp.secret');$this->merchantId = config('wechat.pay.mchid');$this->certificateSerial = config('wechat.pay.certificate_serial');$this->apiV3Key = config('wechat.pay.api_v3_key');// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名$this->merchantPrivateKeyFilePath = root_path() . 'wxcert/apiclient_key.pem';if (!file_exists($this->merchantPrivateKeyFilePath)) throw new \Exception('商户API私钥文件不存在');$merchantPrivateKeyFilePath = 'file://' . $this->merchantPrivateKeyFilePath;$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名$platformCertificateFilePath = root_path() . 'wxcert/cert.pem';if (!file_exists($platformCertificateFilePath)) throw new \Exception('微信支付平台证书文件不存在');$platformCertificateFilePath = 'file://' . $platformCertificateFilePath;$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);// 从「微信支付平台证书」中获取「证书序列号」$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);// 构造一个 APIv3 客户端实例$this->instance = Builder::factory(['mchid' => $this->merchantId,   //  商户号'serial' => $this->certificateSerial,   //「商户API证书」的「证书序列号」'privateKey' => $merchantPrivateKeyInstance,'certs' => [$platformCertificateSerial => $platformPublicKeyInstance,],]);}/*** @note 获取微信支付预交易订单* @param string $openid 用户openid* @param string $out_trade_no 订单号* @param string $notify_url 回调地址* @param float $price 价格* @param string $desc 描述*/public function spPrepayId(string $openid, string $out_trade_no, string $notify_url, float $price = 0.01, string $desc = '订单'){$prepay_id = '';try {$resp = $this->instance->chain('/v3/pay/transactions/jsapi')->post(['json' => ['mchid' => $this->merchantId,'out_trade_no' => $out_trade_no,'appid' => $this->spAppid,'description' => $desc,'notify_url' => $notify_url,'amount' => ['total' => $price * 100,'currency' => 'CNY'],'payer' => ['openid' => $openid]]]);$res = json_decode($resp->getBody());$prepay_id = $res->prepay_id;} catch (\Exception $e) {// 进行错误处理echo $e->getMessage(), PHP_EOL;;if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {$r = $e->getResponse();echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;}echo $e->getTraceAsString(), PHP_EOL;}return $prepay_id;}/*** @note 生成签名* @param string $prepay_id 预交易订单* @param string $nonceStr 随机字符串* @param string $timeStamp 时间戳* @return string*/public function makeSign(string $prepay_id, string $nonceStr, string $timeStamp): string{if (!file_exists($this->merchantPrivateKeyFilePath)) return '';$merchantPrivateKeyFilePath = 'file://' . $this->merchantPrivateKeyFilePath;$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);$params = ['appId' => $this->spAppid,'timeStamp' => $timeStamp,'nonceStr' => $nonceStr,'package' => 'prepay_id=' . $prepay_id,];$params += ['paySign' => Rsa::sign(Formatter::joinedByLineFeed(...array_values($params)),$merchantPrivateKeyInstance), 'signType' => 'RSA'];return $params['paySign'] ?? '';}/*** @note 回调通知,参数解密* @param string $inWechatpaySignature 微信支付平台签名* @param string $inWechatpayTimestamp 微信支付平台时间戳* @param string $inWechatpayNonce 微信支付平台随机串* @param string $inBody 通知内容* @param string $inWechatpaySerial 平台证书序列号* @param string $inRequestID 请求ID* @return array*/public function notifyDecrypt(string $inWechatpaySignature, string $inWechatpayTimestamp, string $inWechatpayNonce, string $inBody, string $inWechatpaySerial, string $inRequestID = ''): array{// 根据通知的平台证书序列号,查询本地平台证书文件,$platformCertificateFilePath = root_path() . 'wxcert/cert.pem';if (!file_exists($platformCertificateFilePath)) throw new \Exception('微信支付平台证书文件不存在');$platformCertificateFilePath = 'file://' . $platformCertificateFilePath;$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);// 检查通知时间偏移量,允许5分钟之内的偏移$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);$verifiedStatus = Rsa::verify(// 构造验签名串Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),$inWechatpaySignature,$platformPublicKeyInstance);if ($timeOffsetStatus && $verifiedStatus) {// 转换通知的JSON文本消息为PHP Array数组$inBodyArray = (array)json_decode($inBody, true);// 使用PHP7的数据解构语法,从Array中解构并赋值变量['resource' => ['ciphertext' => $ciphertext,'nonce' => $nonce,'associated_data' => $aad]] = $inBodyArray;// 加密文本消息解密$inBodyResource = AesGcm::decrypt($ciphertext, $this->apiV3Key, $nonce, $aad);// 把解密后的文本转换为PHP Array数组return (array)json_decode($inBodyResource, true);}return [];}/*** @note 加密消息解密*/public function decryptMsg($encryptedData, $iv, $sessionKey): array|string{$pc = new WxBizDataCrypt($this->spAppid, $sessionKey);$errCode = $pc->decryptData($encryptedData, $iv, $data);if ($errCode == 0) {return $data;}return [];}}

4.封装接口(完整示例如下)

<?phpnamespace app\api\controller\sp;use think\response\Json;class Activity 
{/*** @note 生成订单*/public function prepayId(): void{$activityId = $this->request->post('ac_id/d', 1);if (empty($activityId)) $this->error('赛事错误,请重试!');$openid = $this->request->post('openid/s', '');if (empty($openid)) $this->error('支付用户获取失败,请重试!');$model = new ActivityModel();$activity = $model->findOrEmpty($activityId)->toArray();if (empty($activity)) $this->error('get Err');if ($activity['status'] != 1) $this->error('get Err!');//  订单信息$orderInfo = ['activity_id' => $activityId,'openid' => $openid,'number' => 'order' . date('YmdHis') . rand(1000, 9999),'money' => $activity['price'],'type' => 1,'status' => 0];//  生成订单$pay = new WechatPay();$notify_url = env('domain') . 'index.php/api/sp.Activity/notify';$prepayId = $pay->spPrepayId($openid, $orderInfo['number'], $notify_url);if (empty($prepayId)) $this->error('订单生成失败,请重试!');$orderInfo['prepay_id'] = $prepayId;$order = new Order();$order->save($orderInfo);$timeStamp = (string)time();$orderInfo['timeStamp'] = $timeStamp;$nonceStr = getRandStr(32);$orderInfo['nonceStr'] = $nonceStr;$orderInfo['package'] = 'prepay_id=' . $prepayId;$orderInfo['paySign'] = $pay->makeSign($prepayId, $nonceStr, $timeStamp);$this->success('get Success', ['order' => $orderInfo]);}/*** @note 支付回调*/public function notify(): Json{$inWechatpaySignature = request()->header('Wechatpay-Signature', ''); // header中获取签名$inWechatpayTimestamp = request()->header('Wechatpay-Timestamp', ''); // header中获取时间戳$inWechatpaySerial = request()->header('Wechatpay-Serial', ''); // header中获取证书序列号$inWechatpayNonce = request()->header('Wechatpay-Nonce', ''); // header中获取随机字符串$inRequestID = request()->header('Request-ID', ''); // 请根据实际情况获取$inBody = file_get_contents('php://input'); // 请根据实际情况获取,例如: file_get_contents('php://input');$pay = new WechatPay();$res = $pay->notifyDecrypt($inWechatpaySignature, $inWechatpayTimestamp, $inWechatpayNonce, $inBody, $inWechatpaySerial, $inRequestID);if (!empty($res)) {//  进行订单数据修改$order = new Order();//  查询订单数据$orderInfo = $order->where('number', $res['out_trade_no'])->find();if (!empty($orderInfo)){$result = $order->where('id',$orderInfo['id'])->save(['transaction_id' => $res['transaction_id'],'status' => $res['trade_state'] == 'SUCCESS' ? 1 : 0,'trade_type' => $res['trade_type'],'trade_state_desc' => $res['trade_state_desc'],'bank_type' => $res['bank_type'],'success_time' => $res['success_time']]);cache(':order_' . $res['out_trade_no'], $result, 3600);}return json(['code' => 'SUCCESS']);}return json(['message' => '失败', 'code' => 'FAIL']);}}

5.uniapp示例

<template><view class="box"><view><up-button text="立即支付" type="primary" @click="toPay"></up-button>    </view><up-toast ref="uToastRef"></up-toast></view>
</template><script setup>import {onLoad} from '@dcloudio/uni-app'import {ref,} from 'vue';import {getPrepayId} from '@/utils/api/order.js'const uToastRef = ref(null)//	点击支付const toPay = () => {getPrepayId({openid: ''}).then((res) => {if (res.code == 1) {const order = res.data.orderuni.requestPayment({provider: 'wxpay',timeStamp: order.timeStamp, //	时间戳nonceStr: order.nonceStr, //	随机字符串,长度为32个字符以下package: order.package, //		统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***signType: 'RSA', //	签名算法,应与后台下单时的值一致paySign: order.paySign, //	签名success: function(res) {console.log('success:' + JSON.stringify(res));},fail: function(err) {console.log('fail:' + JSON.stringify(err),);}});} else {uToastRef.value.error(res.msg)}})}
</script><style lang="scss">.box {width: 100%;}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/685581.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为招聘目的:不是筛选人才,而是筛选忠诚度。。

华为招聘 日常瞎逛发现一篇「机械应届生锐评华为」的帖子&#xff1a; 楼主提到&#xff1a;华为很会营销&#xff0c;华为和爱国之间的等号已经植入到老一辈的思想中&#xff0c;这就导致家里父母总是不断关心华为招聘的进展。 虽然楼主原本并不反感华为&#xff0c;但却被本次…

《黑暗之魂》系列迎来打折:魂三标准版半价134,豪华版199

《黑暗之魂》系列游戏降价促销&#xff0c;现在是购买的好时机&#xff01;想必喜欢挑战和探索的玩家们对这个消息会特别感兴趣。让我们来看看具体的折扣信息吧&#xff01; 首先是《黑暗之魂&#xff1a;重制版》&#xff0c;原价198元&#xff0c;现价仅为99元&#xff01;这…

winform图书销售管理系统+mysql

winform图书销售管理系统mysql数据库说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 功能模块&#xff1a; 管理员:ttt 123 登陆可以操作我的 个人信息 修改密码 用户信息 添加删除用户 图书 添加删除图书信息 购物车 购买订单信息 充值 退出账户 …

【数据分享】2006-2022年我国省份级别的市政公用设施建设固定资产投资相关指标(30多项指标)

《中国城市建设统计年鉴》中细致地统计了我国城市市政公用设施建设与发展情况&#xff0c;在之前的文章中&#xff0c;我们分享过基于2006-2022年《中国城市建设统计年鉴》整理的2006—2022年我国省份级别的市政设施水平相关指标、2006-2022年我国省份级别的各类建设用地面积数…

白酒:酒精度数与白酒品质消费的关联性研究

酒精度数作为白酒的一项重要指标&#xff0c;不仅影响着白酒的口感和风格&#xff0c;更在很大程度上与白酒的消费存在密切关联。在探讨云仓酒庄豪迈白酒时&#xff0c;我们不能忽视酒精度数与品质消费之间的关联性。 首先&#xff0c;酒精度数的高低直接影响到白酒中酒精的含量…

泰国小店又出“黑马”!如何在3个月打造百万GMV店铺?

泰国&#xff0c;正在逐渐成为TikTk Shop最重要的战场之一。 就在4月份&#xff0c;泰国TikTok小店整体GMV跑出了超400万美金的规模&#xff0c;占全球市场份额超过21.03%&#xff0c;连续两个月蝉联第一。此时&#xff0c;距离上线泰国TikTok Shop Mall已经过去了一个多月&am…

【C++阅览室】C++之Vector(容器)

目录 vector的介绍 vector的使用 vector的定义 vector iterator 的使用 vector 空间增长问题 vector 增删查改 vector 迭代器失效问题。&#xff08;重点&#xff09; vector的介绍 1、 vector 是表示可变大小数组的序列容器&#xff0c;可以使用连…

【鸟叔的Linux私房菜】1-Linux是什么与如何学习

文章目录 Linux是什么Linux的发展Linux的内核版本 Linux的学习学习方法学习重点处理问题 总结 Linux是什么 Linux是一个操作系统&#xff0c;包括内核和系统调用。开源的操作系统。 同一个操作系统无法在不同的硬件上运行&#xff0c;将操作系统修改代码从一个硬件平台迁移到…

effective python学习笔记_类与接口

用组合类实现多层结构而不用内置类型 例子&#xff1a;成绩单&#xff0c;存储学生各科成绩多个然后加权重&#xff0c;如果用字典类型会导致字典有多层嵌套结构 思想 当用内置类型如字典元组等结构出现超过二层的多层嵌套结构时&#xff0c;读起来会比较难懂&#xff0c;此时…

【影片欣赏】【指环王】【魔戒:国王归来 The Lord of the Rings: The Return of the King】

往期魔戒博客见&#xff1a; 【影片欣赏】【指环王】【魔戒&#xff1a;护戒使者 The Lord of the Rings: The Fellowship of the Ring】 【影片欣赏】【指环王】【魔戒&#xff1a;双塔奇谋 The Lord of the Rings: The Two Towers】 2004年发行&#xff0c;Special Extend…

kotlin语法快速入门--(完整版)

Kotlin语法入门 文章目录 Kotlin语法入门一、变量声明1、整型2、字符型3、集合3.1、创建array数组3.2、创建list集合3.3、不可变类型数组3.4、Set集合--不重复添加元素3.5、键值对集合Map 4、kotlin特有的数据类型和集合4.1、Any、Nothing4.2、二元组--Pair4.3、三元组--Triple…

中国211大学全部排名一览表

211大学是指中国教育部实施的名为“211工程”的高等教育发展战略中被选为重点支持的高等院校。这个名称来源于项目的启动背景和目标&#xff1a;“211”中的“21”代表21世纪&#xff0c;意味着该项目面向21世纪的中国高等教育发展&#xff1b;“1”则意指要重点建设大约100所左…