|
@@ -0,0 +1,235 @@
|
|
|
+package com.miaxis.app.controller.wx;
|
|
|
+
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.miaxis.common.config.WxpayConfig;
|
|
|
+import com.miaxis.common.constant.Constants;
|
|
|
+import com.miaxis.common.exception.CustomException;
|
|
|
+import com.miaxis.common.utils.AesUtil;
|
|
|
+import com.miaxis.wx.domain.RefundRecord;
|
|
|
+import com.miaxis.wx.domain.WxOrder;
|
|
|
+import com.miaxis.wx.dto.WxNotifyReturnDTO;
|
|
|
+import com.miaxis.wx.dto.WxpayNotifyDTO;
|
|
|
+import com.miaxis.wx.service.IRefundRecordService;
|
|
|
+import com.miaxis.wx.service.IWxOrderService;
|
|
|
+import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
|
|
|
+import io.swagger.annotations.Api;
|
|
|
+import io.swagger.annotations.ApiOperation;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.joda.time.DateTime;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.util.Base64Utils;
|
|
|
+import org.springframework.web.bind.annotation.PostMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestBody;
|
|
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.security.*;
|
|
|
+import java.security.cert.X509Certificate;
|
|
|
+import java.util.Random;
|
|
|
+
|
|
|
+@RestController
|
|
|
+@RequiredArgsConstructor
|
|
|
+@RequestMapping(Constants.OPEN_PREFIX+"/wx/notify")
|
|
|
+@Api(tags = {"【H5-微信回调】"})
|
|
|
+@Slf4j
|
|
|
+public class WxNotifyController {
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WxpayConfig wxpayConfig;
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IWxOrderService wxOrderService;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IRefundRecordService refundRecordService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private AutoUpdateCertificatesVerifier verifier;
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信支付回调接口
|
|
|
+ */
|
|
|
+ @PostMapping(value = "/wxpay")
|
|
|
+ @ApiOperation("微信支付回调")
|
|
|
+ public WxNotifyReturnDTO wxpayNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
|
|
|
+ String bodyString = getBodyString(request);
|
|
|
+ if (!validate(request,bodyString)){
|
|
|
+ throw new CustomException("签名失败");
|
|
|
+ }
|
|
|
+ String resourceString = getSourString(wxpayNotifyDTO);
|
|
|
+ log.info(resourceString);
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(resourceString);
|
|
|
+ //将回调数据写入数据库
|
|
|
+ String outTradeNo = writeNotifyDataToDb(jsonObject);
|
|
|
+ WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
|
|
|
+ wxNotifyReturnDTO.setCode("SUCCESS");
|
|
|
+ wxNotifyReturnDTO.setMessage("成功");
|
|
|
+ return wxNotifyReturnDTO;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ private String getBodyString(HttpServletRequest request) {
|
|
|
+ BufferedReader br = null;
|
|
|
+ StringBuilder sb = new StringBuilder("");
|
|
|
+ try
|
|
|
+ {
|
|
|
+ br = request.getReader();
|
|
|
+ String str;
|
|
|
+ while ((str = br.readLine()) != null)
|
|
|
+ {
|
|
|
+ sb.append(str);
|
|
|
+ }
|
|
|
+ br.close();
|
|
|
+ }
|
|
|
+ catch (IOException e)
|
|
|
+ {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ if (null != br)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ br.close();
|
|
|
+ }
|
|
|
+ catch (IOException e)
|
|
|
+ {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sb.toString();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private Boolean validate(HttpServletRequest request, String bodyString) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
|
|
+ String sign = request.getHeader("Wechatpay-Signature");
|
|
|
+ String timestamp = request.getHeader("Wechatpay-Timestamp");
|
|
|
+ String nonce = request.getHeader("Wechatpay-Nonce");
|
|
|
+
|
|
|
+ StringBuffer sb = new StringBuffer();
|
|
|
+ sb.append(timestamp + "\n");
|
|
|
+ sb.append(nonce + "\n");
|
|
|
+ sb.append(bodyString + "\n");
|
|
|
+ X509Certificate validCertificate = verifier.getValidCertificate();
|
|
|
+ // 进行签名服务
|
|
|
+ Signature signature = Signature.getInstance("SHA256withRSA");
|
|
|
+ // 用微信平台公钥对签名器进行初始化
|
|
|
+ signature.initVerify(validCertificate);
|
|
|
+ // 把我们构造的验签名串更新到签名器中
|
|
|
+ signature.update(sb.toString().getBytes(StandardCharsets.UTF_8));
|
|
|
+ Boolean result = signature.verify(Base64Utils.decodeFromString(sign));
|
|
|
+ log.info("微信支付回调验签:"+result.toString());
|
|
|
+ return result;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信退款回调
|
|
|
+ */
|
|
|
+ @PostMapping(value = "/refund")
|
|
|
+ @ApiOperation("微信退款回调")
|
|
|
+ public WxNotifyReturnDTO refundNotify(@RequestBody WxpayNotifyDTO wxpayNotifyDTO, HttpServletRequest request) throws GeneralSecurityException, IOException {
|
|
|
+ String bodyString = getBodyString(request);
|
|
|
+ if (!validate(request,bodyString)){
|
|
|
+ throw new CustomException("签名失败");
|
|
|
+ }
|
|
|
+ String resourceString = getSourString(wxpayNotifyDTO);
|
|
|
+ log.info(resourceString);
|
|
|
+ JSONObject jsonObject = JSONObject.parseObject(resourceString);
|
|
|
+ //将回调数据写入数据库
|
|
|
+ writeRefundNotifyDataToDb(jsonObject);
|
|
|
+
|
|
|
+ WxNotifyReturnDTO wxNotifyReturnDTO = new WxNotifyReturnDTO();
|
|
|
+ wxNotifyReturnDTO.setCode("SUCCESS");
|
|
|
+ wxNotifyReturnDTO.setMessage("成功");
|
|
|
+
|
|
|
+ return wxNotifyReturnDTO;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void writeRefundNotifyDataToDb(JSONObject jsonObject) {
|
|
|
+ String refundId = jsonObject.getString("refund_id");
|
|
|
+ RefundRecord refundRecord = refundRecordService.getByRefundId(refundId);
|
|
|
+ if (refundRecord == null) {
|
|
|
+ throw new CustomException("该退款订单不存在");
|
|
|
+ }
|
|
|
+ refundRecord.setTransactionId(jsonObject.getString("transaction_id"));
|
|
|
+ refundRecord.setUserReceivedAccount(jsonObject.getString("user_received_account"));
|
|
|
+ refundRecord.setStatus(jsonObject.getString("refund_status"));
|
|
|
+ refundRecordService.updateById(refundRecord);
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public String writeNotifyDataToDb(JSONObject jsonObject) {
|
|
|
+ String outTradeNo = jsonObject.getString("out_trade_no");
|
|
|
+ WxOrder wxOrder = wxOrderService.getByOutTradeNo(outTradeNo);
|
|
|
+ if (wxOrder == null) {
|
|
|
+ throw new CustomException("该订单不存在");
|
|
|
+ }
|
|
|
+ wxOrder.setTransactionId(jsonObject.getString("transaction_id"));
|
|
|
+ JSONObject amount = jsonObject.getJSONObject("amount");
|
|
|
+ wxOrder.setPayerTotal(amount.getInteger("payer_total"));
|
|
|
+ wxOrder.setTotal(amount.getInteger("total"));
|
|
|
+ wxOrder.setCurrency(amount.getString("currency"));
|
|
|
+ wxOrder.setPayerCurrency(amount.getString("payer_currency"));
|
|
|
+ wxOrder.setTradeState(jsonObject.getString("trade_state"));
|
|
|
+ wxOrder.setBankType(jsonObject.getString("bank_type"));
|
|
|
+ DateTime dateTime = new DateTime(jsonObject.getString("success_time"));
|
|
|
+ wxOrder.setSuccessTime(dateTime.toDate());
|
|
|
+ wxOrder.setTradeStateDesc(jsonObject.getString("trade_state_desc"));
|
|
|
+ wxOrder.setTradeType(jsonObject.getString("trade_type"));
|
|
|
+ wxOrder.setAttach(jsonObject.getString("attach"));
|
|
|
+ JSONObject sceneInfo = jsonObject.getJSONObject("scene_info");
|
|
|
+ if (sceneInfo != null){
|
|
|
+ wxOrder.setDeviceId(sceneInfo.getString("device_id"));
|
|
|
+ }
|
|
|
+ wxOrderService.updateById(wxOrder);
|
|
|
+ return outTradeNo;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private String getSourString(WxpayNotifyDTO wxpayNotifyDTO) throws GeneralSecurityException, IOException {
|
|
|
+ AesUtil aesUtil = new AesUtil(wxpayConfig.getV3key().getBytes());
|
|
|
+ WxpayNotifyDTO.WxpaySource wxpaySource = wxpayNotifyDTO.getResource();
|
|
|
+ return aesUtil.decryptToString(wxpaySource.getAssociated_data().getBytes(), wxpaySource.getNonce().getBytes(), wxpaySource.getCiphertext());
|
|
|
+ }
|
|
|
+
|
|
|
+ private String randomVipcode() {
|
|
|
+ String result = "";
|
|
|
+ Random random = new Random();
|
|
|
+ for(int i =0 ;i <11 ;i ++){
|
|
|
+ result+=random.nextInt(10);
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|