从零玩转系列之微信支付实战PC端接口搭建
一、前言
halo各位大佬很久没更新了最近在搞微信支付,因商户号审核了我半个月和小程序认证也找了资料并且将商户号和小程序进行关联,至此微信支付Native支付完成.此篇文章过长我将分几个阶段的文章发布(项目源码都有,小程序和PC端)
在此之前已经更新了微信支付开篇、微信支付安全、微信实战基础框架搭建、本次更新为微信支付实战PC端接口搭建,实战篇分为几个章节因为代码量确实有点多哈.

本次项目使用技术栈
后端: SpringBoot3.1.x、Mysql8.0、MybatisPlus
前端: Vue3、Vite、ElementPlus
小程序: Uniapp、Uview
问题微信添加: BN_Tang
备注: 微信支付
二、Native模式
在com.yby6.service包下创建接口 WxPayService
package com.yby6.service;
import cn.hutool.json.JSONUtil;
import com.yby6.config.WxPayConfig;
import com.yby6.enums.WxApiType;
import com.yby6.utils.OrderNoUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
@RequiredArgsConstructor
public class WxPayService {
private final WxPayConfig wxPayConfig;
/**
* 会进行验证签名
* 发送请求
*/
private final CloseableHttpClient wxPayClient;
/**
* 统一调用下单API,生成支付二维码
*
* @param productId 商户ID
*/
@SneakyThrows
public Map<String, Object> nativePay(Long productId) {
log.info("调用统一下单API");
//调用统一下单API
HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));
// 请求body参数
Map<String, Object> paramsMap = builderRequestParams("测试页面统一下单", 1);
//将参数转换成json字符串
String jsonParams = JSONUtil.toJsonStr(paramsMap);
log.info("请求参数 ===> {}" + jsonParams);
StringEntity entity = new StringEntity(jsonParams, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
// 完成签名并执行请求
try (CloseableHttpResponse response = wxPayClient.execute(httpPost)) {
// 获取响应参数
Map resultMap = buildBodyParams(response, Map.class);
// 二维码
String codeUrl = (String) resultMap.get("code_url");
// 保存二维码
return new HashMap<>() {{
put("codeUrl", codeUrl);
}};
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 构建器请求参数
*
* @param description 描述
* @param total 总
* @return {@link Map}<{@link String}, {@link Object}>
*/
private Map<String, Object> builderRequestParams(String description, int total) {
Map<String, Object> paramsMap = new HashMap<>(14);
paramsMap.put("appid", wxPayConfig.getAppid());
paramsMap.put("mchid", wxPayConfig.getMchId());
paramsMap.put("description", description);
paramsMap.put("out_trade_no", OrderNoUtils.getOrderNo());
paramsMap.put("notify_url", wxPayConfig.getNotifyDomain()); // 微信回调通知 支付通知 和退款通知都调用这个接口
Map<String, Object> amountMap = new HashMap<>();
amountMap.put("total", total); // 分
amountMap.put("currency", "CNY");
// 设置金额
paramsMap.put("amount", amountMap);
return paramsMap;
}
/**
* 解析响应参数
*/
private <T> T buildBodyParams(CloseableHttpResponse response, Class<T> tClass) throws IOException {
T bodyAsString = null;
if (null != response.getEntity()) {
String json = EntityUtils.toString(response.getEntity());//响应体
bodyAsString = JSONUtil.toBean(json, tClass);
}
int statusCode = response.getStatusLine().getStatusCode();//响应状态码
if (statusCode == 200) { //处理成功
log.info("成功, 返回结果 = " + bodyAsString);
} else if (statusCode == 204) { //处理成功,无返回Body
log.info("成功");
} else if (statusCode == 404) { //处理成功,无返回Body
log.info("没找到订单...");
} else {
log.info("响应:{}, {}", response.getEntity(), response.getStatusLine());
log.info("失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
throw new IOException("request failed");
}
return bodyAsString;
}
}
在com.yby6.controller包下创建 WechatNativeController
package com.yby6.controller;
import com.yby6.config.WxPayConfig;
import com.yby6.reponse.R;
import com.yby6.service.WxPayService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
*
* @author Yang Shuai
* Create By 2023/6/8
*/
@Slf4j
@RestController
@RequestMapping("/api/wx-pay/native")
@RequiredArgsConstructor
public class WechatNativeController {
private final WxPayService wxPayService;
/**
* 调用统一下单API,生成支付二维码
*
* @param productId 产品id
* @return {@link R}
*/
@PostMapping("/native/{productId}")
public R<Map<String, Object>> nativePay(@PathVariable Long productId) {
log.info("发起支付请求 v3, 返回支付二维码连接和订单号");
return R.ok(wxPayService.nativePay(productId));
}
}
启动程序 请求下单接口 /api/wx-pay/native/native/1

复制返回的微信二维码地址
进入 https://cli.im/url 生成扫描二维码 使用微信扫描

结果可以正常扫码并且支付

接入商品和订单存储数据库 (重点)
修改 WxPayService 引入订单服务
/**
* 订单服务
*/
private final OrderInfoService orderInfoService;
修改 OrderInfoService 新增 createOrderByProductId 方法
package com.yby6.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yby6.domain.OrderInfo;
import com.yby6.domain.Product;
import com.yby6.enums.OrderStatus;
import com.yby6.mapper.OrderInfoMapper;
import com.yby6.utils.OrderNoUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfo> {
private final ProductService productService;
/**
* 创建订单产品id
*
* @param productId 产品id
* @param nickName 支付者名称 (用于小程序)
* @return {@link OrderInfo}
*/
public OrderInfo createOrderByProductId(Long productId, String nickName) {
// 查找已存在但未支付的订单
OrderInfo orderInfo = this.lambdaQuery().eq(OrderInfo::getProductId, productId).eq(OrderInfo::getOrderStatus, OrderStatus.NOTPAY.getType()).one();
if (orderInfo != null) {
return orderInfo;
}
// 根据商品ID 获取商品信息
final Product product = productService.lambdaQuery().eq(Product::getId, productId).one();
// 创建订单信息
orderInfo = new OrderInfo();
String productTitle = product.getTitle();
if (nickName != null) {
productTitle = productTitle.concat("-" + nickName);
}
orderInfo.setTitle(productTitle);
orderInfo.setOrderNo(OrderNoUtils.getOrderNo());
orderInfo.setProductId(productId);
orderInfo.setTotalFee(product.getPrice()); // 分
orderInfo.setOrderStatus(OrderStatus.NOTPAY.getType());
// 保存订单信息
save(orderInfo);
return orderInfo;
}
}
修改 nativePay 方法并且优化部分代码提公共
/**
* 统一调用下单API,生成支付二维码
*
* @param productId 商户ID
*/
@SneakyThrows
public Map<String, Object> nativePay(Long productId) {
log.info("生成订单");
// 生成订单
OrderInfo orderInfo = orderInfoService.createOrderByProductId(productId, null);
String codeUrl = orderInfo.getCodeUrl();
// 下下面这段代码限制了商品只能购买一次在有效期期间不能继续创建订单
if (StrUtil.isNotEmpty(codeUrl)) {
log.info("订单已存在,二维码已保存");
// 返回二维码
Map<String, Object> map = new HashMap<>();
map.put("codeUrl", codeUrl);
map.put("orderNo", orderInfo.getOrderNo());
return map;
}
log.info("调用统一下单API");
//调用统一下单API
HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));
// 请求body参数
builderRequestParams(httpPost, orderInfo.getTitle(), orderInfo.getTotalFee(), orderInfo.getOrderNo());
// 完成签名并执行请求
try (CloseableHttpResponse response = wxPayClient.execute(httpPost)) {
// 获取响应参数
Map resultMap = buildBodyParams(response, Map.class);
// 二维码
String url = (String) resultMap.get("code_url");
// 保存二维码
return saveCodeUrl(orderInfo, url);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 保存二维码
*
* @param orderInfo 订单信息
* @param codeUrl 保存二维码
*/
private Map<String, Object> saveCodeUrl(OrderInfo orderInfo, String codeUrl) {
//保存二维码
orderInfoService.lambdaUpdate().eq(OrderInfo::getOrderNo, orderInfo.getOrderNo()).set(OrderInfo::getCodeUrl, codeUrl).update();
//返回二维码
Map<String, Object> map = new HashMap<>();
map.put("codeUrl", codeUrl);
map.put("orderNo", orderInfo.getOrderNo());
return map;
}
/**
* 构建器请求参数
*
* @param httpPost 构建请求
* @param description 描述
* @param total 总
* @param orderNo 交易订单号
* @return {@link Map}<{@link String}, {@link Object}>
*/
private void builderRequestParams(HttpPost httpPost, String description, int total, String orderNo) {
Map<String, Object> paramsMap = new HashMap<>(8);
paramsMap.put("appid", wxPayConfig.getAppid());
paramsMap.put("mchid", wxPayConfig.getMchId());
paramsMap.put("description", description);
paramsMap.put("out_trade_no", orderNo);
// 微信回调通知 支付通知 和退款通知都调用这个接口
paramsMap.put("notify_url", wxPayConfig.getNotifyDomain());
Map<String, Object> amountMap = new HashMap<>();
amountMap.put("total", total); // 分
amountMap.put("currency", "CNY");
// 设置金额
paramsMap.put("amount", amountMap);
// 将参数转换成json字符串
String jsonParams = JSONUtil.toJsonStr(paramsMap);
log.info("请求参数 ===> {}" + jsonParams);
StringEntity entity = new StringEntity(jsonParams, "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
}
/**
* 解析响应参数
*/
private <T> T buildBodyParams(CloseableHttpResponse response, Class<T> tClass) throws IOException {
T bodyAsString = null;
if (null != response.getEntity()) {
String json = EntityUtils.toString(response.getEntity());//响应体
bodyAsString = JSONUtil.toBean(json, tClass);
}
int statusCode = response.getStatusLine().getStatusCode();//响应状态码
if (statusCode == 200) { //处理成功
log.info("成功, 返回结果 = " + bodyAsString);
} else if (statusCode == 204) { //处理成功,无返回Body
log.info("成功");
} else if (statusCode == 404) { //处理成功,无返回Body
log.info("没找到订单...");
} else {
log.info("响应:{}, {}", response.getEntity(), response.getStatusLine());
log.info("失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
throw new IOException("request failed");
}
return bodyAsString;
}
上面代码新增了生成订单保存到数据库的过程并且优化了部分代码
启动程序 请求下单接口 /api/wx-pay/native/native/{productId}
{productId} 查看商品表数据的ID
复制返回的微信二维码地址
进入 https://cli.im/url 生成扫描二维码 使用微信扫描

从零玩转系列之微信支付实战PC端接口搭建的更多相关文章
- app微信支付-java服务端接口 支付-查询-退款
个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...
- python - 对接微信支付(PC)和 注意点
注:本文仅提供 pc 端微信扫码支付(模式一)的示例代码. 关于对接过程中遇到的问题总结在本文最下方. 参考: 官方文档, https://blog.csdn.net/lm_is_dc/arti ...
- 微信支付(PC扫码支付和H5公众号支付)
最近在做微信支付,微信支付比较坑,官方居然只有.NET.C#.PHP的demo居然没有java的demo.然后微信支付是不提供测试账号的需要直接用正式的公众号.首先来介绍下微信扫码支付吧,微信扫码有两 ...
- 【原创分享·微信支付】C# MVC 微信支付教程系列之现金红包
微信支付教程系列之现金红包 最近最弄这个微信支付的功能,然后扫码.公众号支付,这些都做了,闲着无聊,就看了看微信支付的其他功能,发现还有一个叫“现金红包”的玩意,想 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付
微信支付教程系列之扫码支付 今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添 ...
- 【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后再通 ...
- C# 微信支付教程系列之扫码支付
微信支付教程系列之扫码支付 今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添加好友的时候 ...
- 《从零玩转JavaWeb+项目实战》-系列课堂录制计划
点击试听课程 前言 很多自学编程的同学经常和我说想学一门语言自己到网上找一些教程看到一半就像背单词背到ambulance一样坚持不下去了....究其原因基本上都是:内容太多,太枯燥,专业术语听不懂,学 ...
- C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...
- 【分享·微信支付】 C# MVC 微信支付教程系列之公众号支付
微信支付教程系列之公众号支付 今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...
随机推荐
- EF Core从TPH迁移到TPT
Intro EF Core支持多种方式处理具有继承关系的表,现在支持TPH.TPC(EF Core 7).TPT,具体的实现方式可以参考官方文档和这篇文章. 大致总结一下不同的方式的区别: TPH:所 ...
- 【Deep Learning】DDPM
DDPM 1. 大致流程 1.1 宏观流程 1.2 训练过程 1.3 推理过程 2. 对比GAN 2.1 GAN流程 2.2 相比GAN优点 训练过程更稳定,损失函数指向性更强(loss数值大小指示训 ...
- 【CTF】日志 2019.7.13 pwn 堆溢出基础知识
十六进制两位表示一个字节 堆溢出 先上堆图: 堆的数据结构 一般情况下,物理相邻的两个空闲 chunk 会被合并为一个 chunk struct malloc_chunk { INTERNAL_SIZ ...
- [软件过程/软件生命周期模型]软件过程的工具链&技术链【待续】
0 宣言:DevOps & RUP统一过程建模 1 项目管理 (需求管理 / 缺陷管理 / ...) 禅道(前身:bugfree) [在线协作] JIRA(项目与事务跟踪工具) 与禅道类同,但 ...
- [Linux]监控外部用户登录及外部主机连接情况
1 外部用户/外部主机 /var/log 在CentOS系统上,用户登录历史存储在以下这些文件中: /var/log/wtmp 用于存储系统连接历史记录被last工具用来记录最后登录的用户的列表 /v ...
- SQL优化(二)-- 慢查询
1.慢查询日志 MYSQL的慢查询日志是Mysql提供的一种日志记录方式,它主要是用来记录mysql执行语句过程中,响应时间超过阀值的语句,这个阀值可以通过long_query_time去指定,比如说 ...
- LNMP搭建静态网页服务器
chattr -i default/.user.ini LNMP搭建使用 1.安装screen,命令或者操作可以一直运行下去 yum install screen 2.获取及安装 LNMP wget ...
- Go/Python 基于gRPC传输图片
python程序作为服务端,Go程序作为客户端,基于gPRC进行通信 客户端 定义proto文件: syntax = "proto3"; option go_package = & ...
- 尝试CentOS8---部署集群(生产环境7.9为好)
一.LVS集群简介 什么是集群 通过网络将很多服务器集中起来,提供同一种服务,在客户端看来就像是只有一个服务器 二.LVS-NAT集群 1.环境准备 启动3台虚拟机,禁用selinux和firewal ...
- 五天学会Deep Learning
五天学完deep learning......是时候来证明chatGPT和new bing的能力了...... DAY1 Sigmoid function Sigmoid 函数是一种常用的激活函数,它 ...