微信小程序完整实现微信支付功能(SpringBoot和小程序)

1.前言

不久前给公司实现支付功能,折腾了一阵子,终于实现了,微信支付对于小白来说真的很困难,特别是没有接触过企业级别开发的大学生更不用说,因此尝试写一篇我如何从小白实现微信小程序支付功能的吧,使用的后端是SpringBoot

2.准备工作

首先,要实现支付功能的条件:

(1)小程序是企业级别

(2)拥有微信支付商户号

(3)小程序绑定商户号

(4)拥有域名,并且有SSL证书(也就是HTTPS)

满足以上条件即可开始配置支付功能,这里我实现的是JSAPI支付(也就是小程序直接提供数字金额支付),还有Native支付(也就是弹出二维码进行扫码支付)

3.后端实现

先讲后端,因为后端需要准备的东西比较多,后端差不多就如下图三个类

不过要先准备如下东西,这些都需要去微信支付网页登录得到如下图登录,具体去看其他教程

申请证书,然后可以和我一样把证书放在项目的resources文件夹,如下

导入微信支付的pom.xml相关包依赖

<!-- 微信支付坐标 start--><dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>4.2.5.B</version></dependency><!-- 退款用 --><dependency><groupId>org.jodd</groupId><artifactId>jodd-http</artifactId><version>6.0.8</version></dependency><!-- 微信支付坐标 end-->

微信支付在yml文件的相关配置信息,没有的信息就登录商户号申请得到,接下来如果你是小白的话建议直接复制粘贴我的代码。

# 微信pay相关
wxpay:# appIdappId: wx23d3df1350a9xxxx #小程序appId# 商户idmchId:  164919xxxx #商户Id# 商户秘钥mchKey: xxxxxxxxxxx #商户密钥,登录商户号自定义# p12证书文件的绝对路径或者以classpath:开头的类路径.keyPath: classpath:/wxpay_cert/apiclient_cert.p12 #证书路径,我放在项目resources目录下privateKeyPath: classpath:/wxpay_cert/apiclient_key.pem #这个也是和上面一样privateCertPath: classpath:/wxpay_cert/apiclient_cert.pem #这个也是一样# 微信支付的异步通知接口notifyUrl: https://www.xxxx.com/wechat/pay/notify #这个是回调函数就是前端要来访问支付的路由,可以自己写,域名写自己的# 退款回调地址refundNotifyUrl: https://www.xxxx.com/wechat/pay/refund_notify #退款的也一样

接下来就是获取上面配置信息的Java代码,WechatPayConfig类,注意这里变量名和yml文件的变量名要一样


import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {private String appId;private String mchId;private String mchKey;private String keyPath;private String privateKeyPath;private String privateCertPath;private String notifyUrl;private String refundNotifyUrl;}

接下来就是人们说的创建支付统一订单,BizWechatPayServic类,直接复制粘贴


