用于个人记录,以防后续用到,也希望能帮助到更多的小伙伴
1、商户生成订单
2、商户调用微信下单接口,获取预交易的链接
3、商户将链接生成二维码图片,展示给用户;
4、支付结果通知:
微信异步通知商户支付结果,商户告知微信支付接收情况
商户如果没有收到通知,可以调用接口,查询支付状态
5、如果支付成功,发货,修改订单状态
微信支付官方文档:https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F
用到的是:Native支付, 就是扫码支付
参数以及返回值 具体去官网查看
官网下载 : wx-sdk 打成jar包 引入maven
命令:mvn source:jar install -Dmaven.test.skip=true 或者 用maven插件
1.application中加入:
ly:
pay:
wx:
appID: wx*********
mchID: ********
key: *******
payType: NATIVE
2.配置类:
package com.leyou.order.config;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfigImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class PayConfiguration {
@Bean
@ConfigurationProperties(prefix = "ly.pay.wx")
public WXPayConfigImpl payConfig(){
return new WXPayConfigImpl();
}
/**
* 注册WXPay对象
* @param payConfig 支付相关配置
* @return WXPay对象
* @throws Exception 连结WX失败时用到
*/
@Bean
public WXPay wxPay(WXPayConfigImpl payConfig) throws Exception {
return new WXPay(payConfig);
}
}
================以下开始生成支付二维码并且给前台返回=============================
3.订单生成以后 传入一个 orderId (订单ID)到后台 , 进入支付界面生成二维码
payHelper 工具类:
package com.leyou.order.utils;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfigImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 支付的工具类
*/
@Slf4j
@Component //加入spring 容器
public class PayHelper {
@Autowired
private WXPay wxPay;
@Autowired
private WXPayConfigImpl wxPayConfig;
/**
* 创建微信支付订单
* @param body
* @param orderId
* @param totalFee
* @return
*/
public String createOrder(String body,Long orderId,Long totalFee){
Map map = new HashMap<>();
// 商品描述
map.put("body",body);
// 本系统的订单号
map.put("out_trade_no",orderId.toString());
// 订单的支付金额
map.put("total_fee",totalFee.toString());
// 终端id
map.put("spbill_create_ip","IP地址例127.0.0.1需要修改");
// 通知地址
map.put("notify_url","网关getway地址/api/pay/wxnotify");
// 交易类型
map.put("trade_type","NATIVE");
try{
Map result = wxPay.unifiedOrder(map);
log.info("【微信支付】result:{}",result);
// 校验业务状态
checkResultCode(result);
// codeurl用来生成二维码
String codeUrl = result.get("code_url");
log.info("【微信支付】codeUrl:{}",codeUrl);
return codeUrl;
}catch(Exception e){
e.printStackTrace();
log.error("【微信支付】微信支付业务失败");
throw new RuntimeException("微信支付失败");
}
}
public void checkResultCode(Map result) {
// 检查业务状态
String resultCode = result.get("result_code");
if ("FAIL".equals(resultCode)) {
log.error("【微信支付】微信支付业务失败,错误码:{},原因:{}", result.get("err_code"), result.get("err_code_des"));
throw new RuntimeException("【微信支付】微信支付业务失败");
}
}
}
controller:
@GetMapping("/url/{id}")
public ResponseEntity getCodeUrl(@PathVariable(name = "id")Long orderId){
return ResponseEntity.ok(orderService.getCodeUrl(orderId));
}
Service :
@Autowired
private PayHelper payHelper;
@Autowired
private StringRedisTemplate redisTemplate;
private String PRE_FIX = "ly:order:pay:id:";
/**
* 获取连接
* @param orderId
* @return
*/
public String getCodeUrl(Long orderId) {
String redisKey = PRE_FIX + orderId;
String codeUrl = redisTemplate.opsForValue().get(redisKey);
if(!StringUtils.isEmpty(codeUrl)){
return codeUrl;
}
TbOrder tbOrder = tbOrderService.getById(orderId);
// 实付金额,为了测试改成1分 ,应该写为 实付金额从数据库中查取
Long actualFee = 1L;//tbOrder.getActualFee();
String body = "商城支付";
// 用户微信支付的url
codeUrl = payHelper.createOrder(body, orderId, actualFee);
// codeurl有2小时的有效期,需要放入redis ,并设置过期时长
redisTemplate.opsForValue().set(redisKey,codeUrl,2, TimeUnit.HOURS);
//给用户返回 codeUrl 就是二维码
return codeUrl;
}
以上生成二维码 ==========================================================================
用户扫完二维码以后会有一个回调地址进入回调方法中
=====================
以下有一个注意事项:
@RestController
@PostMapping(value = "/wxnotify",produces = "application/xml")
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.9.6
1.回调到后台的数据是 xml 格式 所以我们这里要把xml 格式转化为对象map结构,需要配置 以上就是配置 需要引入坐标
2.返回也是 xml 这里用map 会自动转化成xml格式返回到前台
======================
以下回调以后的方法:
package com.leyou.order.controller;
import com.leyou.order.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/pay")
public class PayController {
@Autowired
private PayService payService;
/**
* 接收 微信支付发来的回调
* produces 说明request中的参数的媒体类型
* 现在 媒体类型是 xml
* @param map
*/
@PostMapping(value = "/wxnotify",produces = "application/xml")
public Map wxPayNotify(@RequestBody Map map){
payService.wxPayNotify(map);
//
//
//
//
//
Map returnMap = new HashMap<>();
returnMap.put("return_code","SUCCESS");
returnMap.put("return_msg","OK");
return returnMap;
}
}
回调以后的Service:
package com.leyou.order.service;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConstants;
import com.leyou.common.Exceptions.LyException;
import com.leyou.common.enums.ExceptionEnums;
import com.leyou.order.entity.TbOrder;
import com.leyou.order.enums.OrderStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
@Slf4j
@Service
public class PayService {
@Autowired
private TbOrderService tbOrderService;
@Autowired
private WXPay wxPay;
/**
* 接收微信支付的回调处理方法
* 从参数中获取 orderid,和 用户支付金额
* 和自己数据库的订单数据进行比对
* 如果正确就直接返回
* 如果不对,直接抛出异常
* @param map
*/
public void wxPayNotify(Map map) {
if(map.get("result_code") == null || !map.get("result_code").equals(WXPayConstants.SUCCESS)){
throw new LyException(ExceptionEnums.INVALID_NOTIFY_PARAM);
}
try {
boolean bWeiChat = wxPay.isPayResultNotifySignatureValid(map);
} catch (Exception e) {
throw new LyException(ExceptionEnums.INVALID_NOTIFY_PARAM);
}
// 本系统的订单号
String orderId = map.get("out_trade_no");
// 用户支付的总金额
String userPayTotal = map.get("total_fee");
Long userPayTotalLong = Long.valueOf(userPayTotal);
// 查询数据库中的order信息
Long orderIdLong = Long.valueOf(orderId);
TbOrder tbOrder = tbOrderService.getById(orderIdLong);
// 订单的状态
Integer status = tbOrder.getStatus();
// 订单的实付金额
Long actualFee = tbOrder.getActualFee();
// 订单当前已经不是 未支付状态了
if(status.intValue() != OrderStatusEnum.INIT.value().intValue()){
return ;
}
// 用户的支付金额不一致,这里为了测试 把代码注掉,上线加上判断金额是否一致
// if(actualFee.longValue() != userPayTotalLong.longValue()){
//
// log.error("【微信支付】用户的付款金额不对!!");
// throw new LyException(ExceptionEnums.INVALID_NOTIFY_PARAM);
// }
// 支付成功后,修改状态,修改为 已支付,待发货
tbOrderService.updateOrderStatusForPaySuccess(orderIdLong);
}
}
修改订单状态:已经支付成功,将未支付改成已支付状态
package com.leyou.order.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.order.entity.TbOrder;
import com.leyou.order.enums.OrderStatusEnum;
import com.leyou.order.mapper.TbOrderMapper;
import com.leyou.order.service.TbOrderService;
import org.springframework.stereotype.Service;
/**
*
* 服务实现类
*
*
* @author HM
* @since 2019-10-15
*/
@Service
public class TbOrderServiceImpl extends ServiceImpl implements TbOrderService {
/**
* 把订单的状态改为 已支付
* @param orderId
* @return
*/
@Override
public void updateOrderStatusForPaySuccess(Long orderId) {
// update tb_order set status =2 where order_id=? and status=1
UpdateWrapper updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().eq(TbOrder::getOrderId,orderId);
updateWrapper.lambda().eq(TbOrder::getStatus, OrderStatusEnum.INIT.value());
updateWrapper.lambda().set(TbOrder::getStatus,OrderStatusEnum.PAY_UP.value());
this.update(updateWrapper);
}
}
====================================
注意事项:
update tb_order set status =2 where order_id=? and status=1
幂等
不写后面 and status=1 的话,状态会出问题,
本身已经是发货,但是执行了错误的sql 会把状态改回到2 这样会 重复发货
====================================