New file |
| | |
| | | package com.dy.pipIrrWechat.wechatpay; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.dy.common.multiDataSource.DataSourceContext; |
| | | import com.dy.common.util.AES; |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.common.webUtil.ResultCodeMsg; |
| | | import com.dy.pipIrrGlobal.cert.WxCertUtil; |
| | | import com.dy.pipIrrGlobal.pojoSe.*; |
| | | import com.dy.pipIrrGlobal.voSe.VoClient; |
| | | import com.dy.pipIrrWechat.result.WechatResultCode; |
| | | import com.dy.pipIrrWechat.util.AesUtil; |
| | | import com.dy.pipIrrWechat.util.PayHelper; |
| | | import com.dy.pipIrrWechat.util.RestTemplateUtil; |
| | | import com.dy.pipIrrWechat.virtualCard.VirtualCardSv; |
| | | import com.dy.pipIrrWechat.virtualCard.dto.DtoVirtualCard; |
| | | import com.dy.pipIrrWechat.virtualCard.enums.LastOperateENUM; |
| | | import com.dy.pipIrrWechat.virtualCard.enums.RefundItemStateENUM; |
| | | import com.dy.pipIrrWechat.wechatpay.dto.*; |
| | | import com.dy.pipIrrWechat.wechatpay.enums.RefundStatusENUM; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.media.Content; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | 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 lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.BindingResult; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | | import java.security.InvalidKeyException; |
| | | import java.security.NoSuchAlgorithmException; |
| | | import java.security.SignatureException; |
| | | import java.security.spec.InvalidKeySpecException; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * @author ZhuBaoMin |
| | | * @date 2024-07-16 15:05 |
| | | * @LastEditTime 2024-07-16 15:05 |
| | | * @Description |
| | | */ |
| | | |
| | | @Slf4j |
| | | @Tag(name = "微信支付管理", description = "微信支付各种操作") |
| | | @RestController |
| | | @RequestMapping(path="payment") |
| | | @RequiredArgsConstructor |
| | | public class PaymentCtrl { |
| | | private final ResourceLoader resourceLoader; |
| | | private final PaymentSv paymentSv; |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | private final PayHelper payHelper; |
| | | private final VirtualCardSv virtualCardSv; |
| | | //private final String appid = com.dy.pipIrrWechat.wechatpay.PayInfo.appid; |
| | | //private final String secret = com.dy.pipIrrWechat.wechatpay.PayInfo.secret; |
| | | //private final String mchid = com.dy.pipIrrWechat.wechatpay.PayInfo.mchid; |
| | | //private final String schema = com.dy.pipIrrWechat.wechatpay.PayInfo.schema; |
| | | //private final String signType = com.dy.pipIrrWechat.wechatpay.PayInfo.signType; |
| | | //private final String description = com.dy.pipIrrWechat.wechatpay.PayInfo.description; |
| | | //private final String loginUrl = com.dy.pipIrrWechat.wechatpay.PayInfo.loginUrl; |
| | | //private final String notifyUrl = com.dy.pipIrrWechat.wechatpay.PayInfo.notifyUrl; |
| | | //private final String grantType = com.dy.pipIrrWechat.wechatpay.PayInfo.grantType; |
| | | |
| | | // 平台证书公钥 |
| | | 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) |
| | | 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()); |
| | | } |
| | | |
| | | String phoneNumber = code2Session.getPhoneNumber(); |
| | | String jsCode = code2Session.getJs_code(); |
| | | |
| | | Map<String, Object> queryParams = new HashMap<>(); |
| | | queryParams.put("appid", PayInfo.appid); |
| | | queryParams.put("secret", PayInfo.secret); |
| | | queryParams.put("js_code", jsCode); |
| | | queryParams.put("grant_type", com.dy.pipIrrWechat.wechatpay.PayInfo.grantType); |
| | | Map<String, String> headerParams = new HashMap<>(); |
| | | JSONObject job = restTemplateUtil.get(com.dy.pipIrrWechat.wechatpay.PayInfo.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"); |
| | | |
| | | Long clientId = paymentSv.getClientIdByPhone(phoneNumber); |
| | | String SessionId = ""; |
| | | if(clientId != null) { |
| | | // 添加微信用户账户记录 |
| | | SeOpenId seOpenId = new SeOpenId(); |
| | | seOpenId.setClientId(clientId); |
| | | seOpenId.setOpenId(openid); |
| | | seOpenId.setSessionKey(sessionKey); |
| | | seOpenId.setCreateTime(new Date()); |
| | | Long rec = paymentSv.addOpenId(seOpenId); |
| | | if(rec != null) { |
| | | SessionId = String.valueOf(rec); |
| | | } |
| | | return BaseResponseUtils.buildSuccess(SessionId); |
| | | |
| | | } else { |
| | | return BaseResponseUtils.buildError(WechatResultCode.PHONE_NUMBER_IS_ERROR.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 下载微信支付平台证书 测试完废除 |
| | | * @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 = "certificates") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public BaseResponse<Boolean> certificates() throws Exception { |
| | | String method = "GET"; |
| | | String httpUrl = "/v3/certificates"; |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String header = com.dy.pipIrrWechat.wechatpay.PayInfo.schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | JSONObject job_result = restTemplateUtil.getHeaders(com.dy.pipIrrWechat.wechatpay.PayInfo.certificates,null, headers); |
| | | JSONObject job_headers = job_result.getJSONObject("headers"); |
| | | String wechatpayNonce = job_headers.getJSONArray("Wechatpay-Nonce").getString(0); |
| | | String wechatpaySerial = job_headers.getJSONArray("Wechatpay-Serial").getString(0); |
| | | String wechatpaySignature = job_headers.getJSONArray("Wechatpay-Signature").getString(0); |
| | | String wechatpaySignatureType = job_headers.getJSONArray("Wechatpay-Signature-Type").getString(0); |
| | | String wechatpayTimestamp = job_headers.getJSONArray("Wechatpay-Timestamp").getString(0); |
| | | |
| | | JSONObject job_body = job_result.getJSONObject("body"); |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, job_body.toJSONString()); |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, certFileBs); |
| | | |
| | | return BaseResponseUtils.buildSuccess(); |
| | | } |
| | | |
| | | /** |
| | | * JSAPI下单 |
| | | * @param order 下单请求对象,包含需要传入的参数 |
| | | * @param bindingResult |
| | | * @return 预支付交易会话标识(有效期2小时) |
| | | */ |
| | | @PostMapping(path = "placeOrder") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | | |
| | | // 接收参数:登录态ID、虚拟卡ID、充值金额(单位元) |
| | | Long sessionId = order.getSessionId(); |
| | | Long virtualId = order.getVcId(); |
| | | //Integer rechargeAmount = (int)(order.getRechargeAmount()*100); |
| | | |
| | | String prepayId = ""; |
| | | |
| | | SeOpenId po = paymentSv.selectOne(sessionId); |
| | | if(po == null) { |
| | | return BaseResponseUtils.buildErrorMsg(WechatResultCode.SESSION_ID_ERROR.getMessage()); |
| | | } |
| | | String openid = po.getOpenId(); |
| | | |
| | | SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(virtualId); |
| | | if(seVirtualCard == null) { |
| | | return BaseResponseUtils.buildErrorMsg(WechatResultCode.VIRTUAL_CARD_NOT_EXIST.getMessage()); |
| | | } |
| | | Long clientId = seVirtualCard.getClientId(); |
| | | |
| | | VoClient voClient = paymentSv.getOneClient(clientId); |
| | | if(voClient == null) { |
| | | return BaseResponseUtils.buildErrorMsg(WechatResultCode.VIRTUAL_CARD_CLIENT_NOT_EXIST.getMessage()); |
| | | } |
| | | String clientNum = voClient.getClientNum(); |
| | | |
| | | // 生成订单号并添加充值记录 |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS"); |
| | | String orderNumber = clientNum + dateFormat.format(new Date()); |
| | | |
| | | // 生成虚拟卡充值记录(部分字段) |
| | | DtoVirtualCard virtualCard = new DtoVirtualCard(); |
| | | virtualCard.setVirtualId(virtualId); |
| | | virtualCard.setClientId(clientId); |
| | | virtualCard.setOrderNumber(orderNumber); |
| | | //virtualCard.setRechargeAmount(rechargeAmount); |
| | | virtualCard.setRechargeAmount(order.getRechargeAmount()); |
| | | |
| | | // -1:虚拟卡不存在,0:添加充值记录失败 |
| | | Long rechargeId = virtualCardSv.insertVCRecharge(virtualCard); |
| | | if(rechargeId.equals(-1)) { |
| | | return BaseResponseUtils.buildFail(WechatResultCode.NO_ACCOUNT.getMessage()); |
| | | } |
| | | if(rechargeId.equals(0)) { |
| | | return BaseResponseUtils.buildFail(WechatResultCode.RECHARGE_FAIL.getMessage()); |
| | | } |
| | | |
| | | // 生成充值操作记录,充值操作人为农户 |
| | | SeVcOperate seVcOperate = new SeVcOperate(); |
| | | seVcOperate.setVcId(virtualId); |
| | | seVcOperate.setClientId(clientId); |
| | | seVcOperate.setOperateType(LastOperateENUM.RECHARGE.getCode()); |
| | | seVcOperate.setRechargeId(rechargeId); |
| | | seVcOperate.setOperator(clientId); |
| | | seVcOperate.setOperateTime(new Date()); |
| | | Long vcOperateId = virtualCardSv.insertVcOperate(seVcOperate); |
| | | if(vcOperateId == null) { |
| | | return BaseResponseUtils.buildErrorMsg(WechatResultCode.RECHARGE_ADD_FAIL.getMessage()); |
| | | } |
| | | |
| | | JSONObject job_body = new JSONObject(); |
| | | job_body.put("appid", PayInfo.appid); |
| | | job_body.put("mchid", PayInfo.mchid); |
| | | job_body.put("description", com.dy.pipIrrWechat.wechatpay.PayInfo.description); |
| | | job_body.put("out_trade_no", orderNumber); |
| | | job_body.put("attach", DataSourceContext.get()); |
| | | job_body.put("notify_url", com.dy.pipIrrWechat.wechatpay.PayInfo.notifyUrl); |
| | | |
| | | //订单金额 |
| | | JSONObject job_amount = new JSONObject(); |
| | | job_amount.put("total", (int)(order.getRechargeAmount()*100)); |
| | | job_amount.put("currency", "CNY"); |
| | | job_body.put("amount", job_amount); |
| | | |
| | | //支付者 |
| | | JSONObject job_payer = new JSONObject(); |
| | | job_payer.put("openid", openid); |
| | | job_body.put("payer", job_payer); |
| | | |
| | | // 获取随机串和时间戳 |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String method = "POST"; |
| | | String httpUrl = "/v3/pay/transactions/jsapi"; |
| | | |
| | | String body = job_body.toJSONString(); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String header = com.dy.pipIrrWechat.wechatpay.PayInfo.schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | | headers.put("Accept", "application/json"); |
| | | headers.put("Content-Type", "application/json"); |
| | | |
| | | // 暂时注释掉,认证通过后再放开 |
| | | JSONObject job_result = restTemplateUtil.post(com.dy.pipIrrWechat.wechatpay.PayInfo.orderUrl, body, headers); |
| | | if(job_result == null) { |
| | | return BaseResponseUtils.buildFail(WechatResultCode.RECHARGE_ADD_FAIL.getMessage()); |
| | | } |
| | | |
| | | return BaseResponseUtils.buildSuccess(job_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) |
| | | public BaseResponse<JSONObject> signAgain(@RequestParam("prepayId") String prepayId) throws Exception { |
| | | |
| | | // 获取随机串和时间戳,放在此处以保证 |
| | | String appid = com.dy.pipIrrWechat.wechatpay.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); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), certFileBs); |
| | | |
| | | JSONObject job_result = new JSONObject(); |
| | | job_result.put("timeStamp", timeStamp); |
| | | job_result.put("nonceStr", nonceStr); |
| | | job_result.put("package", pkg); |
| | | job_result.put("signType", com.dy.pipIrrWechat.wechatpay.PayInfo.signType); |
| | | job_result.put("paySign", paySign); |
| | | |
| | | return BaseResponseUtils.buildSuccess(job_result) ; |
| | | } |
| | | |
| | | /** |
| | | * 支付通知/退款结果通知 |
| | | * @param headers |
| | | * @param request |
| | | * @param response |
| | | * @return |
| | | * @throws IOException |
| | | * @throws GeneralSecurityException |
| | | */ |
| | | @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 = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE) |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException, Exception { |
| | | JSONObject result = new JSONObject(); |
| | | |
| | | /** |
| | | * 1.验签处理 |
| | | * 从header中取出4个子参数 |
| | | * 验时间差,超过5分钟的不处理 |
| | | * 验证签名 |
| | | * 验证书序列号,必须与某一个证书的序列号一致 |
| | | */ |
| | | String wechatpayNonce = String.valueOf(headers.get("Wechatpay-Nonce").get(0)); |
| | | 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)); |
| | | |
| | | // 获取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; |
| | | if(timeDiff > 5) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, bodyStr); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, certFileBs); |
| | | if(!valid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 序列号验证要放在验签后,因为验签时可能会下载新的证书 |
| | | boolean SerialIsValid = false; |
| | | for (String key : payHelper.CERTIFICATE_MAP.keySet()) { |
| | | if(key.equals(wechatpaySerial)) { |
| | | SerialIsValid = true; |
| | | } |
| | | } |
| | | if(!SerialIsValid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 解密处理 |
| | | String eventType = orderNotify.getEvent_type(); |
| | | |
| | | if(eventType != null && eventType.equals("TRANSACTION.SUCCESS")) { |
| | | // 支付成功回调 |
| | | /** |
| | | * 支付成功的回调 |
| | | * 取出通知数据对象,继而取出解密所需的associatedData和nonce,以及密文ciphertext |
| | | * 解密ciphertext得到 |
| | | */ |
| | | NotifyResource notifyResource = orderNotify.getResource(); |
| | | String associatedData = notifyResource.getAssociated_data(); |
| | | String nonce = notifyResource.getNonce(); |
| | | String ciphertext = notifyResource.getCiphertext(); |
| | | |
| | | String resource = AesUtil.decryptToString(com.dy.pipIrrWechat.wechatpay.PayInfo.key.getBytes("utf-8"), associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext); |
| | | JSONObject job_resource = JSONObject.parseObject(resource); |
| | | |
| | | // 解密后取出:商户订单号、微信支付订单号、交易状态、支付完成时间 |
| | | String attach = job_resource.getString("attach"); |
| | | DataSourceContext.set(attach); |
| | | 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"); |
| | | |
| | | // 如果当前订单状态为未支付状态,则更新虚拟卡表及充值表响应字段 |
| | | 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得到 |
| | | */ |
| | | 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 out_refund_no = job_resource.getString("out_refund_no"); |
| | | String refund_status = job_resource.getString("refund_status"); |
| | | Date success_time = job_resource.getDate("success_time"); |
| | | if(!refund_status.equals("SUCCESS")) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 更新虚拟卡表及充值表响应字段 |
| | | SeVcRefundItem seVcRefundItem = new SeVcRefundItem(); |
| | | seVcRefundItem.setRefundTime(success_time); |
| | | seVcRefundItem.setRefundStatus(RefundItemStateENUM.REFUNDED.getCode()); |
| | | Integer rec = virtualCardSv.updateRefundItem(seVcRefundItem); |
| | | if(rec == null && rec <= 0) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | | result.put("message", "失败"); |
| | | return result; |
| | | } |
| | | |
| | | // 根据退款单号反查退款ID,根据退款ID获取退款状态是未退款的记录数量,如果是0则说明全部退款完成,更新退款表状态为已退款,将退款后金额更新到虚拟卡表 |
| | | /** |
| | | * 根据退款通知接口返回的退款单号反查退款ID,查询该退款ID下未退款记录数量 |
| | | * 如果结果为0,则该退款已经完成 |
| | | * 1. 更新退款表状态为已退款 |
| | | * 2. 将退款后余额更新到虚拟卡表中 |
| | | */ |
| | | Integer noRefundedCount = virtualCardSv.getNoRefundedCount(out_refund_no); |
| | | if(noRefundedCount != null && noRefundedCount == 0) { |
| | | // 获取退款对象并修改退款状态 |
| | | Long refundId = virtualCardSv.getRefundIdByRefundNumber(out_refund_no); |
| | | SeVcRefund seVcRefund = virtualCardSv.selectRefundByRefundId(refundId); |
| | | seVcRefund.setRefundStatus(RefundStatusENUM.REFUNDED.getCode()); |
| | | virtualCardSv.updateRefund(seVcRefund); |
| | | |
| | | // 获取虚拟卡对象并修改余额、最后操作、最后操作时间 |
| | | Long vcId = seVcRefund.getVcId(); |
| | | Double afterRefund = seVcRefund.getAfterRefund(); |
| | | SeVirtualCard seVirtualCard = virtualCardSv.selectVirtuCardById(vcId); |
| | | seVirtualCard.setMoney(afterRefund); |
| | | seVirtualCard.setLastOperate(LastOperateENUM.REFUND.getCode()); |
| | | seVirtualCard.setLastOperateTime(new Date()); |
| | | virtualCardSv.updateVirtualCard(seVirtualCard); |
| | | } |
| | | } |
| | | |
| | | // 通知应答 |
| | | response.setStatus(200); |
| | | result.put("code", "SUCCESS"); |
| | | result.put("message", "成功"); |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 添加微信支付信息 |
| | | * @param po |
| | | * @param bindingResult |
| | | * @return |
| | | */ |
| | | @PostMapping(path = "add_wechatpay", consumes = MediaType.APPLICATION_JSON_VALUE) |
| | | public BaseResponse<Boolean> addWechatpay(@RequestBody @Valid Wechatpay po, BindingResult bindingResult) throws Exception { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | | |
| | | if(paymentSv.getWechatpayByAppId(po.getAppId()) != null) { |
| | | return BaseResponseUtils.buildErrorMsg("该微信支付信息已经存在"); |
| | | } |
| | | |
| | | SeWechatpay seWechatpay = new SeWechatpay(); |
| | | seWechatpay.setAppId(AES.encrypt(po.getAppId())); |
| | | seWechatpay.setAppSecret(AES.encrypt(po.getAppSecret())); |
| | | seWechatpay.setMchId(AES.encrypt(po.getMchId())); |
| | | seWechatpay.setMchKey(AES.encrypt(po.getMchKey())); |
| | | seWechatpay.setSerialNo((AES.encrypt(po.getSerialNo()))); |
| | | seWechatpay.setNotifyUrl(AES.encrypt(po.getNotifyUrl())); |
| | | seWechatpay.setRemarks(po.getRemarks()); |
| | | |
| | | Long rec = Optional.ofNullable(paymentSv.addWechatpay(seWechatpay)).orElse(0L); |
| | | if(rec == 0) { |
| | | return BaseResponseUtils.buildFail("添加微信支付信息失败"); |
| | | } |
| | | return BaseResponseUtils.buildSuccess(true) ; |
| | | } |
| | | } |