import com.example.mengchuangyuan.common.wechat.wxpay.config.WechatPayConfig;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;import java.net.InetAddress;/*** 微信支付*/
@Slf4j
@Service
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WechatPayConfig.class)
@AllArgsConstructor
public class BizWechatPayService {private WechatPayConfig wechatPayConfig;public WxPayService wxPayService() {WxPayConfig payConfig = new WxPayConfig();payConfig.setAppId(wechatPayConfig.getAppId());payConfig.setMchId(wechatPayConfig.getMchId());payConfig.setMchKey(wechatPayConfig.getMchKey());payConfig.setKeyPath(wechatPayConfig.getKeyPath());payConfig.setPrivateKeyPath(wechatPayConfig.getPrivateKeyPath());payConfig.setPrivateCertPath(wechatPayConfig.getPrivateCertPath());// 可以指定是否使用沙箱环境payConfig.setUseSandboxEnv(false);payConfig.setSignType("MD5");WxPayService wxPayService = new WxPayServiceImpl();wxPayService.setConfig(payConfig);return wxPayService;}/*** 创建微信支付订单** @param productTitle 商品标题* @param outTradeNo   订单号* @param totalFee     总价* @param openId     openId* @return*/public Object createOrder(String productTitle, String outTradeNo, Integer totalFee, String openId) {log.info(openId);log.info(wechatPayConfig.toString());try {WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();// 支付描述request.setBody(productTitle);// 订单号request.setOutTradeNo(outTradeNo);// 请按照分填写request.setTotalFee(totalFee);// 小程序需要传入 openidrequest.setOpenid(openId);// 回调链接request.setNotifyUrl(wechatPayConfig.getNotifyUrl());// 终端IP.request.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());// 设置类型为JSAPIrequest.setTradeType(WxPayConstants.TradeType.JSAPI);// 一定要用 createOrder 不然得自己做二次校验Object order = wxPayService().createOrder(request);return order;} catch (Exception e) {return "未知错误!";}}/*** 退款** @param tradeNo 订单号* @param totalFee 总价* @return*/public WxPayRefundResult refund(String tradeNo, Integer totalFee) {WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();wxPayRefundRequest.setTransactionId(tradeNo);wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));wxPayRefundRequest.setTotalFee(totalFee);wxPayRefundRequest.setRefundFee(totalFee);wxPayRefundRequest.setNotifyUrl(wechatPayConfig.getRefundNotifyUrl());try {WxPayRefundResult refund = wxPayService().refundV2(wxPayRefundRequest);if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {return refund;}} catch (WxPayException e) {e.printStackTrace();}return null;}
}

然后到提供前端调用支付路由的类,WechatController类,注意我这里路由拼接的有/wechat/pay/notify,这个要和之前配置yml文件的支付回调函数一样,要不然不行。这里也可以看到需要用户的openid,获取openid我就不多说了,有疑问的同学直接私信我或者评论区

package com.example.mengchuangyuan.common.wechat.wxpay.controller;import com.example.mengchuangyuan.common.wechat.wxpay.service.BizWechatPayService;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;@Slf4j
@RestController
@RequestMapping("/wechat/pay")
@AllArgsConstructor
public class WechatController {BizWechatPayService wechatPayService;private static Logger logger = LoggerFactory.getLogger(WechatController.class);/*** 创建微信订单** @return 统一下单参数*/
//    @GetMapping("/unified/request")
//    public Object appPayUnifiedRequest(@RequestParam("openid") String openid,@RequestParam("totalFee") Integer totalFee) {
//        log.info(openid);
//        // totalFee 必须要以分为单位
//
//        Object createOrderResult = wechatPayService.createOrder("报名支付", String.valueOf(System.currentTimeMillis()), totalFee*100, openid);
//        log.info("统一下单的生成的参数:{}", createOrderResult);
//        return createOrderResult;
//    }@GetMapping("/unified/request")public Object appPayUnifiedRequest(@RequestParam("openid") String openid,@RequestParam("totalFee") Integer totalFee) {log.info(openid);// totalFee 必须要以分为单位Object createOrderResult = wechatPayService.createOrder("报名支付", String.valueOf(System.currentTimeMillis()), totalFee*100, openid);log.info("统一下单的生成的参数:{}", createOrderResult);return createOrderResult;}@PostMapping("/notify")public String notify(@RequestBody String xmlData) {try {WxPayOrderNotifyResult result = wechatPayService.wxPayService().parseOrderNotifyResult(xmlData);// 支付返回信息if ("SUCCESS".equals(result.getReturnCode())) {// 可以实现自己的业务逻辑logger.info("来自微信支付的回调:{}", result);}return WxPayNotifyResponse.success("成功");} catch (WxPayException e) {logger.error(e.getMessage());return WxPayNotifyResponse.fail("失败");}}/*** 退款** @param transaction_id*/@PostMapping("/refund")public void refund(@RequestBody String transaction_id) {// totalFee 必须要以分为单位,退款的价格可以这里只做的全部退款WxPayRefundResult refund = wechatPayService.refund(transaction_id, 1);// 实现自己的逻辑logger.info("退款本地回调:{}", refund);}/*** 退款回调** @param xmlData* @return*/@PostMapping("/refund_notify")public String refundNotify(@RequestBody String xmlData) {// 实现自己的逻辑logger.info("退款远程回调:{}", xmlData);// 必须要返回 SUCCESS 不过有 WxPayNotifyResponse 给整合成了 xml了return WxPayNotifyResponse.success("成功");}}

到此后端完成,说明一下,我只实现了支付功能,接下来前端没有退款功能

4.前端(小程序端)

前端相对简单,就是小程序给后端金额并且向后端发起支付请求弹出支付界面输入密码就OK,就一个方法。

goPay(e) {var that = this//用户openid,我之前缓存有的,这里根据你是如何获取用户openidvar openid = app.globalData.userInfo.openid sendRequest({url: "/wechat/pay/unified/request", //后端支付请求路由method: 'GET',data: {openid: openid,totalFee: that.data.money //支付金额,注意是以分为单位,要么在前端处理乘100,要么在后端,在我后端代码中,可以看出我选择的是后端处理// totalFee: 1},}).then(res => {// console.log(res);let obj = {  //这里的数据就是之前后端生成的统一订单appid: res.data.appId,noncestr: res.data.nonceStr,package: res.data.packageValue,// partnerid: res.data.partnerId,prepayid: res.data.prepayId,timestamp: res.data.timeStamp,signType: res.data.signType,paySign: res.data.paySign,};// console.log(obj);这里就是小程序弹出支付界面了wx.requestPayment({ //下面参数为必传provider: 'wxpay', //支付类型appId: obj.appid, //小程序AppidtimeStamp: obj.timestamp, //创建订单时间戳nonceStr: obj.noncestr, // 随机字符串,长度为32个字符以下。package: obj.package, // 统一下单接口返回的 prepay_id 参数值,格式如“prepay_id=*”signType: obj.signType, // 加密方式统一'MD5'paySign: obj.paySign, // 后台支付签名返回success: function (res) {// 支付成功后的回调函数, res.errMsg = 'requestPayment:ok'// console.log(res);if (res.errMsg === 'requestPayment:ok') {that.signStart(e)}},fail: function (res) {// console.log(res);// 支付失败或取消支付后的回调函数, res.errMsg = 'requestPayment:fail cancel' 取消支付;res.errMsg = 'requestPayment:fail (detail error message)'}})}).catch(err => {wx.showModal({content: "支付失败,请重试!",showCancel: false})})},

sendRequest函数包装在request.js如下

request.js代码内容如下

const baseUrl = 'https://www.xxxx.com'const sendRequest = (params) => {//   console.log(params); return new Promise((resolve, reject) => {wx.request({url: baseUrl + params.url,method: params.method || 'GET',header: {'Content-Type': 'application/json' // 设置请求的Content-Type},data: params.data || '',success: res => {// console.log(res);resolve(res)},fail: err => {reject(err)}})})
}export default sendRequest

效果如下,这里因为我的手机不能截图支付页面,所以用的开发者工具支付的效果,都是一样的。

到此为止,微信小程序支付功能就完成了,看似简单,但是应该还有一些细节要处理,希望大家有耐心处理。

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

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

相关文章

当老师应该选文科还是理科

教育不断发展和改革&#xff0c;教师职业的选择也越来越受到关注。许多人在选择专业时都会考虑成为一名教师&#xff0c;但对于选择文科还是理科却感到困惑。本文将探讨当老师应该选文科还是理科。 文科注重的是人文素养和社会科学方面的知识&#xff0c;而理科则注重自然科学和…

electron27-react-mateos:基于electron+react18仿matePad桌面系统

基于Electron27React18ArcoDesign搭建桌面版OS管理系统。 electron-react-mateos 基于最新前端跨端技术栈electron27.xreact18arco-designzustand4sortablejs构建的一款仿制matePad界面多层级路由管理OS系统。 ElectronReactOS支持桌面多路由配置&#xff0c;新开窗口弹窗开启路…

windows电脑定时开关机设置

设置流程 右击【此电脑】>【管理】 【任务计划程序】>【创建基本任务】 gina 命令 查看 已经添加的定时任务从哪看&#xff1f;这里&#xff1a; 往下滑啦&#xff0c;看你刚才添加的任务&#xff1a;

61 权限提升-RedisPostgre令牌窃取进程注入

目录 演示案例:Redis数据库权限提升-计划任务PostgreSQL数据库权限提升Windows2008&7令牌窃取提升-本地Windows2003&10进程注入提升-本地pinjector进程注入工具针对-win2008以前操作系统pexec64 32进程注入工具针对-win2008及后操作系统- (佛系) 涉及资源: postgersql是…

【经验分享】在vm中安装openEuler及使用yum安装openGauss

1.前言 随着互联网时代对数据库的新要求,以PostgreSQL为基础的开源数据库openGauss应运而生。openGauss在保持PostgreSQL接口兼容的前提下,对其查询优化器、高可用特性等进行了全面优化,实现了超高性能。 同时,openGauss作为社区项目,新增功能持续丰富。优点是查询性能高、可…

BMS实战: BMS产品介绍,电池外观分析,电芯种类分析,焊接方式分析,充电方式,电压平台,电芯型号分析。

快速入门的办法就是了解产品,了解现在市面上正在流通的成熟产品方案。光看基础知识是没有效果的。 首先我们找到了一张市面上正在出售的电池pack包。 图片来源网上,侵权删 电池外观分析 外壳: 一般是金属外壳,大部分都是铁壳加喷漆,特殊材质可以定制。 提手 一般是…

Word怎么看字数?简单教程分享!

“我在写文章时&#xff0c;总是想看看写了多少字。但是我发现我的Word无法看到字数。在Word中应该怎么查看字数呢&#xff1f;请帮帮我&#xff01;” Word是一个广泛使用的文档编辑工具。在我们编辑文章时&#xff0c;如果想查看写了多少字&#xff0c;也是可以轻松完成的。 …

2015年7月28日 Go生态洞察:GopherCon 2015综述

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

python 如何利用everything的能力快速搜索兴趣文档

演示代码 # -*- coding:UTF-8 -*- """ author: dyy contact: douyaoyuan126.com time: 2023/11/23 17:10 file: python 如何通过everything搜索兴趣文档.py desc: xxxxxx """# region 引入必要的依赖 import os模块名 DebugInfo try:from Debu…

智能医疗越发周到!新的机器人系统评估中风后的活动能力

原创 | 文 BFT机器人 中风是在医疗界上最难的解决的病例之一&#xff0c;全球每年有超过1500万人中风&#xff0c;四分之三的中风患者的手臂和手部会出现损伤、虚弱和瘫痪。 许多中风患者日常生活是依靠他们强壮的手臂来完成的&#xff0c;从拿一些小东西到梳头&#xff0c;即…

【Python】itertools模块,补充:可迭代对象、迭代器

Python中 itertools模块创建高效迭代器、处理序列数据集。 此模块所有函数返回迭代器&#xff0c;可用for循环获取迭代器中的内容&#xff0c;也可用list(...)用列表形式显示内容。 import itertools[ x for x in dir(itertools) if not x.startswith(_)] # 结果&#xff1a;…

内衣洗衣机怎么选?性价比高的小型洗衣机推荐

在机器解放了双手的时代中&#xff0c;洗衣机走进了千家万户&#xff0c;虽然在某种程度上缓解了人们手洗衣服的压力&#xff0c;但还是有不少人选择了人工手洗自己的内衣内裤&#xff0c;甚至连袜子都是手工洗的&#xff0c;这让人很是郁闷&#xff0c;倒不是说洗衣机不方便&a…