取消預約分兩種情況:
FrontOrderInfoController中新增介面方法
@ApiOperation("取消預約")
@ApiImplicitParam(name = "outTradeNo",value = "訂單id", required = true)
@GetMapping("/auth/cancelOrder/{outTradeNo}")
public Result cancelOrder(@PathVariable("outTradeNo") String outTradeNo, HttpServletRequest request, HttpServletResponse response) {
authContextHolder.checkAuth(request, response);
orderInfoService.cancelOrder(outTradeNo);
return Result.ok().message("預約已取消");
}
在OrderStatusEnum增加兩個狀態:
CANCLE_UNREFUND(-2,"取消預約,退款中"),
CANCLE_REFUND(-3,"取消預約,已退款"),
介面:OrderInfoService
/**
* 根據訂單號取消訂單
* @param outTradeNo
*/
void cancelOrder(String outTradeNo);
實現:OrderInfoServiceImpl
@Override
public void cancelOrder(String outTradeNo) {
//獲取訂單
OrderInfo orderInfo = this.selectByOutTradeNo(outTradeNo);
//當前時間大於退號時間,不能取消預約
DateTime quitTime = new DateTime(orderInfo.getQuitTime());
if (quitTime.isBeforeNow()) {
throw new GuiguException(ResultCodeEnum.CANCEL_ORDER_NO);
}
//呼叫醫院端介面,同步資料
Map<String, Object> params = new HashMap<>();
params.put("hoscode", orderInfo.getHoscode());
params.put("hosOrderId", orderInfo.getHosOrderId());
params.put("hosScheduleId", orderInfo.getHosScheduleId());
params.put("timestamp", HttpRequestHelper.getTimestamp());
params.put("sign", HttpRequestHelper.getSign(params, "8af52af00baf6aec434109fc17164aae"));
JSONObject jsonResult = HttpRequestHelper.sendRequest(params, "http://localhost:9998/order/updateCancelStatus");
if(jsonResult.getInteger("code") != 200) {
throw new GuiguException(ResultCodeEnum.CANCEL_ORDER_FAIL);
}
//是否支付
if (orderInfo.getOrderStatus().intValue() == OrderStatusEnum.PAID.getStatus().intValue()) {
//已支付,則退款
log.info("退款");
//wxPayService.refund(outTradeNo);
//更改訂單狀態
this.updateStatus(outTradeNo, OrderStatusEnum.CANCLE_UNREFUND.getStatus());
}else{
//更改訂單狀態
this.updateStatus(outTradeNo, OrderStatusEnum.CANCLE.getStatus());
}
//TODO 根據醫院返回資料,更新排班數量
//TODO 給就診人傳送簡訊
}
orderInfo.js中新增方法
//取消預約
cancelOrder(outTradeNo) {
return request({
url: `/front/order/orderInfo/auth/cancelOrder/${outTradeNo}`,
method: 'get'
})
},
order/show.vue中新增方法
//取消預約方法
cancelOrder() {
this.$confirm('確定取消預約嗎?', '提示', {
confirmButtonText: '確定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
// 點選確定,遠端呼叫
orderInfoApi.cancelOrder(this.orderInfo.outTradeNo).then((response) => {
this.$message.success('取消成功')
this.init()
})
})
},
參考檔案:申請退款API
注意:此步驟只是申請退款,具體退款是否成功,要通過退款查詢介面獲取,或通過退款回撥通知獲取。
OrderInfoServiceImpl
@Resource
private WxPayService wxPayService;
//已支付,則退款
log.info("退款");
wxPayService.refund(outTradeNo);
介面:WxPayService
/**
* 退款
* @param outTradeNo
*/
void refund(String outTradeNo);
實現:WxPayServiceImpl
@Resource
private RefundInfoService refundInfoService;
@Override
public void refund(String outTradeNo) {
// 初始化服務
RefundService service = new RefundService.Builder().config(rsaAutoCertificateConfig).build();
// 呼叫介面
try {
//獲取訂單
OrderInfo orderInfo = orderInfoService.selectByOutTradeNo(outTradeNo);
CreateRequest request = new CreateRequest();
// 呼叫request.setXxx(val)設定所需引數,具體引數可見Request定義
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo("TK_" + outTradeNo);
AmountReq amount = new AmountReq();
//amount.setTotal(orderInfo.getAmount().multiply(new BigDecimal(100)).intValue());
amount.setTotal(1L);//1分錢
amount.setRefund(1L);
amount.setCurrency("CNY");
request.setAmount(amount);
// 呼叫介面
Refund response = service.create(request);
Status status = response.getStatus();
// SUCCESS:退款成功(退款申請成功)
// CLOSED:退款關閉
// PROCESSING:退款處理中
// ABNORMAL:退款異常
if(Status.CLOSED.equals(status)){
throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "退款已關閉,無法退款");
}else if(Status.ABNORMAL.equals(status)){
throw new GuiguException(ResultCodeEnum.FAIL.getCode(), "退款異常");
} else{
//SUCCESS:退款成功(退款申請成功) || PROCESSING:退款處理中
//記錄支退款紀錄檔
refundInfoService.saveRefundInfo(orderInfo, response);
}
} catch (HttpException e) { // 傳送HTTP請求失敗
// 呼叫e.getHttpRequest()獲取請求列印紀錄檔或上報監控,更多方法見HttpException定義
log.error(e.getHttpRequest().toString());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (ServiceException e) { // 服務返回狀態小於200或大於等於300,例如500
// 呼叫e.getResponseBody()獲取返回體列印紀錄檔或上報監控,更多方法見ServiceException定義
log.error(e.getResponseBody());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (MalformedMessageException e) { // 服務返回成功,返回體型別不合法,或者解析返回體失敗
// 呼叫e.getMessage()獲取資訊列印紀錄檔或上報監控,更多方法見MalformedMessageException定義
log.error(e.getMessage());
throw new GuiguException(ResultCodeEnum.FAIL);
}
}
介面:RefundInfoService
/**
* 儲存退款記錄
* @param orderInfo
* @param response
*/
void saveRefundInfo(OrderInfo orderInfo, Refund response);
實現:RefundInfoServiceImpl
@Override
public void saveRefundInfo(OrderInfo orderInfo, Refund response) {
// 儲存退款記錄
RefundInfo refundInfo = new RefundInfo();
refundInfo.setOutTradeNo(orderInfo.getOutTradeNo());
refundInfo.setOrderId(orderInfo.getId());
refundInfo.setPaymentType(PaymentTypeEnum.WEIXIN.getStatus());
refundInfo.setTradeNo(response.getOutRefundNo());
refundInfo.setTotalAmount(new BigDecimal(response.getAmount().getRefund()));
refundInfo.setSubject(orderInfo.getTitle());
refundInfo.setRefundStatus(RefundStatusEnum.UNREFUND.getStatus());//退款中
baseMapper.insert(refundInfo);
}
前面我們已經申請了退款,具體退款是否成功,要通過退款查詢介面獲取,或通過退款回撥通知獲取。這裡我們學習退款通知如何實現。
資料:資料>微信支付>小米球ngrok.rar
參考小米球使用教學
開通內網穿透服務,獲取內網穿透服務地址
將組態檔中的開發引數wxpay.notify-refund-url
的主機地址部分
修改為自己的內網穿透地址,例如:
#退款通知回撥地址:申請退款是提交這個引數
wxpay.notify-refund-url=http://agxnyzl04y90.ngrok.xiaomiqiu123.top/api/order/wxpay/refunds/notify
呼叫退款申請API時新增引數notify-refund-url
引數。
WxPayServiceImpl類中的refund方法中新增如下引數:
request.setNotifyUrl(wxPayConfig.getNotifyRefundUrl());
介面:RefundInfoService
/**
* 更新退款狀態
* @param refundNotification
* @param refund
*/
void updateRefundInfoStatus(RefundNotification refundNotification, RefundStatusEnum refund);
實現:RefundInfoServiceImpl
@Override
public void updateRefundInfoStatus(RefundNotification refundNotification, RefundStatusEnum refundStatus) {
LambdaQueryWrapper<RefundInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(RefundInfo::getOutTradeNo, outTradeNo);
RefundInfo refundInfo = new RefundInfo();
refundInfo.setRefundStatus(refundStatus.getStatus());
refundInfo.setCallbackContent(refundNotification.toString());
refundInfo.setCallbackTime(new Date());
baseMapper.update(refundInfo, queryWrapper);
}
<!--回撥驗籤程式碼需要-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.9</version>
</dependency>
將資料目錄中的請求引數工具放入service-order微服務的utils包中
資料:資料>微信支付>RequestUtils.java
程式碼參考:GitHub - wechatpay-apiv3/wechatpay-java: 微信支付 APIv3 的官方 Java Library
建立controller.api包,建立ApiWXPayController類:
解析回撥引數、驗籤、請求內容解密、獲取退款結果、記錄退款紀錄檔
package com.atguigu.syt.order.controller.api;
/**
* 接收微信傳送給伺服器的遠端回撥
*/
@Api(tags = "微信支付介面")
@Controller
@RequestMapping("/api/order/wxpay")
@Slf4j
public class ApiWXPayController {
@Resource
private RefundInfoService refundInfoService;
@Resource
private OrderInfoService orderInfoService;
@Resource
private RSAAutoCertificateConfig rsaAutoCertificateConfig;
/**
* 退款結果通知
* 退款狀態改變後,微信會把相關退款結果傳送給商戶。
*/
@PostMapping("/refunds/notify")
public String callback(HttpServletRequest request, HttpServletResponse response){
log.info("退款通知執行");
Map<String, String> map = new HashMap<>();//應答物件
try {
/*使用回撥通知請求的資料,構建 RequestParam。
HTTP 頭 Wechatpay-Signature
HTTP 頭 Wechatpay-Nonce
HTTP 頭 Wechatpay-Timestamp
HTTP 頭 Wechatpay-Serial
HTTP 頭 Wechatpay-Signature-Type
HTTP 請求體 body。切記使用原始報文,不要用 JSON 物件序列化後的字串,避免驗籤的 body 和原文不一致。*/
// 構造 RequestParam
String signature = request.getHeader("Wechatpay-Signature");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");
//請求體
String requestBody = RequestUtils.readData(request);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(wechatPayCertificateSerialNumber)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.body(requestBody)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
// 驗籤、解密並轉換成 Transaction
RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);
String orderTradeNo = refundNotification.getOutTradeNo();
Status refundStatus = refundNotification.getRefundStatus();
if("SUCCESS".equals(refundStatus.toString())){
log.info("更新退款記錄:已退款");
//退款狀態
refundInfoService.updateRefundInfoStatus(refundNotification, RefundStatusEnum.REFUND);
//訂單狀態
orderInfoService.updateStatus(orderTradeNo, OrderStatusEnum.CANCLE_REFUND.getStatus());
}
//成功應答
response.setStatus(200);
map.put("code", "SUCCESS");
return JSONObject.toJSONString(map);
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
//失敗應答
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "失敗");
return JSONObject.toJSONString(map);
}
}
}
本文來自部落格園,作者:自律即自由-,轉載請註明原文連結:https://www.cnblogs.com/deyo/p/17495586.html