前言

最近公司需要接第三方支付,因为鄙人还没做过这样的功能,于是用勇于率先冲在前面,尝试新鲜的事物,扩大自己知识面。

一、前提须知

  • 第一步你调用别人的接口(包含回调地址)然后返回一个第三发放给你的页面
  • 第三方回调你的回调地址,你来校验金额、sign签名、订单是否存在,状态是否支付成功均是否OK
  • sdk功能集成了用户登录、充值通道、社区功能、社交分享功能、数据后台统计功能的一个功能模块。我们包含了登陆登出,充值,用户信息修改等功能

流程图

image.png

二、回到正题支付

1、点击支付

  • 调用pay/placePCOrder,检验是否非法拉起订单,保存预支付订单。

2、拉起startH5Pay支付接口

  • 数据库配置需要调用的支付,例如这里foceplay,他就会调用接口为foceplay的类,因为继承了父类的startH5Pay,实现这个接口来返回第三方返回给我们这边的视图url
String urlPay;
    try {
       payWay = ("weixin".equals(payWay)) ? "wx" : "alipay";
       urlPay = getPayUrl(orderId, payWay);
    } catch (Exception e) {
       throw XException.XExceptionBuilder.newBuilder(XServiceExceptionEnum.SDK_PAY_ERROR).build();
   }
       logger.info("startH5Pay 请求支付url:" + urlPay);
       return new ModelAndView("/pay/getRedirect").addObject("url", urlPay);

调用第三方

/**
     * 获取支付链接
     * @param orderId 订单ID
     * @param payWay
     * @return
     * @throws Exception
     */
    public String getPayUrl(String orderId, String payWay) throws Exception {
        TPayPreOrderEntity preOrderEntity = payPreOrderEntityRepository.findByOrderId(Long.parseLong(orderId, 16));

        Map<String, String> map = new HashMap<>();
        map.put("app_id", APP_ID);
        map.put("goods_desc", DESC);
        map.put("goods_title", "title");
        map.put("type", payWay);
        map.put("method", "wap");
        map.put("notify_url", this.paybackUrl + "/foceplay/validate");
        map.put("order_id", Long.toHexString(preOrderEntity.getOrderId()));
        map.put("amount", "0.01");
        // 参数加密
        getSign(map);
        if ("wx".equals(payWay)) {
            map.put("ip", "192.168.1.1");
        }
        String response = xHttpService.doPostForm(PAY_URL, map);
        logger.debug("请求结果:" + response);
        Map jsonMap = JSONObject.toJavaObject(JSONObject.parseObject(response), Map.class);
        String retUrl =
            JSONObject.parseObject(jsonMap.get("data").toString()).getJSONObject("pay_info").getString("pay_url");
        return retUrl;
    }

3、回调接口

  • 校验签名
  • 校验预订单是否存在该订单的
  • 校验金额、支付状态是否成功
  • 保存支付成功订单
    /**
     * 支付回调
     * @param request
     * @param validateDto
     * @return
     */
    @RequestMapping(value = "validate")
    @XPermission(permissionType = XPermissionType.PUBLIC)
    @ResponseBody
    public String validate(HttpServletRequest request, @RequestBody FocePlayValidateDto validateDto) throws Exception {
        logger.error("--------------------------------");
        logger.error("回调参数:{}", validateDto.toString());
        String requestamount = new String(request.getParameter("amount").getBytes("ISO-8859-1"), "UTF-8");
        logger.error("回调request参数:{}", requestamount);
        logger.error("--------------------------------");

        if (StringUtils.isEmpty(validateDto.getOrder_id())) {
            logger.error("支付失败:回调参数不完整");
            return "error";
        }
        Long orderIdLong = Long.parseLong(validateDto.getOrder_id(), 16);
        logger.error("/foceplay/validate orderId:{},orderIdLong{}", validateDto.getOrder_id(), orderIdLong);

        if(!notifyVerify(validateDto)) {
            logger.error("支付失败:密钥验证失败");
            return "error";
        }

        // 检查预订单
        TPayPreOrderEntity payPreOrderEntity = payPreOrderEntityRepository.findByOrderId(orderIdLong);
        if (payPreOrderEntity == null) {
            logger.error("支付失败:预订单不存在");
            return "error_out_trade_no_not_found";
        }
        StringBuffer sysOrderId = new StringBuffer();
        StringBuffer payTime = new StringBuffer();
        Long amount = payPreOrderEntity.getAmount();
        if (!checkOrder(validateDto.getOrder_id(), amount, sysOrderId, payTime)) {
            logger.error("支付失败:校验失败");
            return "error";
        }
        // 数据库数据更改
        payOrderDataUpdate(orderIdLong, payPreOrderEntity, sysOrderId, payTime, amount);
        logger.error("支付成功");
        return "success";
    }

询问第三方校验规则

// 校验签名
    public Boolean notifyVerify(FocePlayValidateDto validateDto) {
        Map<String, String> params = new HashMap<>();
        params.put("order_id", validateDto.getOrder_id());
        params.put("amount", validateDto.getAmount());
        params.put("app_id", APP_ID);
        // 平台流水号
        params.put("sys_order_id", validateDto.getSys_order_id());
        // 支付成功
        params.put("type", validateDto.getType());
        // 退款成功
        // params.put("type", "refund.success");
        String notifystr = formatUrlMap(params) + "&secret=" + SECERET;
        String bSign = md5(notifystr);
        logger.error("sign:{},_sign:{}", validateDto.getSign(), bSign);
        // 将sign和回调通知里面的sign进行比对,如果一样则验证通过
        return bSign.equalsIgnoreCase(validateDto.getSign());
    }

调用第三方校验支付

    /**
     * 调用第三方校验支付
     * @param orderId 订单ID
     * @param amount    金额
     * @param sysorderid    平台订单ID
     * @param payTime   支付时间
     * @return
     * @throws Exception
     */
    public Boolean checkOrder(String orderId, Long amount, StringBuffer sysorderid, StringBuffer payTime)
        throws Exception {
        // 查询订单
        String response = query(orderId);

        JSONObject jsonObject = JSONObject.parseObject(response);
        String result = jsonObject.getString("result");
        if (jsonObject.isEmpty() || !SUCCESS_CODE.equals(result)) {
            logger.error("校验支付状态不正确");
            return false;
        } else {
            JSONObject jsonObjectData = jsonObject.getJSONObject("data");
            if (jsonObjectData == null) {
                logger.error("校验支付data为空");
                return false;
            } else {
                JSONObject jsonObjectOrderInfo = jsonObjectData.getJSONObject("order_info");
                System.out.println("-----------1------------" + jsonObjectOrderInfo.toJSONString());
                logger.error("请求返回状态参数,{}", jsonObjectOrderInfo.getString("status"));
                logger.error("请求返回金额,{}", jsonObjectOrderInfo.getDouble("amount"));
                logger.error("回调金额,{}", amount);

                if ((SUCCESS_CODE.equals(jsonObjectOrderInfo.getString("status")))
                    && amount.equals(BigDecimal.valueOf(jsonObjectOrderInfo.getDouble("amount") * 1000).longValue())) {
                    sysorderid.append(jsonObjectOrderInfo.getString("sys_order_id"));
                    payTime.append(jsonObjectOrderInfo.getString("create_time"));
                    return true;
                } else {
                    logger.error("回调数据金额、状态不正确");
                    return false;
                }
            }
        }

    }

注意地方

  • 调用接口拼装场水获取到支付链接
  • 校验签名需要问第三方拼接规则,否则你无法拼接到想要的sign