支付宝手机网页版支付、支付异步回调、退款

时间:2025-02-15 20:27:04
@ApiOperation(value = "支付宝网站订单回调接口") @RequestMapping(value = "/alipaywapnotify", method = RequestMethod.POST, produces = "text/html;charset=UTF-8") @IgnoreAuth @ResponseBody public String alipaywapnotify(HttpServletRequest request) { Map<String, String> params = convertRequestParamsToMap(request); // 将异步通知中收到的待验证所有参数都存放到map中 String paramsJson = JSON.toJSONString(params); try { // 调用SDK验证签名 boolean signVerified = AlipaySignature.rsaCheckV1(params, ResourceUtil.getConfigByName(""), "utf-8",ResourceUtil.getConfigByName("")); if (signVerified) { logger.info("支付宝回调签名认证成功"); // 按照支付结果异步通知中的描述,对支付结果中的业务内容进行1\2\3\4二次校验,校验成功后在response中返回success,校验失败返回failure this.check(params); // 处理业务 AlipayNotifyParam param = buildAlipayNotifyParam(params); String trade_status = param.getTradeStatus(); //处理订单的redis状态 String value = RedisUtils.get(param.getOutTradeNo()); if (value != null && "51".equals(value)) { RedisUtils.del(param.getOutTradeNo()); } else { //查询支付已结操作过 logger.info("SUCCSEE"); return "success"; } // 支付成功 if (trade_status.equals("TRADE_SUCCESS") || trade_status.equals("TRADE_FINISHED")) { // 处理支付成功逻辑 try { // 处理业务逻辑。 // 更改订单状态 ........................... ........................... } } } catch (Exception e) { logger.error("支付宝回调业务处理报错,params:" + paramsJson, e); } } else { logger.error("没有处理支付宝回调业务,支付宝交易状态:{},params:{}"); } // 如果签名验证正确,立即返回success,后续业务另起线程单独处理 // 业务处理失败,可查看日志进行补偿,跟支付宝已经没多大关系。 return "success"; } else { logger.info("支付宝回调签名认证失败,signVerified=false, paramsJson:{}"); return "failure"; } } catch (AlipayApiException e) { logger.error("支付宝回调签名认证失败,paramsJson:{},errorMsg:{}"); return "failure"; } } // 将request中的参数转换成Map private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) { Map<String, String> retMap = new HashMap<String, String>(); 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 valLen = values.length; if (valLen == 1) { retMap.put(name, values[0]); } else if (valLen > 1) { StringBuilder sb = new StringBuilder(); for (String val : values) { sb.append(",").append(val); } retMap.put(name, sb.toString().substring(1)); } else { retMap.put(name, ""); } } return retMap; } /** * 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, * 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), * 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email), * 4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略。 * 在上述验证通过后商户必须根据支付宝不同类型的业务通知,正确的进行不同的业务处理,并且过滤重复的通知结果数据。 * 在支付宝的业务通知中,只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时,支付宝才会认定为买家付款成功。 * * @param params * @throws AlipayApiException */ private void check(Map<String, String> params) throws AlipayApiException { String outTradeNo = params.get("out_trade_no"); // 1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, List<OrderVo> orderVos = orderService.queryByAllOrderId(outTradeNo);// 这个方法自己实现 if (orderVos.isEmpty()) { throw new AlipayApiException("out_trade_no错误"); } // 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), long total_amount = new BigDecimal(params.get("total_amount")).multiply(new BigDecimal(100)).longValue(); if (total_amount != orderVos.get(0).getActual_price().multiply(new BigDecimal(100)).longValue()){ throw new AlipayApiException("error total_amount"); } // 3、校验通知中的seller_id(或者seller_email)是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email), // 第三步可根据实际情况省略 // 4、验证app_id是否为该商户本身。 if (!params.get("app_id").equals(ResourceUtil.getConfigByName(""))) { throw new AlipayApiException("app_id不一致"); } } private AlipayNotifyParam buildAlipayNotifyParam(Map<String, String> params) { String json = JSON.toJSONString(params); return JSON.parseObject(json, AlipayNotifyParam.class); } } class AlipayNotifyParam implements Serializable { public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getTradeNo() { return tradeNo; } public void setTradeNo(String tradeNo) { this.tradeNo = tradeNo; } public String getOutTradeNo() { return outTradeNo; } public void setOutTradeNo(String outTradeNo) { this.outTradeNo = outTradeNo; } public String getOutBizNo() { return outBizNo; } public void setOutBizNo(String outBizNo) { this.outBizNo = outBizNo; } public String getBuyerId() { return buyerId; } public void setBuyerId(String buyerId) { this.buyerId = buyerId; } public String getBuyerLogonId() { return buyerLogonId; } public void setBuyerLogonId(String buyerLogonId) { this.buyerLogonId = buyerLogonId; } public String getSellerId() { return sellerId; } public void setSellerId(String sellerId) { this.sellerId = sellerId; } public String getSellerEmail() { return sellerEmail; } public void setSellerEmail(String sellerEmail) { this.sellerEmail = sellerEmail; } public String getTradeStatus() { return tradeStatus; } public void setTradeStatus(String tradeStatus) { this.tradeStatus = tradeStatus; } public BigDecimal getTotalAmount() { return totalAmount; } public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; } public BigDecimal getReceiptAmount() { return receiptAmount; } public void setReceiptAmount(BigDecimal receiptAmount) { this.receiptAmount = receiptAmount; } public BigDecimal getBuyerPayAmount() { return buyerPayAmount; } public void setBuyerPayAmount(BigDecimal buyerPayAmount) { this.buyerPayAmount = buyerPayAmount; } public BigDecimal getRefundFee() { return refundFee; } public void setRefundFee(BigDecimal refundFee) { this.refundFee = refundFee; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public Date getGmtCreate() { return gmtCreate; } public void setGmtCreate(Date gmtCreate) { this.gmtCreate = gmtCreate; } public Date getGmtPayment() { return gmtPayment; } public void setGmtPayment(Date gmtPayment) { this.gmtPayment = gmtPayment; } public Date getGmtRefund() { return gmtRefund; } public void setGmtRefund(Date gmtRefund) { this.gmtRefund = gmtRefund; } public Date getGmtClose() { return gmtClose; } public void setGmtClose(Date gmtClose) { this.gmtClose = gmtClose; } public String getFundBillList() { return fundBillList; } public void setFundBillList(String fundBillList) { this.fundBillList = fundBillList; } public String getPassbackParams() { return passbackParams; } public void setPassbackParams(String passbackParams) { this.passbackParams = passbackParams; } private String appId; private String tradeNo; // 支付宝交易凭证号 private String outTradeNo; // 原支付请求的商户订单号 private String outBizNo; // 商户业务ID,主要是退款通知中返回退款申请的流水号 private String buyerId; // 买家支付宝账号对应的支付宝唯一用户号。以2088开头的纯16位数字 private String buyerLogonId; // 买家支付宝账号 private String sellerId; // 卖家支付宝用户号 private String sellerEmail; // 卖家支付宝账号 private String tradeStatus; // 交易目前所处的状态,见交易状态说明 private BigDecimal totalAmount; // 本次交易支付的订单金额 private BigDecimal receiptAmount; // 商家在交易中实际收到的款项 private BigDecimal buyerPayAmount; // 用户在交易中支付的金额 private BigDecimal refundFee; // 退款通知中,返回总退款金额,单位为元,支持两位小数 private String subject; // 商品的标题/交易标题/订单标题/订单关键字等 private String body; // 该订单的备注、描述、明细等。对应请求时的body参数,原样通知回来 private Date gmtCreate; // 该笔交易创建的时间。格式为yyyy-MM-dd HH:mm:ss private Date gmtPayment; // 该笔交易的买家付款时间。格式为yyyy-MM-dd HH:mm:ss private Date gmtRefund; // 该笔交易的退款时间。格式为yyyy-MM-dd HH:mm: private Date gmtClose; // 该笔交易结束时间。格式为yyyy-MM-dd HH:mm:ss private String fundBillList; // 支付成功的各个渠道金额信息,array private String passbackParams; // 公共回传参数,如果请求时传递了该参数,则返回给商户时会在异步通知时将该参数原样返回。 }