|  |  | 
 |  |  | package com.dy.pipIrrSell.wechatpay; | 
 |  |  |  | 
 |  |  | import com.alibaba.fastjson2.JSON; | 
 |  |  | import com.alibaba.fastjson2.JSONObject; | 
 |  |  | import com.dy.common.aop.SsoAop; | 
 |  |  | import com.dy.common.webUtil.BaseResponse; | 
 |  |  | 
 |  |  | import com.dy.pipIrrSell.virtualCard.enums.LastOperateENUM; | 
 |  |  | import com.dy.pipIrrSell.virtualCard.enums.RefundItemStateENUM; | 
 |  |  | import com.dy.pipIrrSell.wallet.enums.RefundStatusENUM; | 
 |  |  | import com.dy.pipIrrSell.wechatpay.dto.Code2Session; | 
 |  |  | import com.dy.pipIrrSell.wechatpay.dto.DtoOrder; | 
 |  |  | import com.dy.pipIrrSell.wechatpay.dto.NotifyResource; | 
 |  |  | import com.dy.pipIrrSell.wechatpay.dto.OrderNotify; | 
 |  |  | import io.swagger.v3.oas.annotations.Operation; | 
 |  |  | import io.swagger.v3.oas.annotations.media.Content; | 
 |  |  | 
 |  |  | import io.swagger.v3.oas.annotations.responses.ApiResponse; | 
 |  |  | import io.swagger.v3.oas.annotations.responses.ApiResponses; | 
 |  |  | import io.swagger.v3.oas.annotations.tags.Tag; | 
 |  |  | import jakarta.servlet.http.HttpServletRequest; | 
 |  |  | import jakarta.servlet.http.HttpServletResponse; | 
 |  |  | import jakarta.validation.Valid; | 
 |  |  | import lombok.RequiredArgsConstructor; | 
 |  |  | 
 |  |  | import org.springframework.web.bind.annotation.*; | 
 |  |  |  | 
 |  |  | import javax.crypto.NoSuchPaddingException; | 
 |  |  | import java.io.BufferedReader; | 
 |  |  | import java.io.IOException; | 
 |  |  | import java.security.GeneralSecurityException; | 
 |  |  | import java.security.InvalidKeyException; | 
 |  |  | 
 |  |  |  | 
 |  |  |     // 平台证书公钥 | 
 |  |  |     private final Map CERTIFICATE_MAP = new HashMap(); | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 登录凭证校验 | 
 |  |  |      * @param code2Session 登录凭证校验传入对象 | 
 |  |  |      * @param bindingResult | 
 |  |  |      * @return | 
 |  |  |      * @throws Exception | 
 |  |  |      */ | 
 |  |  |     @Operation(summary = "登录凭证校验", description = "登录凭证校验") | 
 |  |  |     @ApiResponses(value = { | 
 |  |  |             @ApiResponse( | 
 |  |  |                     responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, | 
 |  |  |                     description = "操作结果:true:成功,false:失败(BaseResponse.content)", | 
 |  |  |                     content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, | 
 |  |  |                             schema = @Schema(implementation = Boolean.class))} | 
 |  |  |             ) | 
 |  |  |     }) | 
 |  |  |     @PostMapping(path = "getSessionId") | 
 |  |  |     @Transactional(rollbackFor = Exception.class) | 
 |  |  |     @SsoAop() | 
 |  |  |     public BaseResponse<Boolean> getSessionId(@RequestBody @Valid Code2Session code2Session, BindingResult bindingResult) throws Exception { | 
 |  |  |         if(bindingResult != null && bindingResult.hasErrors()){ | 
 |  |  |             return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         Map<String, Object> queryParams = new HashMap<>(); | 
 |  |  |         queryParams.put("appid", appid); | 
 |  |  |         queryParams.put("secret", code2Session.getSecret()); | 
 |  |  |         queryParams.put("js_code", code2Session.getJs_code()); | 
 |  |  |         queryParams.put("grant_type", grantType); | 
 |  |  |         Map<String, String> headerParams = new HashMap<>(); | 
 |  |  |         JSONObject job = restTemplateUtil.get(loginUrl, queryParams, headerParams); | 
 |  |  |  | 
 |  |  |         if(job.getLong("errcode") != null && job.getLong("errcode") >= -1) { | 
 |  |  |             return BaseResponseUtils.buildFail("登录凭证校验失败"); | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         String openid = job.getString("openid"); | 
 |  |  |         String sessionKey = job.getString("session_key"); | 
 |  |  |  | 
 |  |  |         // 添加登录态记录 | 
 |  |  |         SeWebchatLogonState po = new SeWebchatLogonState(); | 
 |  |  |         po.setOpenId(openid); | 
 |  |  |         po.setSessionKey(sessionKey); | 
 |  |  |         Date createTime = new Date(); | 
 |  |  |         po.setCreateTime(createTime); | 
 |  |  |         Long id = paymentSv.insert(po); | 
 |  |  |         if(id == null || id <= 0) { | 
 |  |  |             return BaseResponseUtils.buildFail("登录态记录添加失败"); | 
 |  |  |         } | 
 |  |  |         String SessionId = String.valueOf(id); | 
 |  |  |  | 
 |  |  |         return BaseResponseUtils.buildSuccess(SessionId) ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 下载微信支付平台证书 测试完废除 | 
 |  |  | 
 |  |  |         if(bindingResult != null && bindingResult.hasErrors()){ | 
 |  |  |             return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); | 
 |  |  |         } | 
 |  |  |         // 接收参数:登录态ID、农户ID、虚拟卡ID、充值金额 | 
 |  |  |         // 接收参数:登录态ID、虚拟卡ID、充值金额(分) | 
 |  |  |         Long sessionId = order.getSessionId(); | 
 |  |  |         Long virtualId = order.getVirtualId(); | 
 |  |  |         Long virtualId = order.getVcId(); | 
 |  |  |         Integer rechargeAmount = order.getRechargeAmount(); | 
 |  |  |  | 
 |  |  |         String prepayId = ""; | 
 |  |  |         //SeWebchatLogonState po = paymentSv.selectOne(Long.parseLong(sessionId)); | 
 |  |  |         //String openid = po.getOpenId(); | 
 |  |  |  | 
 |  |  |         SeOpenId po = paymentSv.selectOne(sessionId); | 
 |  |  |         String openid = po.getOpenId(); | 
 |  |  | 
 |  |  |  | 
 |  |  |         //订单金额 | 
 |  |  |         JSONObject job_amount = new JSONObject(); | 
 |  |  |         job_amount.put("total", 1); | 
 |  |  |         job_amount.put("total", rechargeAmount); | 
 |  |  |         job_amount.put("currency", "CNY"); | 
 |  |  |         job_body.put("amount", job_amount); | 
 |  |  |  | 
 |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 再次签名 | 
 |  |  |      * @param prepayId 预支付交易会话标识 | 
 |  |  |      * @return 小程序调起支付参数 | 
 |  |  |      * @throws Exception | 
 |  |  |      */ | 
 |  |  |     @Operation(summary = "再次签名", description = "再次签名") | 
 |  |  |     @ApiResponses(value = { | 
 |  |  |             @ApiResponse( | 
 |  |  |                     responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, | 
 |  |  |                     description = "操作结果:true:成功,false:失败(BaseResponse.content)", | 
 |  |  |                     content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, | 
 |  |  |                             schema = @Schema(implementation = Boolean.class))} | 
 |  |  |             ) | 
 |  |  |     }) | 
 |  |  |     @GetMapping(path = "/signAgain") | 
 |  |  |     @Transactional(rollbackFor = Exception.class) | 
 |  |  |     @SsoAop() | 
 |  |  |     public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId") String prepayId) throws Exception { | 
 |  |  |  | 
 |  |  |         // 获取随机串和时间戳,放在此处以保证 | 
 |  |  |         String appid = PayInfo.appid; | 
 |  |  |         String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); | 
 |  |  |         String nonceStr = payHelper.generateRandomString(); | 
 |  |  |         String pkg = "prepay_id=" + prepayId; | 
 |  |  |         String message = payHelper.buildMessage_signAgain(appid, timeStamp, nonceStr, pkg); | 
 |  |  |         String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName); | 
 |  |  |  | 
 |  |  |         JSONObject job_result = new JSONObject(); | 
 |  |  |         job_result.put("timeStamp", timeStamp); | 
 |  |  |         job_result.put("nonceStr", nonceStr); | 
 |  |  |         job_result.put("package", pkg); | 
 |  |  |         job_result.put("signType", signType); | 
 |  |  |         job_result.put("paySign", paySign); | 
 |  |  |  | 
 |  |  |         return BaseResponseUtils.buildSuccess(job_result) ; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 支付通知/退款结果通知 | 
 |  |  |      * @param headers | 
 |  |  |      * @param orderNotify | 
 |  |  |      * @param request | 
 |  |  |      * @param response | 
 |  |  |      * @return | 
 |  |  |      * @throws IOException | 
 |  |  | 
 |  |  |     @PostMapping(path = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE) | 
 |  |  |     @Transactional(rollbackFor = Exception.class) | 
 |  |  |     @SsoAop() | 
 |  |  |     public JSONObject orderNotify(@RequestHeader HttpHeaders headers, @RequestBody OrderNotify orderNotify, HttpServletResponse response) throws IOException, GeneralSecurityException { | 
 |  |  |     public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException { | 
 |  |  |         JSONObject result = new JSONObject(); | 
 |  |  |  | 
 |  |  |         /** | 
 |  |  |          * 1.验签处理 | 
 |  |  |          *      从header中取出4个子参数,同时取出body | 
 |  |  |          *      从header中取出4个子参数 | 
 |  |  |          *      验时间差,超过5分钟的不处理 | 
 |  |  |          *      验证签名 | 
 |  |  |          *      验证书序列号,必须与某一个证书的序列号一致 | 
 |  |  | 
 |  |  |         String wechatpaySerial = String.valueOf(headers.get("Wechatpay-Serial").get(0)); | 
 |  |  |         String wechatpaySignature = String.valueOf(headers.get("Wechatpay-Signature").get(0)); | 
 |  |  |         String wechatpayTimestamp = String.valueOf(headers.get("Wechatpay-Timestamp").get(0)); | 
 |  |  |         String bodyStr = JSONObject.toJSONString(orderNotify); | 
 |  |  |  | 
 |  |  |         // 获取body内容 | 
 |  |  |         BufferedReader reader = request.getReader(); | 
 |  |  |         StringBuilder stringBuilder = new StringBuilder(); | 
 |  |  |         String line; | 
 |  |  |         while ((line = reader.readLine()) != null) { | 
 |  |  |             stringBuilder.append(line); | 
 |  |  |         } | 
 |  |  |         String bodyStr = stringBuilder.toString(); | 
 |  |  |  | 
 |  |  |         // body转对象 | 
 |  |  |         OrderNotify orderNotify = JSON.parseObject(bodyStr, OrderNotify.class); | 
 |  |  |  | 
 |  |  |         // 验时间戳,时间差大于5分钟的拒绝 | 
 |  |  |         Long timeDiff = (System.currentTimeMillis() / 1000 - Long.parseLong(wechatpayTimestamp))/60; | 
 |  |  | 
 |  |  |             return result; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         /** | 
 |  |  |          * 解密处理 | 
 |  |  |          *      1 | 
 |  |  |          */ | 
 |  |  |         // 解密处理 | 
 |  |  |         String eventType = orderNotify.getEvent_type(); | 
 |  |  |  | 
 |  |  |         if(eventType != null && eventType.equals("TRANSACTION.SUCCESS")) { | 
 |  |  | 
 |  |  |              * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext | 
 |  |  |              * 解密ciphertext得到 | 
 |  |  |              */ | 
 |  |  |             OrderNotify.NotifyResource notifyResource = orderNotify.getResource(); | 
 |  |  |             NotifyResource notifyResource = orderNotify.getResource(); | 
 |  |  |             String associatedData = notifyResource.getAssociated_data(); | 
 |  |  |             String nonce = notifyResource.getNonce(); | 
 |  |  |             String ciphertext = notifyResource.getCiphertext(); | 
 |  |  | 
 |  |  |             String resource = AesUtil.decryptToString(PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); | 
 |  |  |             JSONObject job_resource = JSONObject.parseObject(resource); | 
 |  |  |  | 
 |  |  |             // 解密后取出:商户订单员、微信支付订单号、交易状态、支付完成时间 | 
 |  |  |             // 解密后取出:商户订单号、微信支付订单号、交易状态、支付完成时间 | 
 |  |  |             String out_trade_no = job_resource.getString("out_trade_no"); | 
 |  |  |             String transaction_id = job_resource.getString("transaction_id"); | 
 |  |  |             String trade_state = job_resource.getString("trade_state"); | 
 |  |  |             Date success_time = job_resource.getDate("success_time"); | 
 |  |  |  | 
 |  |  |             // 更新虚拟卡表及充值表响应字段 | 
 |  |  |             BaseResponse result_ = virtualCardSv.updateVCRecharge(out_trade_no, success_time); | 
 |  |  |             if(!result_.getCode().equals("0001")) { | 
 |  |  |                 response.setStatus(500); | 
 |  |  |                 result.put("code", "FAIL"); | 
 |  |  |                 result.put("message", "失败"); | 
 |  |  |                 return result; | 
 |  |  |             // 如果当前订单状态为未支付状态,则更新虚拟卡表及充值表响应字段 | 
 |  |  |             SeVcRecharge seVcRecharge = virtualCardSv.getVCRechargeByorderNumber(out_trade_no); | 
 |  |  |             if(seVcRecharge != null && seVcRecharge.getOrderState() == 1) { | 
 |  |  |                 BaseResponse result_ = virtualCardSv.updateVCRecharge(out_trade_no, success_time); | 
 |  |  |                 if(!result_.getCode().equals("0001")) { | 
 |  |  |                     response.setStatus(500); | 
 |  |  |                     result.put("code", "FAIL"); | 
 |  |  |                     result.put("message", "失败"); | 
 |  |  |                     return result; | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } else if(eventType != null && eventType.equals("REFUND.SUCCESS")) { | 
 |  |  |             // 退款成功后回调 | 
 |  |  | 
 |  |  |              * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext | 
 |  |  |              * 解密ciphertext得到 | 
 |  |  |              */ | 
 |  |  |             OrderNotify.NotifyResource notifyResource = orderNotify.getResource(); | 
 |  |  |             NotifyResource notifyResource = orderNotify.getResource(); | 
 |  |  |             String associatedData = notifyResource.getAssociated_data(); | 
 |  |  |             String nonce = notifyResource.getNonce(); | 
 |  |  |             String ciphertext = notifyResource.getCiphertext(); | 
 |  |  | 
 |  |  |         result.put("code", "SUCCESS"); | 
 |  |  |         result.put("message", "成功"); | 
 |  |  |         return  result; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 再次签名 | 
 |  |  |      * @param prepayId 预支付交易会话标识 | 
 |  |  |      * @return 小程序调起支付参数 | 
 |  |  |      * @throws Exception | 
 |  |  |      */ | 
 |  |  |     @Operation(summary = "再次签名", description = "再次签名") | 
 |  |  |     @ApiResponses(value = { | 
 |  |  |             @ApiResponse( | 
 |  |  |                     responseCode = ResultCodeMsg.RsCode.SUCCESS_CODE, | 
 |  |  |                     description = "操作结果:true:成功,false:失败(BaseResponse.content)", | 
 |  |  |                     content = {@Content(mediaType = MediaType.APPLICATION_JSON_VALUE, | 
 |  |  |                             schema = @Schema(implementation = Boolean.class))} | 
 |  |  |             ) | 
 |  |  |     }) | 
 |  |  |     @GetMapping(path = "/signAgain") | 
 |  |  |     @Transactional(rollbackFor = Exception.class) | 
 |  |  |     @SsoAop() | 
 |  |  |     public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId")  String prepayId) throws Exception { | 
 |  |  |  | 
 |  |  |         // 获取随机串和时间戳,放在此处以保证 | 
 |  |  |         String appid = PayInfo.appid; | 
 |  |  |         String timestamp = String.valueOf(System.currentTimeMillis() / 1000); | 
 |  |  |         String nonceStr = payHelper.generateRandomString(); | 
 |  |  |         String pkg = "prepay_id=" + prepayId; | 
 |  |  |         String message = payHelper.buildMessage_signAgain(appid, timestamp, nonceStr, pkg); | 
 |  |  |         String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName); | 
 |  |  |  | 
 |  |  |         JSONObject job_result = new JSONObject(); | 
 |  |  |         job_result.put("timestamp", timestamp); | 
 |  |  |         job_result.put("nonceStr", nonceStr); | 
 |  |  |         job_result.put("package", pkg); | 
 |  |  |         job_result.put("signType", signType); | 
 |  |  |         job_result.put("paySign", paySign); | 
 |  |  |  | 
 |  |  |         return BaseResponseUtils.buildSuccess(job_result) ; | 
 |  |  |     } | 
 |  |  | } |