前言
最近公司需要接第三方支付,因为鄙人还没做过这样的功能,于是用勇于率先冲在前面,尝试新鲜的事物,扩大自己知识面。
一、前提须知
- 第一步你调用别人的接口(包含回调地址)然后返回一个第三发放给你的页面
- 第三方回调你的回调地址,你来校验金额、sign签名、订单是否存在,状态是否支付成功均是否OK
- sdk功能集成了用户登录、充值通道、社区功能、社交分享功能、数据后台统计功能的一个功能模块。我们包含了登陆登出,充值,用户信息修改等功能
流程图
二、回到正题支付
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