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