一、前言

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端接口搭建的更多相关文章

  1. app微信支付-java服务端接口 支付-查询-退款

    个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...

  2. python - 对接微信支付(PC)和 注意点

    注:本文仅提供 pc 端微信扫码支付(模式一)的示例代码. 关于对接过程中遇到的问题总结在本文最下方. 参考: 官方文档,    https://blog.csdn.net/lm_is_dc/arti ...

  3. 微信支付(PC扫码支付和H5公众号支付)

    最近在做微信支付,微信支付比较坑,官方居然只有.NET.C#.PHP的demo居然没有java的demo.然后微信支付是不提供测试账号的需要直接用正式的公众号.首先来介绍下微信扫码支付吧,微信扫码有两 ...

  4. 【原创分享·微信支付】C# MVC 微信支付教程系列之现金红包

            微信支付教程系列之现金红包           最近最弄这个微信支付的功能,然后扫码.公众号支付,这些都做了,闲着无聊,就看了看微信支付的其他功能,发现还有一个叫“现金红包”的玩意,想 ...

  5. 【原创分享·微信支付】 C# MVC 微信支付教程系列之扫码支付

    微信支付教程系列之扫码支付                  今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添 ...

  6. 【原创分享·微信支付】 C# MVC 微信支付教程系列之公众号支付

    微信支付教程系列之公众号支付         今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后再通 ...

  7. C# 微信支付教程系列之扫码支付

    微信支付教程系列之扫码支付            今天,我们来一起探讨一下这个微信扫码支付.何为扫码支付呢?这里面,扫的码就是二维码了,就是我们经常扫一扫的那种二维码图片,例如,我们自己添加好友的时候 ...

  8. 《从零玩转JavaWeb+项目实战》-系列课堂录制计划

    点击试听课程 前言 很多自学编程的同学经常和我说想学一门语言自己到网上找一些教程看到一半就像背单词背到ambulance一样坚持不下去了....究其原因基本上都是:内容太多,太枯燥,专业术语听不懂,学 ...

  9. C# MVC 微信支付教程系列之公众号支付

    微信支付教程系列之公众号支付           今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...

  10. 【分享·微信支付】 C# MVC 微信支付教程系列之公众号支付

    微信支付教程系列之公众号支付           今天,我们接着讲微信支付的系列教程,前面,我们讲了这个微信红包和扫码支付.现在,我们讲讲这个公众号支付.公众号支付的应用环境常见的用户通过公众号,然后 ...

随机推荐

  1. 通过Navicat导入SQLServer的MDF文件和LDF文件

    新建查询运行: EXEC  sp_attach_db  @dbname  =  '你的数据库名',      @filename1  =  'mdf文件路径(包缀名)',      @filename ...

  2. 微前端框架single-spa子应用加载解析

    作者:京东物流 宁冲 1 前言 什么是微前端? 微前端是指存在于浏览器中的微服务. 本文主要通过对微前端框架single-spa的基座应用加载子应用的single-spa-vue函数库进行分析,通过代 ...

  3. 一条SQL语句在MySQL中如何执行

    一条SQL语句在MySQL中如何执行 本篇文章会分析一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的. 在分析之前我会 ...

  4. 长达 1.7 万字的 explain 关键字指南!

    当你的数据里只有几千几万,那么 SQL 优化并不会发挥太大价值,但当你的数据里去到了几百上千万,SQL 优化的价值就体现出来了!因此稍微有些经验的同学都知道,怎么让 MySQL 查询语句又快又好是一件 ...

  5. MySQL 8.0:无锁可扩展的 WAL 设计

    这篇文章整理自MySQL官方文档,介绍了8.0在预写式日志上实现上的修改,观点总结如下: 在8.0以前,为了保证flush list的顺序,redo log buffer写入过程需要加锁,无法实现并行 ...

  6. [架构]辨析: 高可用 | 集群 | 主从 | 负载均衡 | 反向代理 | 中间件 | 微服务 | 容器 | 云原生 | DevOps | ...

    词汇集 灾备 冷备份 双机热备份 异地容灾备份 云备份 灾难演练 磁盘阵列(RAID) 故障切换 心跳监测 高可用 集群 主从复制(Master-Slave) 多集群横向扩容(master-clust ...

  7. Android ViewGroup的事件分发机制-源码分析

    为了更好的理解ViewGroup的事件分发机制,我们在自定义一个MyLinerLayout. public class MyLinearLayout extends LinearLayout { pr ...

  8. 使用laravel开发微信公众的一个大坑,适合新手学习的laravel接入微信接口

    最近使用laravel做微信公众号二次开发,发现网上能够参考的资料基本上很少,很多地方都讲的不够详细,致使许多新手采坑无数,所以这篇文章讲一下如何使用laravel接入微信接口,实现微信公众号二次开发 ...

  9. TEC-6计算机组成原理实验(简图)

    TEC-6计算机组成原理实验

  10. c/c++快乐算法第三天

    c/c++感受算法快乐(3) 开始时间2023-04-16 22:21:10 结束时间2023-04-17 00:09:34 前言:很好,这周就要结束了,大家都回学校了么,嘻嘻.回顾一下昨天的算法题, ...