所有代码发布在 https://github.com/hades0525/leyou
订单微服务
- 查询订单(提交订单后展示订单)
- OrderController
/**
*根据订单id查询订单
*@param id
*@return
*/
@GetMapping("{id}")
publicResponseEntity<Order>queryOrderById(@PathVariable("id")Longid){
returnResponseEntity.ok(orderService.queryOrderById(id));
}
- OrderService
/**
*根据订单id查询订单
*@paramid
*@return
*/
publicOrderqueryOrderById(Longid){
//查询订单
Orderorder=orderMapper.selectByPrimaryKey(id);
if(order==null){
thrownewLyException(ExceptionEnum.ORDER_NOT_FOUND);
}
//查询订单详情
OrderDetaildetail=newOrderDetail();
detail.setOrderId(id);
List<OrderDetail>details=detailMapper.select(detail);
if(CollectionUtils.isEmpty(details)){
thrownewLyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);
}
order.setOrderDetails(details);
//查询订单状态
OrderStatusorderStatus=statusMapper.selectByPrimaryKey(id);
if(orderStatus==null){
thrownewLyException(ExceptionEnum.ORDER_STATUS_NOT_FOUND);
}
order.setOrderStatus(orderStatus);
returnorder;
}
微信支付
- 微信支付需要的数据,写到配置文件中
- applicatoin.yml
ly:
pay:
appID: wx8397f8696b538317
mchID: 1472426802
key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
httpConnectTimeoutMs: 1000
httpReadTimeoutMs: 8000
notifyUrl: http://ae4cja.natappfree.cc/notify/wxpay
- 加载配置到WXpay
- PayConfig
@Data
publicclassPayConfigimplementsWXPayConfig{
/**
*公众账号ID
*/
privateStringappID;
/**
*商户号
*/
privateStringmchID;
/**
*生成签名的密钥
*/
privateStringkey;
/**
*连接超时时间
*/
privateinthttpConnectTimeoutMs;
/**
*读取超时时间
*/
privateinthttpReadTimeoutMs;
/**
*读取超时时间
*/
privateStringnotifyUrl;
@Override
publicInputStreamgetCertStream(){
return null;
}
}
- WXPayConfiguration
@Configuration
public class WXPayConfiguration{
@Bean
@ConfigurationProperties(prefix="ly.pay")
public PayConfig payConfig(){
return new PayConfig();
}
@Bean
public WXPay wxPay(PayConfigpayConfig){
return new WXPay(payConfig,WXPayConstants.SignType.HMACSHA256);
}
}
- 获取支付的url,生成二维码
- OrderController
/**
*获取支付url
*@paramorderId
*@return
*/
@GetMapping("url/{id}")
publicResponseEntity<String>createPayUrl(@PathVariable("id")LongorderId){
returnResponseEntity.ok(orderService.createPayUrl(orderId));
}
- OrderService
/**
*获取支付url
*@paramorderId
*@return
*/
public String createPayUrl(Long orderId){
//查询订单
Order order=queryOrderById(orderId);
//判断订单状态
Integer status=order.getOrderStatus().getStatus();
if(status!=OrderStatusEnum.UN_PAY.value()){
thrownewLyException(ExceptionEnum.ORDER_STATUS_ERROR);
}
//支付金额
LongactualPay=order.getActualPay();
//商品描述
OrderDetaildetail=order.getOrderDetails().get(0);
Stringdesc=detail.getTitle();
//returnpayHelper.createOrder(orderId,actualPay,desc);
return"weixin://wxpay/bizpayurl?pr=IEukvBd";
}
- PayHelper.createOrder 微信创建订单
@Autowired
privateWXPaywxPay;
@Autowired
privatePayConfigconfig;
@Autowired
privateOrderMapperorderMapper;
@Autowired
private OrderStatusMapper statusMapper;
public String createOrder(LongorderId,LongtotalPay,Stringdesc){
try{
Map<String,String>data=newHashMap<>();
//商品描述
data.put("body",desc);
//订单号
data.put("out_trade_no",orderId.toString());
//金额,单位是分
data.put("total_fee",totalPay.toString());
//调用微信支付的终端IP(estore商城的IP)
data.put("spbill_create_ip","127.0.0.1");
//回调地址
data.put("notify_url",config.getNotifyUrl());
//交易类型为扫码支付
data.put("trade_type","NATIVE");
Map<String,String>result=this.wxPay.unifiedOrder(data);
//判断标识
isSuccess(result);
//下单成功,获取url
Stringurl=result.get("code_url");
returnurl;
}catch(Exceptione){
log.error("[微信下单创建订单]创建预交易订单异常",e);
returnnull;
}
}
- PayHelper.isSuccess 判断是否创建成功
/**
*判断标识
*@paramresult
*/
public void isSuccess(Map<String,String>result){
//判断通信标识
String returnCode=result.get("return_code");
if(FAIL.equals(returnCode)){
//通信失败
log.error("[微信下单通信标识]微信下单通信失败,失败原因:{}",result.get("return_msg"));
throw new LyException(ExceptionEnum.WX_PAY_ORDER_FALI);
}
//判断业务标识
String resultCode=result.get("result_code");
if(FAIL.equals(resultCode)){
//通信失败
log.error("[微信下单业务标识]微信下单通信失败,错误码:{},错误原因:{}",
result.get("err_code"),result.get("err_code_des"));
throw new LyException(ExceptionEnum.WX_PAY_ORDER_FALI);
}
}
- 生成二维码 使用qrcode.min.js
// 生成付款链接
ly.http.get("/order-service/order/url/" + id).then(resp => {
new QRCode(document.getElementById("qrImage"), {
text: resp.data,
width: 250,
height: 250,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
})
- 支付成功并通知
- 使用内网穿透,让外网访问本地的应用
§ 使用NATAPP生成ip - 微信支付结果通知的url是post请求并且参数是xml格式的
§ 要让参数是xml格式。
§ springMVC实现序列化是通过消息转化器
§ 要让消息转化器可以序列化jason或者xml需要引入相关的依赖
§ 之前已经在ly-common中已经引入了jakson的类,现在需要引入xml的类
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.8</version>
</dependency>
- 接受回调的NotifyController controller的路径对应配置文件中的
notifyUrl: http://ae4cja.natappfree.cc/notify/wxpay 前面为natapp生成的域名
@RestController
@RequestMapping("notify")
@Slf4j
publicclassNotifyController{
@Autowired
privateOrderServiceorderService;
@PostMapping(value="wxpay",produces="application/xml")
publicMap<String,String>hello(@RequestBodyMap<String,String>result){
//处理回调
orderService.handleNotify(result);
log.info("[支付回调]接受微信支付回调,结果{}",result);
//返回成功
Map<String,String>msg=newHashMap<>();
msg.put("return_code","SUCCESS");
msg.put("return_msg","OK");
return msg;
}
}
- OrderService.handleNotify 处理回调参数
/**
*支付结果通知
*@paramresult
*/
publicvoidhandleNotify(Map<String,String>result){
//1数据校验
payHelper.isSuccess(result);
//2校验签名
payHelper.isValidSign(result);
//3校验金额
StringtotalFeeStr=result.get("total_fee");
//订单号
StringtradeNo=result.get("out_trade_no");
if(StringUtils.isBlank(totalFeeStr)||StringUtils.isBlank(tradeNo)){
thrownewLyException(ExceptionEnum.INVALID_ORDER_PARAM);
}
//3.1获取结果中的金额
longtotalFee=Long.valueOf(totalFeeStr);
//获取订单号
LongorderId=Long.valueOf(tradeNo);
//4查询订单
Orderorder=orderMapper.selectByPrimaryKey(orderId);
if(totalFee!=order.getActualPay()){
//金额不符
thrownewLyException(ExceptionEnum.INVALID_ORDER_PARAM);
}
//5修改订单状态
OrderStatusstatus=newOrderStatus();
status.setStatus(OrderStatusEnum.PAYED.value());
status.setOrderId(orderId);
status.setPaymentTime(newDate());
intcount=statusMapper.updateByPrimaryKeySelective(status);
if(count!=1){
thrownewLyException(ExceptionEnum.UPDATE_ORDERSTATUS_ERROR);
}
log.info("[订单回调]订单支付成功!订单编号:{}",orderId);
}
- payhelper.isValidSign 校验签名
/**
*校验签名
*@paramdata
*/
publicvoidisValidSign(Map<String,String>data){
//重新生成签名
try{
Stringsign1=WXPayUtil.generateSignature(data,this.config.getKey(),WXPayConstants.SignType.HMACSHA256);
Stringsign2=WXPayUtil.generateSignature(data,this.config.getKey(),WXPayConstants.SignType.MD5);
//和传过来的签名进行比较
Stringsign=data.get("sign");
if(!StringUtils.equals(sign,sign1)&&!StringUtils.equals(sign,sign2)){
//签名有误
thrownewLyException(ExceptionEnum.INVALID_SIGN_ERROR);
}
}catch(Exceptione){
thrownewLyException(ExceptionEnum.INVALID_SIGN_ERROR);
}
}
- 前端查询订单状态,跳转页面
- 前端 定时器,隔一段时间查一次支付状态
//开启定时任务,查询付款状态
consttaskId=setInterval(()=>{
ly.http.get("/order-service/order/state/"+id)
.then(resp=>{
leti=resp.data;
if(i===1){
//付款成功
clearInterval(taskId);
//跳转到付款成功页
location.href="/paysuccess.html?orderId="+id;
}elseif(i===2){
//付款失败
clearInterval(taskId);
//跳转到付款失败页
location.href="/payfail.html";
}
}).catch((e)=>{
alert("支付状态查询失败,请刷新页面重试。");
clearInterval(taskId);
})
},3000);
- 定义一个枚举PayState,表示支付状态
public enum PayState{
/**
*未支付0
*支付成功1
*支付失败2
*/
NOT_PAY(0),SUCCESS(1),FAIL(2);
PayState(intvalue){
this.value=value;
}
int value;
public int getValue(){
return value;
}
}
- OrderController
/**
*查询订单状态
*@paramorderId
*@return
*/
@GetMapping("state/{id}")
public ResponseEntity<Integer> queryOrderStatus(@PathVariable("id")Long orderId){
returnResponseEntity.ok(orderService.queryOrderStatus(orderId).getValue());
}
• OrderService
/**
*查询订单状态
*@paramorderId
*@return
*/
publicPayStatequeryOrderStatus(LongorderId){
//查询订单状态
OrderStatusorderStatus=statusMapper.selectByPrimaryKey(orderId);
Integerstatus=orderStatus.getStatus();
//判断是否支付
if(status!=OrderStatusEnum.UN_PAY.value()){
//如果已支付,就是真的支付了
returnPayState.SUCCESS;
}
//如果未支付,需要到微信查询支付状态
returnpayHelper.queryPayState(orderId);
}
- PayHelper.queryPayState 可能已支付,但回调函数还未执行所以要到微信查一下支付的状态。
/**
*查询订单状态
*@paramorderId
*@return
*/
publicPayStatequeryPayState(Long orderId){
try{
Map<String,String>data=newHashMap<>();
//订单号
data.put("out_trade_no",orderId.toString());
//查询状态
Map<String,String>result=wxPay.orderQuery(data);
if(result==null){
//未查询到结果,认为是未付款
returnPayState.NOT_PAY;
}
//校验状态
isSuccess(result);
//校验签名
isValidSign(result);
//3校验金额
StringtotalFeeStr=result.get("total_fee");
//订单号
StringtradeNo=result.get("out_trade_no");
if(StringUtils.isBlank(totalFeeStr)||StringUtils.isBlank(tradeNo)){
thrownewLyException(ExceptionEnum.INVALID_ORDER_PARAM);
}
//3.1获取结果中的金额
longtotalFee=Long.valueOf(totalFeeStr);
//获取订单号
//4查询订单
Orderorder=orderMapper.selectByPrimaryKey(orderId);
if(totalFee!=order.getActualPay()){
//金额不符
thrownewLyException(ExceptionEnum.INVALID_ORDER_PARAM);
}
Stringstate=result.get("trade_state");
if(SUCCESS.equals(state)){
//5修改订单状态
OrderStatusstatus=newOrderStatus();
status.setStatus(OrderStatusEnum.PAYED.value());
status.setOrderId(orderId);
status.setPaymentTime(newDate());
intcount=statusMapper.updateByPrimaryKeySelective(status);
if(count!=1){
thrownewLyException(ExceptionEnum.UPDATE_ORDERSTATUS_ERROR);
}
returnPayState.SUCCESS;
}
if("NOTPAY".equals(state)||"USERPAYING".equals(state)){
return PayState.NOT_PAY;
}
returnPayState.FAIL;
}catch(Exceptione){
log.error("查询订单状态异常",e);
returnPayState.NOT_PAY;
}
}