29 changed files with 1003 additions and 52 deletions
@ -0,0 +1,14 @@ |
|||
package cn.nla.common.constant; |
|||
|
|||
public class TimeConstant { |
|||
|
|||
/** |
|||
* |
|||
* 支付订单的有效时长,超过未支付则关闭订单 |
|||
* |
|||
* 订单超时,毫秒,默认30分钟 |
|||
*/ |
|||
//public static final long ORDER_PAY_TIMEOUT_MILLS = 30*60*1000;
|
|||
public static final long ORDER_PAY_TIMEOUT_MILLS = 5*60*1000; |
|||
|
|||
} |
@ -0,0 +1,128 @@ |
|||
package cn.nla.order.component; |
|||
|
|||
import cn.nla.common.enums.BizCodeEnum; |
|||
import cn.nla.common.enums.ClientType; |
|||
import cn.nla.common.exception.BizException; |
|||
import cn.nla.order.config.AlipayConfig; |
|||
import cn.nla.order.config.PayUrlConfig; |
|||
import cn.nla.order.model.VO.PayInfoVO; |
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alipay.api.AlipayApiException; |
|||
import com.alipay.api.request.AlipayTradePagePayRequest; |
|||
import com.alipay.api.request.AlipayTradeQueryRequest; |
|||
import com.alipay.api.request.AlipayTradeWapPayRequest; |
|||
import com.alipay.api.response.AlipayTradePagePayResponse; |
|||
import com.alipay.api.response.AlipayTradeQueryResponse; |
|||
import com.alipay.api.response.AlipayTradeWapPayResponse; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.HashMap; |
|||
|
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class AlipayStrategy implements PayStrategy { |
|||
@Autowired |
|||
private PayUrlConfig payUrlConfig; |
|||
|
|||
@Override |
|||
public String unifiedorder(PayInfoVO payInfoVO) { |
|||
HashMap<String, String> content = new HashMap<>(); |
|||
//商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
|
|||
content.put("out_trade_no", payInfoVO.getOutTradeNo()); |
|||
content.put("product_code", "FAST_INSTANT_TRADE_PAY"); |
|||
//订单总金额,单位为元,精确到小数点后两位
|
|||
content.put("total_amount", payInfoVO.getPayFee().toString()); |
|||
//商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,& 等。
|
|||
content.put("subject", payInfoVO.getTitle()); |
|||
//商品描述,可空
|
|||
content.put("body", payInfoVO.getDescription()); |
|||
double timeout = Math.floor(payInfoVO.getOrderPayTimeoutMills() / (1000 * 60)); |
|||
//前端也需要判断订单是否要关闭了, 如果要快要到期则不给二次支付
|
|||
if (timeout < 1) { |
|||
throw new BizException(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT); |
|||
} |
|||
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
|
|||
// 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
|
|||
content.put("timeout_express", Double.valueOf(timeout).intValue() + "m"); |
|||
String clientType = payInfoVO.getClientType(); |
|||
String form = ""; |
|||
try { |
|||
if (clientType.equalsIgnoreCase(ClientType.H5.name())) { |
|||
//H5手机网页支付
|
|||
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); |
|||
request.setBizContent(JSON.toJSONString(content)); |
|||
request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl()); |
|||
request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl()); |
|||
AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request); |
|||
log.info("响应日志:alipayResponse={}", alipayResponse); |
|||
if (alipayResponse.isSuccess()) { |
|||
form = alipayResponse.getBody(); |
|||
} else { |
|||
log.error("支付宝构建H5表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO); |
|||
} |
|||
} else if (clientType.equalsIgnoreCase(ClientType.PC.name())) { |
|||
//PC支付
|
|||
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); |
|||
request.setBizContent(JSON.toJSONString(content)); |
|||
request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl()); |
|||
request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl()); |
|||
AlipayTradePagePayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request); |
|||
log.info("响应日志:alipayResponse={}", alipayResponse); |
|||
if (alipayResponse.isSuccess()) { |
|||
form = alipayResponse.getBody(); |
|||
} else { |
|||
log.error("支付宝构建PC表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO); |
|||
} |
|||
} |
|||
} catch (AlipayApiException e) { |
|||
log.error("支付宝构建表单异常:payInfo={},异常={}", payInfoVO, e); |
|||
} |
|||
return form; |
|||
} |
|||
|
|||
@Override |
|||
public String refund(PayInfoVO payInfoVO) { |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 查询订单状态 |
|||
* 支付成功 返回非空 |
|||
* 其他返回空 |
|||
* <p> |
|||
* 未支付 |
|||
* {"alipay_trade_query_response":{"code":"40004","msg":"Business Failed","sub_code":"ACQ.TRADE_NOT_EXIST","sub_msg":"交易不存在","buyer_pay_amount":"0.00","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00"},"sign":"xxxxx"} |
|||
* <p> |
|||
* 已经支付 |
|||
* {"alipay_trade_query_response":{"code":"10000","msg":"Success","buyer_logon_id":"mqv***@sandbox.com","buyer_pay_amount":"0.00","buyer_user_id":"2088102176996700","buyer_user_type":"PRIVATE","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00","send_pay_date":"2020-12-04 17:06:47","total_amount":"111.99","trade_no":"2020120422001496700501648498","trade_status":"TRADE_SUCCESS"},"sign":"xxxx"} |
|||
* |
|||
* @param payInfoVO |
|||
* @return |
|||
*/ |
|||
@Override |
|||
public String queryPaySuccess(PayInfoVO payInfoVO) { |
|||
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); |
|||
HashMap<String, String> content = new HashMap<>(); |
|||
//订单商户号,64位
|
|||
content.put("out_trade_no", payInfoVO.getOutTradeNo()); |
|||
request.setBizContent(JSON.toJSONString(content)); |
|||
AlipayTradeQueryResponse response = null; |
|||
try { |
|||
response = AlipayConfig.getInstance().execute(request); |
|||
log.info("支付宝订单查询响应:{}", response.getBody()); |
|||
|
|||
} catch (AlipayApiException e) { |
|||
log.error("支付宝订单查询异常:{}", e); |
|||
} |
|||
if (response.isSuccess()) { |
|||
log.info("支付宝订单状态查询成功:{}", payInfoVO); |
|||
return response.getTradeStatus(); |
|||
} else { |
|||
log.info("支付宝订单状态查询失败:{}", payInfoVO); |
|||
return ""; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,55 @@ |
|||
package cn.nla.order.component; |
|||
|
|||
import cn.nla.common.enums.ProductOrderPayTypeEnum; |
|||
import cn.nla.order.model.VO.PayInfoVO; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class PayFactory { |
|||
@Autowired |
|||
private AlipayStrategy alipayStrategy; |
|||
@Autowired |
|||
private WechatPayStrategy wechatPayStrategy; |
|||
/** |
|||
* 创建支付,简单工程模式 |
|||
* @param payInfoVO |
|||
* @return |
|||
*/ |
|||
public String pay(PayInfoVO payInfoVO){ |
|||
String payType = payInfoVO.getPayType(); |
|||
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){ |
|||
//支付宝支付
|
|||
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy); |
|||
return payStrategyContext.executeUnifiedorder(payInfoVO); |
|||
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){ |
|||
//微信支付 暂未实现
|
|||
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy); |
|||
return payStrategyContext.executeUnifiedorder(payInfoVO); |
|||
} |
|||
return ""; |
|||
} |
|||
/** |
|||
* 查询订单支付状态 |
|||
* |
|||
* 支付成功返回非空,其他返回空 |
|||
* |
|||
* @param payInfoVO |
|||
* @return |
|||
*/ |
|||
public String queryPaySuccess(PayInfoVO payInfoVO){ |
|||
String payType = payInfoVO.getPayType(); |
|||
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){ |
|||
//支付宝支付
|
|||
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy); |
|||
return payStrategyContext.executeQueryPaySuccess(payInfoVO); |
|||
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){ |
|||
//微信支付 暂未实现
|
|||
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy); |
|||
return payStrategyContext.executeQueryPaySuccess(payInfoVO); |
|||
} |
|||
return ""; |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package cn.nla.order.component; |
|||
|
|||
import cn.nla.order.model.VO.PayInfoVO; |
|||
|
|||
public interface PayStrategy { |
|||
/** |
|||
* 下单 |
|||
*/ |
|||
String unifiedorder(PayInfoVO payInfoVO); |
|||
/** |
|||
* 退款 |
|||
*/ |
|||
default String refund(PayInfoVO payInfoVO){return "";} |
|||
/** |
|||
* 查询支付是否成功 |
|||
*/ |
|||
default String queryPaySuccess(PayInfoVO payInfoVO){return "";} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package cn.nla.order.component; |
|||
import cn.nla.order.model.VO.PayInfoVO; |
|||
|
|||
public class PayStrategyContext { |
|||
private PayStrategy payStrategy; |
|||
public PayStrategyContext(PayStrategy payStrategy){ |
|||
this.payStrategy = payStrategy; |
|||
} |
|||
/** |
|||
* 根据支付策略,调用不同的支付 |
|||
*/ |
|||
public String executeUnifiedorder(PayInfoVO payInfoVO){ |
|||
return this.payStrategy.unifiedorder(payInfoVO); |
|||
} |
|||
|
|||
/** |
|||
* 根据支付的策略,调用不同的查询订单支持状态 |
|||
*/ |
|||
public String executeQueryPaySuccess(PayInfoVO payInfoVO){ |
|||
return this.payStrategy.queryPaySuccess(payInfoVO); |
|||
} |
|||
} |
@ -0,0 +1,22 @@ |
|||
package cn.nla.order.component; |
|||
|
|||
import cn.nla.order.model.VO.PayInfoVO; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class WechatPayStrategy implements PayStrategy { |
|||
@Override |
|||
public String unifiedorder(PayInfoVO payInfoVO) { |
|||
return null; |
|||
} |
|||
@Override |
|||
public String refund(PayInfoVO payInfoVO) { |
|||
return null; |
|||
} |
|||
@Override |
|||
public String queryPaySuccess(PayInfoVO payInfoVO) { |
|||
return null; |
|||
} |
|||
} |
@ -0,0 +1,74 @@ |
|||
package cn.nla.order.config; |
|||
|
|||
import com.alipay.api.AlipayClient; |
|||
import com.alipay.api.DefaultAlipayClient; |
|||
|
|||
public class AlipayConfig { |
|||
|
|||
/** |
|||
* 支付宝网关地址 |
|||
*/ |
|||
public static final String PAY_GATEWAY = "https://openapi-sandbox.dl.alipaydev.com/gateway.do"; |
|||
|
|||
|
|||
/** |
|||
* 支付宝 APPID |
|||
*/ |
|||
public static final String APPID = "9021000140670744"; |
|||
|
|||
/** |
|||
* 应用私钥 |
|||
*/ |
|||
public static final String APP_PRI_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZqPyUdTWlvNXo9MVd2mXVNSugokQSsAkdfArNoecoSVzxZvXzrK3hyIFSvw3N8BXgdFy2WPZ/55uuWReuudpDmsbUntdT//31hCm86KMKLinK/9qd43+L+xtOc7W9X7WUn0Zdy29OtUUgzp1SV17vZoxi+GVijQkBLgENHxQUe6N6gupl8YtSMrG8aUdw63VA2JzGIIuehv5icseBtPqAv1Bb3PV5RGvhrPxQhY87FXE3Yr144yVCQjqUJIBP6KGqDLt8qp5+If+mNkuE3JfdFQKxAj5h6fWFXtZ4JRP2ax0bJcgk8GA3mhHd/fofFYE5D4nekQ2dTVat4n0D62MVAgMBAAECggEAFFSObpn/ahlM+BCCmINP2+C4D/3IIezWl+cUitZz/hmOyYXE3uOm8euUaL1Md0Xfrx3WMm4c1xluuKejAYQng7BTxEfr13pUQIqm9w6Um8E12Fz3sNIryvqDR2L51b93B9328mv7Ix/KsKeFmyJiNiyMQH/68goU+en7S6AkCP2KRUf9zIkCn+7s5ARlB82nH8nOlL8W4J57GBM5w8T5TXIdeZ+kVehaLbNYs45kRDZHgSCDLVZcvWOlQEDLQupU8Z97LMgNugwu26lChX3XJ1Nd8DrixTV61DKzg5qkKFhHb3f+oK89SYbw6DCNmMdI/Eo+MKK4x71fXeCJF7jigQKBgQDNKWgml+DQ1Yv92NI9jeotmtTKfPmHvYjY99+7WZe9hofwLh0F/53efc/x2woSaFMwF/fYDM2qi4sPf/ee2r9jrz2zr7igCfs65YPePWEfw61Enb6XXY5tVz3+0ukkLh5qsJe3+fYcJryvyFhLRHoR01VadxextqwogFQyP5pwtQKBgQC/vIiXNWdnw1+sgoH1ERJCFcOgQZqkHNOj985so+owiRlDjGY20Vhe+i7AJpASIuZIG5vHSG7Q13m15vkj1eSLa/Xjvo985paEv/wkZH5QWv3VSvN+3KuZX1uxDIhwiSKw8o7T8vIPIwPehemI5/wh2eVz5xuMx0zqpl6kRm2E4QKBgQCza3zcE4LW34qYK2Bh2JRt4bQqiXUoEsd1CAGvtMCRTO/hSlKzhkKn61tJ5gc+5eGqc6uaVEgg1/x7R6xkTmWGZiEN3aUWwOPnNAVI/GDOCLClC1c8xOLkGKHtEsI1k+eh4mIs0Ps8z1vAVvNwj6g5oppIjVI5ZsnUebYoY5tK6QKBgGW8TeWdqo5+Nke8v+Qe+Mcj8TcZWEp4GTxJgrhWiS7TsBoNZzbzAabzrNa8H3ngFrxwGco1/sbFbok8UznWhnwL/t6nAE/PslIh5FITGIrenQ0NxgGsaUjqNibNdZ/ww/2L1olTwWiStbze5TmFjl0P1xYWrKGYJpHjURhtXjNBAoGARNV7eG+H6pAFjECQ7fgNWAbPYQaFuWZQijMdRec6OOnLRvnzMlBWkrCmlSQS8Wl5PKNSkj9x1BCbFitrfv6SUONFi8Mx13lRQKHrWEvyrvPwv7pA2x1QkIn3po1p6obh8xmy7NsUPoBnZsWUWgyE94bYYmHteEmxclezrazsac4="; |
|||
|
|||
/** |
|||
* 支付宝公钥 |
|||
*/ |
|||
public static final String ALIPAY_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoKAJ8j4r772JuPQDgVsMckqkeDQtJN4xZRa208dv9TClOsPUyptpyjDTnb01cXrZCBxWWbnYx5YlYjq+4Fee22HOWlh+lozSmFWeWigKefj7zSFji1WMmHTnSlYF0nvRPKWMfXKBqlnvkG1H82jxmeqDsZGw/a53jFL5EKiaS7bvLf3BlS8KCEuMvnwGQXMiTaIe98GkV8YcbEUXAEi7KLrinslEVwkCOA7y8exjOlCWJEi2lcbXyiuqejDs2K594O8wOd5RcoWpOmWYRMV0dhjue7ovETyErfeK0A0nTCmXmbZdcLYVOODzLHO2zupVSyF6Rj845BLb+z2tnZ3QwIDAQAB"; |
|||
|
|||
|
|||
/** |
|||
* 签名类型 |
|||
*/ |
|||
public static final String SIGN_TYPE = "RSA2"; |
|||
|
|||
|
|||
/** |
|||
* 字符编码 |
|||
*/ |
|||
public static final String CHARSET = "UTF-8"; |
|||
|
|||
|
|||
/** |
|||
* 返回参数格式 |
|||
*/ |
|||
public static final String FORMAT = "json"; |
|||
|
|||
|
|||
/** |
|||
* 构造函数私有化 |
|||
*/ |
|||
private AlipayConfig() { |
|||
|
|||
} |
|||
|
|||
private volatile static AlipayClient instance = null; |
|||
|
|||
/** |
|||
* 单例模式获取, 双重锁校验 |
|||
* |
|||
* @return |
|||
*/ |
|||
public static AlipayClient getInstance() { |
|||
if (instance == null) { |
|||
synchronized (AlipayConfig.class) { |
|||
if (instance == null) { |
|||
instance = new DefaultAlipayClient(PAY_GATEWAY, APPID, APP_PRI_KEY, FORMAT, CHARSET, ALIPAY_PUB_KEY, SIGN_TYPE); |
|||
} |
|||
} |
|||
} |
|||
return instance; |
|||
} |
|||
|
|||
|
|||
} |
@ -0,0 +1,23 @@ |
|||
package cn.nla.order.config; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Configuration |
|||
@Data |
|||
public class PayUrlConfig { |
|||
|
|||
/** |
|||
* 支付成功页面跳转 |
|||
*/ |
|||
@Value("${alipay.success_return_url}") |
|||
private String alipaySuccessReturnUrl; |
|||
|
|||
|
|||
/** |
|||
* 支付成功,回调通知 |
|||
*/ |
|||
@Value("${alipay.callback_url}") |
|||
private String alipayCallbackUrl; |
|||
} |
@ -0,0 +1,74 @@ |
|||
package cn.nla.order.controller; |
|||
|
|||
import cn.nla.common.enums.ProductOrderPayTypeEnum; |
|||
import cn.nla.common.util.JsonData; |
|||
import cn.nla.order.config.AlipayConfig; |
|||
import cn.nla.order.service.ProductOrderService; |
|||
import com.alipay.api.AlipayApiException; |
|||
import com.alipay.api.internal.util.AlipaySignature; |
|||
import io.swagger.annotations.Api; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Controller; |
|||
import org.springframework.web.bind.annotation.PostMapping; |
|||
import org.springframework.web.bind.annotation.RequestMapping; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
|
|||
|
|||
@Api(tags ="订单回调通知模块") |
|||
@Controller |
|||
@RequestMapping("/odr/callback/v1") |
|||
@Slf4j |
|||
public class CallbackController { |
|||
@Autowired |
|||
private ProductOrderService productOrderService; |
|||
|
|||
/** |
|||
* 支付回调通知 post方式 |
|||
*/ |
|||
@PostMapping("alipay") |
|||
public String alipayCallback(HttpServletRequest request, HttpServletResponse response) { |
|||
//将异步通知中收到的所有参数存储到map中
|
|||
log.info("into alipay "); |
|||
Map<String, String> paramsMap = convertRequestParamsToMap(request); |
|||
log.info("支付宝回调通知结果:{}", paramsMap); |
|||
//调用SDK验证签名
|
|||
try { |
|||
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.ALIPAY_PUB_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE); |
|||
if (signVerified) { |
|||
JsonData jsonData = productOrderService.handlerOrderCallbackMsg(ProductOrderPayTypeEnum.ALIPAY, paramsMap); |
|||
if (jsonData.getCode() == 0) { |
|||
//通知结果确认成功,不然会一直通知,八次都没返回success就认为交易失败
|
|||
return "success"; |
|||
} |
|||
} |
|||
} catch (AlipayApiException e) { |
|||
log.info("支付宝回调验证签名失败:异常:{},参数:{}", e, paramsMap); |
|||
} |
|||
return "failure"; |
|||
} |
|||
|
|||
/** |
|||
* 将request中的参数转换成Map |
|||
*/ |
|||
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) { |
|||
Map<String, String> paramsMap = new HashMap<>(16); |
|||
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet(); |
|||
for (Map.Entry<String, String[]> entry : entrySet) { |
|||
String name = entry.getKey(); |
|||
String[] values = entry.getValue(); |
|||
int size = values.length; |
|||
if (size == 1) { |
|||
paramsMap.put(name, values[0]); |
|||
} else { |
|||
paramsMap.put(name, ""); |
|||
} |
|||
} |
|||
return paramsMap; |
|||
} |
|||
} |
@ -0,0 +1,42 @@ |
|||
package cn.nla.order.model.VO; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.math.BigDecimal; |
|||
|
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class PayInfoVO { |
|||
/** |
|||
* 订单号 |
|||
*/ |
|||
private String outTradeNo; |
|||
/** |
|||
* 订单总金额 |
|||
*/ |
|||
private BigDecimal payFee; |
|||
/** |
|||
* 支付类型 微信-支付宝-银行-其他 |
|||
*/ |
|||
private String payType; |
|||
/** |
|||
* 端类型 APP/H5/PC |
|||
*/ |
|||
private String clientType; |
|||
/** |
|||
* 标题 |
|||
*/ |
|||
private String title; |
|||
/** |
|||
* 描述 |
|||
*/ |
|||
private String description; |
|||
/** |
|||
* 订单支付超时时间,毫秒 |
|||
*/ |
|||
private long orderPayTimeoutMills; |
|||
} |
@ -0,0 +1,82 @@ |
|||
package cn.nla.order.model.VO; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class ProductOrderVO { |
|||
private Long id; |
|||
/** |
|||
* 订单唯一标识 |
|||
*/ |
|||
private String outTradeNo; |
|||
|
|||
/** |
|||
* NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单 |
|||
*/ |
|||
private String state; |
|||
|
|||
/** |
|||
* 订单生成时间 |
|||
*/ |
|||
private Date createTime; |
|||
|
|||
/** |
|||
* 订单总金额 |
|||
*/ |
|||
private BigDecimal totalAmount; |
|||
|
|||
/** |
|||
* 订单实际支付价格 |
|||
*/ |
|||
private BigDecimal payAmount; |
|||
|
|||
/** |
|||
* 支付类型,微信-银行-支付宝 |
|||
*/ |
|||
private String payType; |
|||
|
|||
/** |
|||
* 昵称 |
|||
*/ |
|||
private String nickname; |
|||
|
|||
/** |
|||
* 头像 |
|||
*/ |
|||
private String headImg; |
|||
|
|||
/** |
|||
* 用户id |
|||
*/ |
|||
private Long userId; |
|||
|
|||
/** |
|||
* 0表示未删除,1表示已经删除 |
|||
*/ |
|||
private Integer del; |
|||
|
|||
/** |
|||
* 更新时间 |
|||
*/ |
|||
private Date updateTime; |
|||
|
|||
/** |
|||
* 订单类型 DAILY普通单,PROMOTION促销订单 |
|||
*/ |
|||
private String orderType; |
|||
|
|||
/** |
|||
* 收货地址 json存储 |
|||
*/ |
|||
private String receiverAddress; |
|||
|
|||
/** |
|||
* 订单项 |
|||
*/ |
|||
private List<OrderItemVO> orderItemList; |
|||
|
|||
} |
@ -0,0 +1,23 @@ |
|||
package cn.nla.order.model.request; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class RepayOrderRequest { |
|||
/** |
|||
* 订单号 |
|||
*/ |
|||
@JsonProperty("out_trade_no") |
|||
private String outTradeNo; |
|||
/** |
|||
* 支付类型- 微信-银行卡-支付宝 |
|||
*/ |
|||
@JsonProperty("pay_type") |
|||
private String payType; |
|||
/** |
|||
* 订单号 |
|||
*/ |
|||
@JsonProperty("client_type") |
|||
private String clientType; |
|||
} |
@ -0,0 +1,43 @@ |
|||
package cn.nla.order.mq; |
|||
|
|||
import cn.nla.common.model.OrderMessage; |
|||
import cn.nla.order.service.ProductOrderService; |
|||
import com.rabbitmq.client.Channel; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.amqp.core.Message; |
|||
import org.springframework.amqp.rabbit.annotation.RabbitHandler; |
|||
import org.springframework.amqp.rabbit.annotation.RabbitListener; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
import java.io.IOException; |
|||
|
|||
|
|||
@Slf4j |
|||
@Component |
|||
@RabbitListener(queues = "${mq.config.order_close_queue}") |
|||
public class ProductOrderMQListener { |
|||
@Autowired |
|||
private ProductOrderService productOrderService; |
|||
|
|||
/** |
|||
* |
|||
* 消费重复消息,幂等性保证 |
|||
* 并发情况下如何保证安全 |
|||
*/ |
|||
@RabbitHandler |
|||
public void closeProductOrder(OrderMessage orderMessage, Message message, Channel channel) throws IOException { |
|||
log.info("监听到消息:closeProductOrder:{}",orderMessage); |
|||
long msgTag = message.getMessageProperties().getDeliveryTag(); |
|||
try{ |
|||
boolean flag = productOrderService.closeProductOrder(orderMessage); |
|||
if(flag){ |
|||
channel.basicAck(msgTag,false); |
|||
}else { |
|||
channel.basicReject(msgTag,true); |
|||
} |
|||
}catch (IOException e){ |
|||
log.error("定时关单失败:",orderMessage); |
|||
channel.basicReject(msgTag,true); |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue