小程序:Uniapp、Uview
复制返回的微信二维码地址
修改WxPayService引入订单服务
/** | |
* 订单服务 | |
*/ | |
private final OrderInfoService orderInfoService; |
进入草料网址二维码生成器生成扫描二维码使用微信扫描
修改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 (CloseableHttp通达信开放接口api,response 通达信开放接口api,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; | |
} | |
Native模式
在com.ybyservice包下创建接口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; | |
} | |
} | |
在此之前已经更新了微信支付开篇、微信支付安全、微信实战基础框架搭建、本次更新为微信支付实战PC端接口搭建,实战篇分为几个章节因为代码量确实有点多哈.
启动程序请求下单接口/api/wx-pay/native/native/1
问题微信添加:BN_Tang备注:微信支付
????接入商品和订单存储数据库
修改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; | |
} | |
} |
复制返回的微信二维码地址
结果可以正常扫码并且支付
第一章从零玩转系列之微信支付开篇第二章从零玩转系列之微信支付安全第三章从零玩转系列之微信支付实战基础框架搭建第四章从零玩转系列之微信支付实战PC端接口搭建
上面代码新增了生成订单保存到数据库的过程并且优化了部分代码
启动程序请求下单接口/api/wx-pay/native/native/{productId}
本次项目使用技术栈
在com.ybycontroller包下创建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)); | |
} | |
} |
文章为作者独立观点,不代表股票交易接口观点