目录
1.准备工作
1.1 数据库表
这里涉及微信支付一共两个表:
订单表
支付记录表
1.2 实体类
数据库对应的实体类:
订单表
@Data @ToString @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName(\"t_order\") @ApiModel(value = \"Order对象\", description = \"订单\") public class Order implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = \"id\", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = \"订单号\") private String orderNo; @ApiModelProperty(value = \"课程id\") private String courseId; @ApiModelProperty(value = \"课程名称\") private String courseTitle; @ApiModelProperty(value = \"课程封面\") private String courseCover; @ApiModelProperty(value = \"讲师名称\") private String teacherName; @ApiModelProperty(value = \"会员id\") private String memberId; @ApiModelProperty(value = \"会员昵称\") private String nickname; @ApiModelProperty(value = \"会员手机\") private String mobile; @ApiModelProperty(value = \"订单金额(分)\") private BigDecimal totalFee; @ApiModelProperty(value = \"支付类型(1:微信 2:支付宝)\") private Integer payType; @ApiModelProperty(value = \"订单状态(0:未支付 1:已支付)\") private Integer status; @ApiModelProperty(value = \"逻辑删除 1(true)已删除, 0(false)未删除\") private Boolean isDeleted; @ApiModelProperty(value = \"创建时间\") @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = \"更新时间\") @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified; }
支付日志表
@Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName(\"t_pay_log\") @ApiModel(value = \"PayLog对象\", description = \"支付日志表\") public class PayLog implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = \"id\", type = IdType.ID_WORKER_STR) private String id; @ApiModelProperty(value = \"订单号\") private String orderNo; @ApiModelProperty(value = \"支付完成时间\") private Date payTime; @ApiModelProperty(value = \"支付金额(分)\") private BigDecimal totalFee; @ApiModelProperty(value = \"交易流水号\") private String transactionId; @ApiModelProperty(value = \"交易状态\") private String tradeState; @ApiModelProperty(value = \"支付类型(1:微信 2:支付宝)\") private Integer payType; @ApiModelProperty(value = \"其他属性\") private String attr; @ApiModelProperty(value = \"逻辑删除 1(true)已删除, 0(false)未删除\") private Boolean isDeleted; @ApiModelProperty(value = \"创建时间\") @TableField(fill = FieldFill.INSERT) private Date gmtCreate; @ApiModelProperty(value = \"更新时间\") @TableField(fill = FieldFill.INSERT_UPDATE) private Date gmtModified; }
1.3 导入依赖
在订单模块service_order导入微信支付需要的依赖:
<dependencies> <dependency> <groupId>com.github.wxpay</groupId> <artifactId>wxpay-sdk</artifactId> <version>0.0.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> </dependency> </dependencies>
1.4 配置文件
在配置文件application.properties配置相关的信息:
# 服务端口 server.port=8007 # 服务名 spring.application.name=service-order # mysql数据库连接 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root #返回json的全局时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 #配置mapper xml文件的路径 mybatis-plus.mapper-locations=classpath:com/atguigu/eduorder/mapper/xml/*.xml #mybatis日志 mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # nacos服务地址 spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 #开启熔断机制 #feign.hystrix.enabled=true # 设置hystrix超时时间,默认1000ms #hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000 #关联的公众号appid wx.pay.app_id=wx74862e0dfc69954 #商户号 wx.pay.partner=155895011 #商户key wx.pay.partnerkey=T6m9iK73b0kn9g5v426MKHQH7X8rKwb #回调地址 wx.pay.notifyurl=http://guli.shop/api/order/weixinPay/weixinNotify #微信提供的固定地址 wx.pay.wxurl=https://api.mch.weixin.qq.com/pay/unifiedorder #微信查询状态地址 wx.pay.queryUrl=https://api.mch.weixin.qq.com/pay/orderquery
1.5 创建读取微信支付相关信息的工具类
创建一个读取微信支付需要的信息的工具类ConstantWxPayUtils:
@Controller public class ConstantWxPayUtils implements InitializingBean { @Value(\"${wx.pay.app_id}\") private String appID; @Value(\"${wx.pay.partner}\") private String partner; @Value(\"${wx.pay.partnerkey}\") private String partnerKey; @Value(\"${wx.pay.notifyurl}\") private String notifyUrl; @Value(\"${wx.pay.wxurl}\") private String wxUrl; @Value(\"${wx.pay.queryUrl}\") private String queryUrl; //定义公共静态常量 public static String WX_PAY_APP_ID; public static String WX_PAY_PARTNER; public static String WX_PAY_PARTNER_KEY; public static String WX_PAY_NOTIFY_URL; public static String WX_PAY_WX_URL; public static String WX_PAY_QUERY_URL; @Override public void afterPropertiesSet() throws Exception { WX_PAY_APP_ID = appID; WX_PAY_PARTNER = partner; WX_PAY_PARTNER_KEY = partnerKey; WX_PAY_NOTIFY_URL = notifyUrl; WX_PAY_WX_URL = wxUrl; WX_PAY_QUERY_URL=queryUrl; } }
1.6 其他工具类
用于随机生成订单号的工具类OrderNoUtil:
public class OrderNoUtil { /** * 获取订单号 * @return */ public static String getOrderNo() { SimpleDateFormat sdf = new SimpleDateFormat(\"yyyyMMddHHmmss\"); String newDate = sdf.format(new Date()); String result = \"\"; Random random = new Random(); for (int i = 0; i < 3; i++) { result += random.nextInt(10); } return newDate + result; } }
HttpClient工具类:
/** * http请求客户端 * * @author xppll * */ public class HttpClient { private String url; private Map<String, String> param; private int statusCode; private String content; private String xmlParam; private boolean isHttps; public boolean isHttps() { return isHttps; } public void setHttps(boolean isHttps) { this.isHttps = isHttps; } public String getXmlParam() { return xmlParam; } public void setXmlParam(String xmlParam) { this.xmlParam = xmlParam; } public HttpClient(String url, Map<String, String> param) { this.url = url; this.param = param; } public HttpClient(String url) { this.url = url; } public void setParameter(Map<String, String> map) { param = map; } public void addParameter(String key, String value) { if (param == null) param = new HashMap<String, String>(); param.put(key, value); } public void post() throws ClientProtocolException, IOException { HttpPost http = new HttpPost(url); setEntity(http); execute(http); } public void put() throws ClientProtocolException, IOException { HttpPut http = new HttpPut(url); setEntity(http); execute(http); } public void get() throws ClientProtocolException, IOException { if (param != null) { StringBuilder url = new StringBuilder(this.url); boolean isFirst = true; for (String key : param.keySet()) { if (isFirst) url.append(\"?\"); else url.append(\"&\"); url.append(key).append(\"=\").append(param.get(key)); } this.url = url.toString(); } HttpGet http = new HttpGet(url); execute(http); } /** * set http post,put param */ private void setEntity(HttpEntityEnclosingRequestBase http) { if (param != null) { List<NameValuePair> nvps = new LinkedList<NameValuePair>(); for (String key : param.keySet()) nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数 http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数 } if (xmlParam != null) { http.setEntity(new StringEntity(xmlParam, Consts.UTF_8)); } } private void execute(HttpUriRequest http) throws ClientProtocolException, IOException { CloseableHttpClient httpClient = null; try { if (isHttps) { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(null, new TrustStrategy() { // 信任所有 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext); httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .build(); } else { httpClient = HttpClients.createDefault(); } CloseableHttpResponse response = httpClient.execute(http); try { if (response != null) { if (response.getStatusLine() != null) statusCode = response.getStatusLine().getStatusCode(); HttpEntity entity = response.getEntity(); // 响应内容 content = EntityUtils.toString(entity, Consts.UTF_8); } } finally { response.close(); } } catch (Exception e) { e.printStackTrace(); } finally { httpClient.close(); } } public int getStatusCode() { return statusCode; } public String getContent() throws ParseException, IOException { return content; } }
2.生成订单
这里一共涉及service_order订单模块、service_ucenter用户模块、service-edu课程模块。
service_order使用Fegin远程调用其他模块的方法。
详细的Fegin的使用可以参考:SpringCloud-Feign远程调用
2.1 远程调用用户模块和课程模块
在service_order订单模块创建:
@Component @FeignClient(\"service-ucenter\") //调用的服务名称 public interface UcenterClient { //根据用户id获取用户信息,用于生成订单使用 @PostMapping(\"/educenter/member/getUserInfoOrder/{id}\") public UcenterMemberOrder getUserInfoOrder(@PathVariable(\"id\") String id); }
@Component @FeignClient(\"service-edu\") //调用的服务名称 public interface CourseClient { //根据课程id查询课程信息 @PostMapping(\"/eduservice/coursefront/getCourseInfoOrder/{id}\") public CourseWebOrder getCourseInfoOrder(@PathVariable(\"id\") String id); }
2.2 远程调用方法的实现
在service-edu课程模块实现根据课程id查询课程信息的getCourseInfoOrder方法
controller层:
/** * 根据课程id查询课程信息 * @param id 客场id * @return CourseWebOrder */ @PostMapping(\"getCourseInfoOrder/{id}\") public CourseWebOrder getCourseInfoOrder(@PathVariable(\"id\") String id) { CourseWebVo courseInfo = courseService.getBaseCourseInfo(id); CourseWebOrder courseWebOrder = new CourseWebOrder(); BeanUtils.copyProperties(courseInfo, courseWebOrder); return courseWebOrder; }
service层:
/** * 根据课程id,编写sql语句查询课程信息 * @param courseId 课程id * @return CourseWebVo */ @Override public CourseWebVo getBaseCourseInfo(String courseId) { return baseMapper.getBaseCourseInfo(courseId); }
mapper层:
<!--根据课程id查询课程基本信息--> <select id=\"getBaseCourseInfo\" resultType=\"com.atguigu.eduservice.entity.frontvo.CourseWebVo\"> SELECT ec.id, ec.`title`, ec.`price`, ec.lesson_num as lessonNum, ec.cover, ec.buy_count as buyCount, ec.view_count as viewCount, ecd.description, et.id teacherId, et.`name` AS teacherName, et.intro, et.avatar, es1.id as subjectLevelOneId, es1.`title` AS subjectLevelOne, es2.id as subjectLevelTwoId, es2.`title` AS subjectLevelTwo FROM edu_course ec LEFT JOIN edu_course_description ecd ON ec.id = ecd.id LEFT JOIN edu_teacher et ON ec.`teacher_id` = et.`id` LEFT JOIN edu_subject es1 ON ec.`subject_parent_id` = es1.`id` LEFT JOIN edu_subject es2 ON ec.`subject_id` = es2.`id` WHERE ec.id = #{courseId} </select>
在service_ucenter用户模块实现根据用户id获取用户信息的getUserInfoOrder方法
controller层:
/** * 根据用户id获取用户信息,用于生成订单使用 * * @param id 用户id * @return UcenterMemberOrder */ @PostMapping(\"getUserInfoOrder/{id}\") public UcenterMemberOrder getUserInfoOrder(@PathVariable(\"id\") String id) { UcenterMember member = memberService.getById(id); UcenterMemberOrder memberOrder = new UcenterMemberOrder(); BeanUtils.copyProperties(member, memberOrder); return memberOrder; }
2.3 根据课程id和用户id生成订单
controller层:
@CrossOrigin @RestController @RequestMapping(\"/eduorder/order\") public class OrderController { @Autowired private OrderService orderService; /** * 生成订单的方法 * * @param courseId 课程id * @param request 用于获取用户id * @return 返回订单号 */ @PostMapping(\"createOrder/{courseId}\") public R saveOrder(@PathVariable(\"courseId\") String courseId, HttpServletRequest request) { //通过JWT工具类获取用户id //创建订单,返回订单号 String orderNo = orderService.createOrderById(courseId, JwtUtils.getMemberIdByJwtToken(request)); return R.ok().data(\"orderId\", orderNo); } }
service层:
/** * 根据courseId和userId生成订单 * * @param courseId 课程id * @param userId 用户id * @return 返回订单号 */ @Override public String createOrderById(String courseId, String userId) { //通过远程调佣根据用户id获取用户信息 UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(userId); //通过远程调佣根据课程id获取课程信息 CourseWebOrder courseInfoOrder = courseClient.getCourseInfoOrder(courseId); Order order = new Order(); //订单号 order.setOrderNo(OrderNoUtil.getOrderNo()); order.setCourseId(courseId); order.setCourseTitle(courseInfoOrder.getTitle()); order.setCourseCover(courseInfoOrder.getCover()); order.setTeacherName(courseInfoOrder.getTeacherName()); order.setTotalFee(courseInfoOrder.getPrice()); order.setMemberId(userId); order.setMobile(userInfoOrder.getMobile()); order.setNickname(userInfoOrder.getNickname()); //支付状态 未支付:0 已支付:1 order.setStatus(0); //支付类型 微信:1 支付宝:2 order.setPayType(1); //保存到数据库 baseMapper.insert(order); //返回订单号 return order.getOrderNo(); }
3.查询订单信息
3.1 controller层
在OrderController里创建getOrderInfo用于生成订单:
/** * 根据订单id查询订单信息 * @param orderId 订单id * @return 返回订单信息 */ @GetMapping(\"getOrderInfo/{orderId}\") public R getOrderInfo(@PathVariable(\"orderId\") String orderId) { Order order=orderService.getOrderByOrderId(orderId); return R.ok().data(\"item\", order); }
3.2 service层
/** * 根据订单id查询订单信息 * * @param orderId 订单id * @return 返回订单信息 */ @Override public Order getOrderByOrderId(String orderId) { LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Order::getOrderNo, orderId); return baseMapper.selectOne(queryWrapper); }
4.生成微信支付的二维码
4.1 controller层
在PayLogController里创建createNative用于生成支付二维码:
@CrossOrigin @RestController @RequestMapping(\"/eduorder/paylog\") public class PayLogController { @Autowired private PayLogService payLogService; /** * 根据订单号生成微信支付二维码 * @param orderNo 订单号 * @return R */ @GetMapping(\"createNative/{orderNo}\") public R createNative(@PathVariable(\"orderNo\") String orderNo){ //返回信息,包含二维码地址,还有其他信息 Map map=payLogService.createNative(orderNo); return R.ok().data(map); } }
4.2 service层
- 生成微信支付二维码大概分为这几步:
- 根据订单号查询订单信息
- 使用map设置生成二维码需要的参数
- 发送httpclient请求,传递xml格式的参数,传入微信支付提供的固定地址
- 得到发送请求返回的结果
- 最终返回封装数据
/** * 根据订单号生成微信支付二维码 * @param orderNo 订单号 * @return map */ @Override public Map createNative(String orderNo) { try { //1.根据订单号查询订单信息 Order order = orderService.getOrderByOrderId(orderNo); //2.使用map设置生成二维码需要的参数 Map m = new HashMap(); //关联的公众号appid m.put(\"appid\", ConstantWxPayUtils.WX_PAY_APP_ID); //商户号 m.put(\"mch_id\", ConstantWxPayUtils.WX_PAY_PARTNER); //随机字符串 m.put(\"nonce_str\", WXPayUtil.generateNonceStr()); //课程标题 m.put(\"body\", order.getCourseTitle()); //订单号 m.put(\"out_trade_no\", orderNo); //价格 m.put(\"total_fee\", order.getTotalFee().multiply(new BigDecimal(\"100\")).longValue() + \"\"); //支付的ip地址 m.put(\"spbill_create_ip\", \"127.0.0.1\"); m.put(\"notify_url\", ConstantWxPayUtils.WX_PAY_NOTIFY_URL); m.put(\"trade_type\", \"NATIVE\"); //3.发送httpclient请求,传递参数xml格式,传入微信支付提供的固定地址 HttpClient client = new HttpClient(ConstantWxPayUtils.WX_PAY_WX_URL); //设置xml格式的参数,需要传入二维码参数m和商户key client.setXmlParam(WXPayUtil.generateSignedXml(m, ConstantWxPayUtils.WX_PAY_PARTNER_KEY)); //默认不支持https,设置为true支持 client.setHttps(true); //执行请求发送 client.post(); //4.得到发送请求返回的结果 //返回的内容是xml格式 String xml = client.getContent(); //把xml格式转换为map集合 Map<String, String> resultMap = WXPayUtil.xmlToMap(xml); //5.最终返回封装数据 Map map = new HashMap(); //订单号 map.put(\"out_trade_no\", orderNo); //课程id map.put(\"course_id\", order.getCourseId()); //价格 map.put(\"total_fee\", order.getTotalFee()); //返回二维码操作状态码 map.put(\"result_code\", resultMap.get(\"result_code\")); //二维码地址 map.put(\"code_url\", resultMap.get(\"code_url\")); return map; } catch (Exception e) { throw new GuliException(20001, \"生成微信支付二维码失败\"); } }
5.查询订单支付状态
5.1 controller层
在PayLogController里创建queryPayStatus用于获取支付状态:
/** * 获取支付状态 * @param orderNo 订单号 * @return R */ @GetMapping(\"queryPayStatus/{orderNo}\") public R queryPayStatus(@PathVariable(\"orderNo\") String orderNo){ Map<String, String> map=payLogService.queryPayStatus(orderNo); if(map==null){ return R.error().message(\"支付出错!\"); } //如果map不为空,通过map获取订单状态 if(map.get(\"trade_state\").equals(\"SUCCESS\")){ //添加记录到支付表,更新订单表订单状态 payLogService.updateOrdersStatus(map); return R.ok().message(\"支付成功!\"); } return R.ok().code(25000).message(\"正在支付中...\"); }
5.2 service层
根据订单号查询订单支付状态大概分为一下几步:
- 封装参数
- 发送httpclient
- 得到请求返回的内容
/** * 根据订单号查询订单支付状态 * @param orderNo * @return */ @Override public Map<String, String> queryPayStatus(String orderNo) { try { //1.封装参数 Map m=new HashMap(); //关联的公众号appid m.put(\"appid\",ConstantWxPayUtils.WX_PAY_APP_ID); //商户号 m.put(\"mch_id\",ConstantWxPayUtils.WX_PAY_PARTNER); //订单号 m.put(\"out_trade_no\",orderNo); //随机字符串 m.put(\"nonce_str\",WXPayUtil.generateNonceStr()); //2.发送httpclient HttpClient client = new HttpClient(ConstantWxPayUtils.WX_PAY_QUERY_URL); client.setXmlParam(WXPayUtil.generateSignedXml(m,ConstantWxPayUtils.WX_PAY_PARTNER_KEY)); client.setHttps(true); client.post(); //3.得到请求返回的内容 String xml = client.getContent(); Map<String, String> resultMap=WXPayUtil.xmlToMap(xml); return resultMap; } catch (Exception e) { e.printStackTrace(); throw new GuliException(20001,\"查询订单支付状态失败\"); } }
如果支付成功,需要添加记录到支付表,更新订单表订单状态:
/**
* 向支付表添加记录,更新订单表订单状态
* @param map
*/
@Override
public void updateOrdersStatus(Map<String, String> map) {
//从map获取订单号
String orderNo = map.get(\"out_trade_no\");
Order order = orderService.getOrderByOrderId(orderNo);
//更新订单表t_order的订单状态status
if(order.getStatus().intValue()==1){
return;
}
order.setStatus(1);
orderService.updateById(order);
//向支付表 t_pag_log 添加记录
PayLog payLog=new PayLog();
payLog.setOrderNo(orderNo);
payLog.setPayTime(new Date());
//支付类型
payLog.setPayType(1);
//支付金额
payLog.setTotalFee(order.getTotalFee());
//支付状态
payLog.setTradeState(map.get(\"trade_state\"));
//交易流水号
payLog.setTransactionId(map.get(\"transaction_id\"));
//其他属性,转为json字符串
payLog.setAttr(JSONObject.toJSONString(map));
baseMapper.insert(payLog);
}
以上就是SpringBoot实现整合微信支付方法详解的详细内容,更多关于SpringBoot整合微信支付的资料请关注其它相关文章!
做猪小侠源码的代理,提供一站式服务
如果你不懂得搭建网站或者服务器,小程序,源码之类的怎么办? 第一通过本站学习各种互联网的技术 第二就是联系客服,我帮帮你搭建(当然要收取部分的费用) 第三成为我们的代理,我们提供整套的服务。