【Java 实现微信支付、Native 支付流程】,从编写代码到支付成功,一步到位!
文章目录
- 1. 项目环境介绍
- 2. 微信支付文档
- 2.1 业务流程说明
- 3. 准备信息
- 3.1 微信公众账号如何获取?
- 3.2 商户号如何获取?
- 3.3 API密钥如何获取?
- 3.4 准备工具类
- 4. 进入开发阶段
- 4.1 后端编写`生成微信支付二维码`的接口
- 4.2 前端实现
- 4.3 后端编写 `查询订单支付状态` 接口
- 4.4 前端调用 `查询订单支付状态` 接口
- 总结
1. 项目环境介绍
jdk 1.8
mysql 5.7
maven 3.6
项目前后端分离:后端 SpringBoot 项目、前端 Vue 项目
2. 微信支付文档
官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html
Native 场景介绍:用户扫描商户展示在各种场景的二维码进行支付。
这里我们支付流程选择方式二:
官方流程图:
2.1 业务流程说明
用我自己完成一次的过程来说。
- 在前端页面,用户肯定要点击某个按钮或者东西来触发事件,调用我们自己编写的接口,然后返回给前端一个二维码。
- 我们通过调用微信支付 【统一下单API】接口得到一个 code_url。
- 然后在前端通过某项技术跟据返回的 code_url 生成二维码。
- 用户打开微信 “扫一扫”,然后扫描这个二维码。
- 进入支付页面,支付指定金额后完成支付交易。
- 然后我们在调用微信支付【查询订单 API】查询用户支付状态
- 通过此支付状态可以判断用户支付成功还是支付失败。
- 支付成功就执行我们自己的业务逻辑,一般像修改定单状态改成已支付。
3. 准备信息
pom 文件引入微信支付 SDK 依赖
<!-- 微信支付 SDK --><dependency><groupId>com.github.wxpay</groupId><artifactId>wxpay-sdk</artifactId><version>0.0.3</version></dependency>
由于我们后面要调用微信支付的 API,所以我们要提前准备好一些配置信息.
/*** 微信支付参数配置信息** @author: 南独酌酒 <211425401@126.com>* @date: 2020/10/9 16:14*/
public class ConfigUtil {/*** 微信公众账号*/public final static String APPID = "填写自己的";/*** 商户号*/public final static String MCH_ID = "填写自己的";/*** API密钥*/public final static String API_KEY = "填写自己的";
}
3.1 微信公众账号如何获取?
公众账号ID是什么:微信支付分配的公众账号ID(企业号corpid即为此appId)
申请地址:PC网站接入支付
3.2 商户号如何获取?
商户号是什么:微信支付分配的商户号
申请地址:https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal
3.3 API密钥如何获取?
文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=4_3
API 密钥是自己的商户平台设置的:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
这三种信息的获取,大家要好好看文档,微信的生态圈还是挺庞大的,还是要多花些时间好好研究一下。
3.4 准备工具类
这是用来发送 Http 请求的工具类
import javax.net.ssl.HttpsURLConnection;
import java.io.OutputStream;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang.StringUtils;/*** 自定义微信支付工具类** @author: 南独酌酒 <211425401@126.com>* @date: 2020/10/9 16:34*/
public class CommonUtil {/*** 发送 http 请求** @param requestUrl 请求路径* @param requestMethod 请求方式(GET/POST/PUT/DELETE/...)* @param outputStr 请求参数体* @return 结果信息*/public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {try {URL url = new URL(requestUrl);HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);// 设置请求方式(GET/POST)conn.setRequestMethod(requestMethod);conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");// 当outputStr不为null时向输出流写数据if (null != outputStr) {OutputStream outputStream = conn.getOutputStream();// 注意编码格式outputStream.write(outputStr.getBytes("UTF-8"));outputStream.close();}// 从输入流读取返回内容InputStream inputStream = conn.getInputStream();InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");BufferedReader bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}// 释放资源bufferedReader.close();inputStreamReader.close();inputStream.close();inputStream = null;conn.disconnect();return buffer.toString();} catch (Exception e) {e.printStackTrace();}return null;}/*** 获取ip** @param request 请求* @return ip 地址*/public static String getIp(HttpServletRequest request) {if (request == null) {return "";}String ip = request.getHeader("X-Requested-For");if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("X-Forwarded-For");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}
}
4. 进入开发阶段
4.1 后端编写生成微信支付二维码
的接口
编写第一个接口:主要的作用是调用微信支付【统一下单 API】,这里接口返回结果我们封装成一个 Map ,主要返回给前端 code_url
二维码、 total_fee
总金额、 out_trade_no
订单号
创建一个 WeiXinPayController 控制器
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.ruoyi.common.utils.pay.CommonUtil;
import com.ruoyi.common.utils.pay.ConfigUtil;
import com.ruoyi.framework.web.domain.AjaxResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;/*** 微信支付控制层** @author: 南独酌酒 <211425401@126.com>* @date: 2020/10/9 17:07*/
@RestController
@RequestMapping(value = "/pay")
public class WeiXinPayController {private static final DecimalFormat df = new DecimalFormat("#");/*** 生成微信支付二维码** @param outTradeNo 订单号* @param totalFee 金额(分)*/@RequestMapping(value = "/createNative")public Map<String, String> createNative(@RequestParam("outTradeNo") String outTradeNo,@RequestParam("totalFee") String totalFee,HttpServletRequest request, HttpServletResponse response) {try {//todo 创建请求参数SortedMap<String, String> req = new TreeMap<String, String>();req.put("appid", ConfigUtil.APPID); //公众号req.put("mch_id", ConfigUtil.MCH_ID); // 商户号req.put("nonce_str", WXPayUtil.generateNonceStr()); // 32位随机字符串req.put("body", "去氘水"); // 商品描述req.put("out_trade_no", outTradeNo); // 商户订单号req.put("total_fee", df.format(Double.parseDouble(totalFee) * 100)); // 标价金额(分)req.put("spbill_create_ip", CommonUtil.getIp(request)); // 终端IPreq.put("notify_url", "http://www.baidu.com"); // 回调地址req.put("trade_type", "NATIVE"); // 交易类型req.put("sign", WXPayUtil.generateSignature(req, ConfigUtil.API_KEY, WXPayConstants.SignType.MD5)); // 签名//todo 生成要发送的 xmlString xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);System.err.println(String.format("微信支付预下单请求 xml 格式:\n%s", xmlBody));//todo 发送 POST 请求 统一下单 API 并携带 xmlBody 内容,然后获得返回接口结果String result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", xmlBody);System.err.println(String.format("%s", result));//todo 将返回结果从 xml 格式转换为 map 格式Map<String, String> resultMap = WXPayUtil.xmlToMap(result);Map<String, String> map = new HashMap<>();map.put("code_url", resultMap.get("code_url")); // 支付地址map.put("total_fee", totalFee); // 总金额map.put("out_trade_no", outTradeNo); // 订单号return map;} catch (Exception e) {e.printStackTrace();return null;}}
}
4.2 前端实现
- 前端通过点击结算按钮,调用此接口,获取到返回的
code_url
属性,然后通过Vue-qr
来实现二维码的生成操作。
- 首先实现 showalert 函数,通过一系列表单选项都填写后,调用生成二维码的函数
// 立即支付按钮事件showalert() {let that = this;if (that.pro === '') {that.$message.warning("请选择收货省份");} else if (that.city === '') {that.$message.warning("请选择收货城市");} else if (that.value3 === '') {that.$message.warning("请填写联系人");} else if (that.value4 === '') {that.$message.warning("请填写联系电话");} else {if (that.fukuan === true) {that.createNative();} else {that.$message.warning("请选择支付方式");}}}}
- 二维码实现的函数调用
// 生成微信支付二维码createNative() {let that = this;let outTradeNo = moment(new Date()).format('YYYYMMDDHHmmssSSS'); // 订单号let totalFee = 1;$.ajax({type: 'GET',url: 'http://localhost:8080/pay/createNative?outTradeNo=' + outTradeNo + '&totalFee=' + totalFee,success: function (res) {that.codeUrl = res.code_url;}});}
- moment 是一个日期格式化工具包【Moment.js 文档】
Vue 使用 moment :
// 通过 cnpm 安装 moment
cnpm install moment --save
// 在组件中引入后即可使用 moment
import moment from 'moment'
codeUrl
是用来在页面上展示二维码的变量
- 使用 Vue-qr 来在页面上展示二维码
// 安装 vue-qr
cnpm install vue-qr --save
// 使用还是先引入 vueqr
import VueQr from 'vue-qr'
// 然后通过 components 注册 VueQr
components: {VueQr
}
将二维码展示到页面上,codeUrl 就是用的上面提前声明好的 codeUrl
<vue-qr class="wximg":size="191":margin="0":auto-color="true":dot-scale="1":text="codeUrl"/>
常用属性介绍
4.3 后端编写 查询订单支付状态
接口
前面通过后台返回 code_url
,前端通过调用接口,然后利用 vue-qr 技术正确的将支付二维码展示到页面上去了。现在来通过调用微信支付【查询订单 API】,查看用户支付成功还是支付失败。
继续在 WeiXinPayController
控制器中添加接口,通过订单号查询此次支付的状态。
/*** 查询订单支付状态** @param outTradeNo 订单号* @return 支付状态*/@RequestMapping(value = "/queryOrder")public AjaxResult queryOrder(@RequestParam("outTradeNo") String outTradeNo) {int x = 0;while (true) {// 调用查询微信支付订单状态方法Map<String, String> map = this.queryPayStatus(outTradeNo);if (map.isEmpty()) {return AjaxResult.error("支付出错!");}if (map.get("trade_state").equals("SUCCESS")) {return AjaxResult.success("支付成功!");}try {// 间隔3秒TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}// 为了不让程序无休止的运行下去,定义一个 x 变量,当超过 100 次的话我们就退出此程序x++;if (x >= 100) {return AjaxResult.error("二维码超时!");}}}/*** 查询微信支付订单状态** @param outTradeNo 订单号* @return 支付状态*/private Map<String, String> queryPayStatus(String outTradeNo) {try {//todo 创建请求参数SortedMap<String, String> req = new TreeMap<String, String>();req.put("appid", ConfigUtil.APPID); // 公众号IDreq.put("mch_id", ConfigUtil.MCH_ID); // 商户号req.put("out_trade_no", outTradeNo); // 订单号req.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串req.put("sign", WXPayUtil.generateSignature(req, ConfigUtil.API_KEY, WXPayConstants.SignType.MD5));//todo 生成要发送的 xmlString xmlBody = WXPayUtil.generateSignedXml(req, ConfigUtil.API_KEY);System.err.println(String.format("查询订单支付状态 xml 格式:\n%s", xmlBody));//todo 调用查询订单支付状态 APIString result = CommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/orderquery", "POST", xmlBody);//todo 返回解析后的 map 数据Map<String, String> map = WXPayUtil.xmlToMap(result);System.out.println(String.format("%s", map));return map;} catch (Exception e) {e.printStackTrace();return null;}}
4.4 前端调用 查询订单支付状态
接口
- 在原本实现过的
createNative
函数中调用此接口
// 生成微信支付二维码createNative() {let that = this;let outTradeNo = moment(new Date()).format('YYYYMMDDHHmmssSSS'); // 订单号let totalFee = 1;$.ajax({type: 'GET',url: 'http://localhost:8080/pay/createNative?outTradeNo=' + outTradeNo + '&totalFee=' + totalFee,success: function (res) {that.codeUrl = res.code_url;// 在这里调用查询支付状态的函数 --------------------------begin/* 调用查询订单,查看微信支付状态订单号就是生成支付二维码接口返回的参数,直接传给 queryPayStatus 即可*/that.queryPayStatus(res.out_trade_no);// 在这里调用查询支付状态的函数 --------------------------end}});}
- queryPayStatus 函数实现
// 查询订单支付状态queryPayStatus(outTradeNo) {let that = this;$.ajax({type: 'GET',url: 'http://localhost:8080/pay/queryOrder?outTradeNo=' + outTradeNo,success: function (res) {if (res.code === 200) {// 支付成功that.$alert(res.msg, '支付窗口', {confirmButtonText: '确定'//todo 支付成功跳转至自己的订单页面});// 剩下的就是调用自己的业务逻辑等等.../*比如自己写个函数,这里来调用然后主要逻辑就是修改定单的状态为已支付然后清空购物车跳转至订单页面等等...*/} else {// 支付失败that.$alert(res.msg, '支付窗口', {confirmButtonText: '重新支付',callback: action => {that.createNative(); // 重新生成二维码}});}}});}
总结
到这里基本的支付流程就算完成了,通过调用微信开发文档提供的【统一下单】【查询订单】就可以实现一个简单的支付功能,还有后续一些申请退款等功能未实现。
如果你觉得这篇文章对你有帮助,那就给作者点个赞,来个评论增加点人气,让我也开心开心
【Java 实现微信支付、Native 支付流程】,从编写代码到支付成功,一步到位!相关推荐
- java实现微信支付与支付宝支付接口
因为公司要求需要写支付宝支付与微信支付现在写完了,总结一下: 支付宝支付: 支付宝支付比较简单首先我说一下支付宝支付与微信支付大概的流程,就拿支付宝支付来说(微信同理) 首先去蚂蚁金服注册一下App ...
- 支付宝商户支付接口接入流程
...
- stripe支付 新版paymentIntent(付款意图) demo代码
stripe支付 新版paymentIntent(付款意图) demo代码 stripe支付 旧版 charge付款方式 demo代码 可参考 特点: 1.实现客户绑卡,解卡等 2.全部由服务端进行操 ...
- stripe支付 旧版 charge付款方式 demo代码
stripe支付 旧版 charge付款方式 demo代码 stripe支付 新版paymentIntent(付款意图) demo代码 可参考. 特点: 1.实现客户绑卡,解卡等 2.全部由服务端进行 ...
- Java接入微信native、jsapi支付
Java接入微信native.jsapi支付 一.说明 本文示例使用的微信支付版本为V2版本,是旧版本,旧版本与新版本的接口不一,并不通用. 微信官方接口文档地址:https://pay.weixin ...
- JAVA 微信支付 native方式
最近做了一个微信native方式支付的demo,整理一下. 首先到微信公众号官网阅读开发文档,虽然文档对于java没有例子,但是也可以作参考.https://pay.weixin.qq.com/wik ...
- Java实现微信支付之Native模式
前言 微信支付以前就听说过,身边的同事也有弄过,但是自己因为没有遇到相关业务因此也没有去研究过.最近工作上可能会遇到微信支付因此也进行了些许研究,只是做到了接口掉通而已,并没有太深入,对微信支付已经很 ...
- Java对接微信支付(完整全流程)
Java对接微信支付及支付回调通知的全流程 一.所用框架.对接微信支付我们技术组用的是payment框架,因为该框架已整合springboot因此很方便快捷 <dependency>< ...
- java集成微信支付(完整流程)
java集成微信支付(完整流程) 1.申请微信支付能力 * 要想使用微信支付能力,不管是app支付.公众号支付.h5支付等支付方式都需要先在微信商户平台申请开通支付能力.* 申请开通支付能力的资料有公 ...
最新文章
- C++_STL——deque and vector
- Atitit 图像处理类库安装与安装模式的前世今生与未来大趋势attilax总结.docx
- Web开发经验谈之F12开发者工具/Web调试[利刃篇]
- webservice(二)简单实例
- CentOS添加常用yum源
- LeetCode_每日一题今日份_343.整除拆分
- oracle是delete可以加并行吗,提高Oracle DELETE性能的策略
- freemarker处理EL表达式
- c#制作飘动动画窗体
- matlab rem与mod 的区别
- 经典机器学习系列(六)【集成学习】之周志华西瓜书-AdaBoost算法证明解析
- 软件著作权提交源代bai码格式_软件著作权使用说明书字体要求-软件著作权提交源代码格式要求...
- 最新 | 机械工程领域SCI期刊一览(2020JCR)
- 脱不花,如何成为高效学习的人
- maya动画镜像_Maya
- Vin码识别功能实现
- 淘宝直播全屏页重排算法实践
- 【阅读笔记】Cost Volume Pyramid Based Depth Inference for Multi-View Stereo
- 安工大Linux程序设计实验
- 连续环境下基于enhanced GA算法的多目标多机器人路径算法
热门文章
- 系统管理员设置了系统策略,禁止进行此安装”解决方法!
- python分析数据走势图_左大营 | python数据分析篇系列1——探索沪深300指数(附代码)(中)...
- 关于webp、awebp、png、gif等网页中的图片下载,相互转换(亲测有用)
- 今日头条的用户体验分析
- Mysql update 的妙用之顺序操作
- 【大葱虽有4大治病功效】
- OSChina 周六乱弹 —— 到底谁是小公猫……
- 计算机软考高级职称专业,计算机软考高级职称有用吗
- 几乎必问,Spring 面试题开胃菜
- 新书推荐 |《好设计,有方法:我们在搜狐做产品体验设计》