Commit bf17e692 authored by gaoyingwei's avatar gaoyingwei

添加 订单退款

parent f3c3b0b1
......@@ -37,6 +37,14 @@
<artifactId>redisson</artifactId>
<version>3.20.0</version>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
</dependency>
</dependencies>
......
package com.emall.flash.api;
import com.emall.flash.api.config.WxProperties;
import com.emall.flash.dao.BaseRepositoryFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
......@@ -23,6 +25,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@EntityScan(basePackages="com.emall.flash.bean.entity")
@EnableJpaRepositories(basePackages = "com.emall.flash.dao", repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class)
@EnableJpaAuditing
@EnableConfigurationProperties(WxProperties.class)
public class AdminApiApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
......
package com.emall.flash.api.config;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.config.WxMaConfig;
import cn.binarywang.wx.miniapp.config.WxMaInMemoryConfig;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author :enilu
* @date :Created in 2020/3/17 22:11
*/
@Configuration
public class WxConfig {
@Autowired
private WxProperties properties;
@Bean
public WxMaConfig wxMaConfig() {
WxMaInMemoryConfig config = new WxMaInMemoryConfig();
config.setAppid(properties.getAppId());
config.setSecret(properties.getAppSecret());
return config;
}
@Bean
public WxMaService wxMaService(WxMaConfig maConfig) {
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(maConfig);
return service;
}
@Bean
public WxPayConfig wxPayConfig() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(properties.getAppId());
payConfig.setMchId(properties.getMchId());
payConfig.setMchKey(properties.getMchKey());
payConfig.setNotifyUrl(properties.getNotifyUrl());
payConfig.setKeyPath(properties.getKeyPath());
payConfig.setTradeType("JSAPI");
payConfig.setSignType("MD5");
return payConfig;
}
@Bean
public WxPayService wxPayService(WxPayConfig payConfig) {
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
package com.emall.flash.api.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "wx")
public class WxProperties {
private String appId;
private String appSecret;
private String mchId;
private String mchKey;
private String notifyUrl;
private String keyPath;
public String getNotifyUrl() {
return notifyUrl;
}
public void setNotifyUrl(String notifyUrl) {
this.notifyUrl = notifyUrl;
}
public String getMchKey() {
return mchKey;
}
public void setMchKey(String mchKey) {
this.mchKey = mchKey;
}
public String getAppId() {
return this.appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getAppSecret() {
return appSecret;
}
public void setAppSecret(String appSecret) {
this.appSecret = appSecret;
}
public String getMchId() {
return mchId;
}
public void setMchId(String mchId) {
this.mchId = mchId;
}
public String getKeyPath() {
return keyPath;
}
public void setKeyPath(String keyPath) {
this.keyPath = keyPath;
}
}
package com.emall.flash.api.controller.shop;
import com.emall.flash.api.service.WeixinPayService;
import com.emall.flash.bean.constant.factory.PageFactory;
import com.emall.flash.bean.core.BussinessLog;
import com.emall.flash.bean.entity.shop.ExpressInfo;
......@@ -25,6 +26,7 @@ import com.emall.flash.utils.factory.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
......@@ -46,6 +48,8 @@ public class OrderController {
private CfgService cfgService;
@Autowired
private ManagerService managerService;
@Autowired
private WeixinPayService weixinPayService;
/**
* 获取订单统计信息
......@@ -123,6 +127,7 @@ public class OrderController {
}
@RequestMapping(value = "/sendOut/{id}", method = RequestMethod.POST)
@BussinessLog(value = "发货", key = "id")
@Transactional
public Object sendOut(@PathVariable("id") Long id,
@RequestParam("idExpress") Long idExpress,
@RequestParam("shippingSn") String shippingSn,
......@@ -158,4 +163,16 @@ public class OrderController {
ExpressInfo response = orderService.getExpressInfo(orderSn);
return Rets.success(response);
}
@RequestMapping(value = "/refund/{id}", method = RequestMethod.POST)
@BussinessLog(value = "退款", key = "id")
@Transactional
public Object refund(@PathVariable("id") Long id) {
Order order = orderService.get(id);
weixinPayService.refund(order);
String descript = "管理员(" + JwtUtil.getUsername() + ")发起退款";
orderService.saveOrderLog(order, descript);
return Rets.success();
}
}
package com.emall.flash.api.service;
import com.emall.flash.bean.entity.shop.Order;
import com.emall.flash.bean.entity.shop.ShopUser;
import com.emall.flash.bean.enumeration.shop.OrderEnum;
import com.emall.flash.service.shop.OrderService;
import com.emall.flash.utils.HttpUtil;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import org.apache.commons.io.IOUtils;
import org.nutz.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
/**
* 微信支付
* @author :enilu
* @date :Created in 2020/3/17 20:26
*/
@Service
public class WeixinPayService {
@Autowired
private WxPayService wxPayService;
@Autowired
private OrderService orderService;
@Value("${wx.refund-notify-url}")
private String refundNotifyUrl;
private Logger logger = LoggerFactory.getLogger(WeixinPayService.class);
/**
* 退款
*
* @return
*/
public WxPayRefundResult refund(Order order) {
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
wxPayRefundRequest.setTransactionId(order.getPayId());
wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));
BigDecimal totalPrice = order.getTotalPrice();
wxPayRefundRequest.setTotalFee(totalPrice.intValue());
wxPayRefundRequest.setRefundFee(totalPrice.intValue());
wxPayRefundRequest.setNotifyUrl(refundNotifyUrl);
try {
WxPayRefundResult refund = wxPayService.refund(wxPayRefundRequest);
if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {
order.setStatus(OrderEnum.OrderStatusEnum.REFUND_ING.getId());
order.setRefundTime(new Date());
orderService.updateOrder(order);
return refund;
}
} catch (WxPayException e) {
logger.error("微信退款异常",e);
}
return null;
}
}
......@@ -42,3 +42,13 @@ spring.cache.redis.cache-null-values=true
# 发送小程序订阅信息url
wechat.sendMessageUrl=https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=
wx.app-id=wx16cfb2d12e4ab57c
wx.app-secret=37556072ad536b8d9d6cf5fb638fec88
wx.mch-id=1615816319
wx.mch-key=b90d421495604c33a943bb9be12d28c0
wx.notify-url=https://qf.91isoft.com/cshop/pay/wx/notify
# 退款回调
wx.refund-notify-url=https://qf.91isoft.com/cshop/pay/wx/refundNotify
# 商户证书文件路径
wx.key-path= /working/upload/config/certs/apiclient_cert.p12
......@@ -41,3 +41,13 @@ spring.cache.redis.cache-null-values=true
# 发送小程序订阅信息url
wechat.sendMessageUrl=https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=
wx.app-id=wx16cfb2d12e4ab57c
wx.app-secret=37556072ad536b8d9d6cf5fb638fec88
wx.mch-id=1615816319
wx.mch-key=b90d421495604c33a943bb9be12d28c0
wx.notify-url=https://qf.91isoft.com/cshop/pay/wx/notify
# 退款回调
wx.refund-notify-url=https://qf.91isoft.com/cshop/pay/wx/refundNotify
# 商户证书文件路径
wx.key-path= /working/upload/config/certs/apiclient_cert.p12
......@@ -91,6 +91,10 @@ public class Order extends ShopBaseEntity {
private String payId;
@Column(columnDefinition = "VARCHAR(32) COMMENT '支付成功时间'")
private Date payTime;
@Column(columnDefinition = "VARCHAR(32) COMMENT '退款单号'")
private String refundSn;
@Column(columnDefinition = "VARCHAR(32) COMMENT '退款时间'")
private Date refundTime;
//商圈id
@Column(updatable = false)
private Long tenantId;
......@@ -117,4 +121,8 @@ public class Order extends ShopBaseEntity {
public Boolean hasPayed(){
return OrderEnum.PayStatusEnum.UN_SEND.getId().equals(payStatus);
}
public Boolean hasRefunded(){
return OrderEnum.OrderStatusEnum.REFUND.getId().equals(status);
}
}
......@@ -13,7 +13,8 @@ public class OrderEnum {
FINISHED(4, "已完成"),
CANCEL(5,"已取消"),
REFUND_ING(6,"退款中"),
REFUND(7,"已退款");
REFUND(7,"已退款"),
UN_REFUND(8,"待退款");
private String value;
......
......@@ -151,7 +151,7 @@ public class OrderService extends BaseService<Order, Long, OrderRepository> {
}
private void saveOrderLog(Order order, String descript) {
public void saveOrderLog(Order order, String descript) {
OrderLog orderLog = new OrderLog();
orderLog.setIdOrder(order.getId());
orderLog.setDescript(descript);
......@@ -207,7 +207,7 @@ public class OrderService extends BaseService<Order, Long, OrderRepository> {
*/
public ExpressInfo getExpressInfo(String orderSn) {
Order order = getByOrderSn(orderSn);
ExpressInfo expressInfo = expressInfoService.get(SearchFilter.build("idOrder", order.getId()));
ExpressInfo expressInfo = expressInfoService.get(SearchFilter.build("idOrder", 3));
if ((expressInfo == null
|| expressInfo.getState() != ExpressInfo.STATE_FINISH) &&
......
......@@ -4,6 +4,7 @@ import com.emall.flash.bean.constant.factory.PageFactory;
import com.emall.flash.bean.enumeration.shop.OrderEnum;
import com.emall.flash.bean.vo.front.Rets;
import com.emall.flash.bean.vo.query.SearchFilter;
import com.emall.flash.service.WeixinPayService;
import com.emall.flash.service.api.express.kdniao.KdniaoService;
import com.emall.flash.service.shop.*;
import com.emall.flash.service.system.CfgService;
......@@ -22,6 +23,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
......@@ -47,6 +49,8 @@ public class OrderController extends BaseController {
private CfgService cfgService;
@Autowired
private KdniaoService kdniaoService;
@Autowired
private WeixinPayService weixinPayService;
@RequestMapping(value = "{orderSn}", method = RequestMethod.GET)
public Object get(@PathVariable(value = "orderSn") String orderSn) {
......@@ -62,7 +66,10 @@ public class OrderController extends BaseController {
page.addFilter(SearchFilter.build("idUser", SearchFilter.Operator.EQ, idUser));
page.setSort(Sort.by(Sort.Direction.DESC, "id"));
if (status != null && status != 0) {
page.addFilter(SearchFilter.build("status", SearchFilter.Operator.EQ, status));
if (status == 5)
page.addFilter(SearchFilter.build("status", SearchFilter.Operator.IN, Lists.newArrayList(6,7,8)));
else
page.addFilter(SearchFilter.build("status", SearchFilter.Operator.EQ, status));
}
if (tenantId != null) {
page.addFilter(SearchFilter.build("tenantId", SearchFilter.Operator.EQ, tenantId));
......@@ -230,4 +237,26 @@ public class OrderController extends BaseController {
);
return Rets.success(data);
}
/**
* 用户订单退款
*
* @param orderSn
*/
@RequestMapping(value = "refund/{orderSn}", method = RequestMethod.POST)
public Object refund(@PathVariable("orderSn") String orderSn,
@RequestParam(required = false) String refundSn) {
Order order = orderService.getByOrderSn(orderSn);
//如果订单状态是待发货,可以直接退款
if (order.getStatus().equals(OrderEnum.OrderStatusEnum.UN_SEND.getId())){
weixinPayService.refund(order);
} else {
order.setRefundSn(refundSn);
order.setStatus(OrderEnum.OrderStatusEnum.UN_REFUND.getId());
orderService.updateOrder(order);
}
String descript = "用户申请退款";
orderService.saveOrderLog(order, descript);
return Rets.success();
}
}
......@@ -62,4 +62,14 @@ public class PayController extends BaseController {
&& OrderEnum.PayStatusEnum.UN_SEND.getId().equals(order.getStatus());
return Rets.success(payResult);
}
/**
* 微信退款回调
* @return
*/
@RequestMapping(value = "wx/refundNotify",method = RequestMethod.POST)
public Object refundNotify(){
String msg = weixinPayService.refundNotify();
return msg;
}
}
......@@ -7,8 +7,11 @@ import com.emall.flash.service.shop.OrderService;
import com.emall.flash.utils.HttpUtil;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
......@@ -17,11 +20,13 @@ import org.nutz.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
/**
* 微信支付
......@@ -34,6 +39,9 @@ public class WeixinPayService {
private WxPayService wxPayService;
@Autowired
private OrderService orderService;
@Value("${wx.refund-notify-url}")
private String refundNotifyUrl;
private Logger logger = LoggerFactory.getLogger(WeixinPayService.class);
public WxPayMpOrderResult prepare(ShopUser user, Order order){
......@@ -112,4 +120,95 @@ public class WeixinPayService {
//todo 发送微信模板消息
return WxPayNotifyResponse.success("支付成功!");
}
/**
* 退款
*
* @param tradeNo
* @param totalFee
* @return
*/
public WxPayRefundResult refund( Order order) {
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
wxPayRefundRequest.setTransactionId(order.getPayId());
wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));
BigDecimal totalPrice = order.getTotalPrice();
wxPayRefundRequest.setTotalFee(totalPrice.intValue());
wxPayRefundRequest.setRefundFee(totalPrice.intValue());
wxPayRefundRequest.setNotifyUrl(refundNotifyUrl);
try {
WxPayRefundResult refund = wxPayService.refund(wxPayRefundRequest);
if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {
order.setStatus(OrderEnum.OrderStatusEnum.REFUND_ING.getId());
order.setRefundTime(new Date());
orderService.updateOrder(order);
return refund;
}
} catch (WxPayException e) {
logger.error("微信退款异常",e);
}
return null;
}
public String refundNotify() {
String xmlResult = null;
try {
HttpServletRequest request = HttpUtil.getRequest();
xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
} catch (IOException e) {
logger.error("解析微信退款结果通知异常",e);
return WxPayNotifyResponse.fail(e.getMessage());
}
logger.error(xmlResult);
WxPayRefundNotifyResult result = null;
try {
result = wxPayService.parseRefundNotifyResult(xmlResult);
if (!WxPayConstants.ResultCode.SUCCESS.equals(result.getReturnCode())) {
logger.error(xmlResult);
throw new WxPayException("微信退款-通知失败!");
}
if (!WxPayConstants.RefundStatus.SUCCESS.equals(result.getReqInfo().getRefundStatus())) {
logger.error(xmlResult);
throw new WxPayException("微信退款-通知失败");
}
} catch (WxPayException e) {
logger.error("微信退款-通知失败", e);
return WxPayNotifyResponse.fail(e.getMessage());
}
WxPayRefundNotifyResult.ReqInfo reqInfo = result.getReqInfo();
logger.info("处理腾讯支付平台的订单退款", Json.toJson(result));
String orderSn = reqInfo.getOutTradeNo();
// String payId = reqInfo.getTransactionId();
Integer totalFee = reqInfo.getTotalFee();
Order order = orderService.getByOrderSn(orderSn);
if (order == null) {
return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
}
// 检查这个订单是否已经处理过
if (order.hasRefunded()) {
return WxPayNotifyResponse.success("订单已经处理成功!");
}
// 检查支付订单金额
if (totalFee.intValue()!=order.getTotalPrice().intValue()) {
return WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee);
}
order.setStatus(OrderEnum.OrderStatusEnum.REFUND.getId());
orderService.updateOrder(order);
orderService.saveOrderLog(order, "退款成功");
//todo 发送短信通知
//todo 发送微信模板消息
return WxPayNotifyResponse.success("支付成功!");
}
}
......@@ -14,6 +14,7 @@ wx.app-secret=37556072ad536b8d9d6cf5fb638fec88
wx.mch-id=1615816319
wx.mch-key=b90d421495604c33a943bb9be12d28c0
wx.notify-url=https://qf.91isoft.com/cshop/pay/wx/notify
wx.refund-notify-url=https://qf.91isoft.com/cshop/pay/wx/refundNotify
# 商户证书文件路径
wx.key-path= /working/upload/config/certs/apiclient_cert.p12
#server.servlet.context-path=/api
......
......@@ -14,6 +14,7 @@ wx.app-secret=37556072ad536b8d9d6cf5fb638fec88
wx.mch-id=1615816319
wx.mch-key=b90d421495604c33a943bb9be12d28c0
wx.notify-url=https://qf.91isoft.com/cshop/pay/wx/notify
wx.refund-notify-url=https://qf.91isoft.com/cshop/pay/wx/refundNotify
# 商户证书文件路径
wx.key-path= /working/upload/config/certs/apiclient_cert.p12
#server.servlet.context-path=/api
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment