个人学习总结,为java后台接口,不足之处请指教。
基于云通付实现的支付,所以先去云通付官网注册账号。得到相应的合作身份者PID、MD5密钥、商户号。(后面会用到)
下载相应jar包。将下载文件解压,把解压文件中的工具包拷贝到自己的项目中。
新建一个PayController类
先建立一个方法,是从用户点击“确认订单”的按钮后跳转到方法
@RequestMapping("page/{orderNo}")
@IsLogin
public String list(@PathVariable String orderNo, HttpServletRequest request, Model model)
throws PayException {
Integer loginUserId = LoginIdentityUtil.getFromLoginId(request);
// 根据订单号去获取订单信息:
PayRequestVo payRequestVo = orderService.buildPayRequestVo(orderNo, loginUserId);
model.addAttribute("payRequestVo", payRequestVo);
model.addAttribute("gateway", PayConstant.GATEWAY_NEW);
return "pay/pay";
}
该方法主要是生成签名,以及将支付请求的数据返回到页面,进行支付请求
页面主要Form表单如下,自动进行提交到“云通付”的支付网关
<!DOCTYPE html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>支付跳转中...</title>
</head>
<body style="background:#F3F3F4">
<br />
<br />
<div style="display: none">
<form id='paysubmit' name='paysubmit' action='${gateway}' accept-charset='utf-8' method='post'>
<input type='text' name='sign' value='${payRequestVo.sign}'/>
<input type='text' name='body' value='${payRequestVo.body}'/>
<input type='text' name='user_seller' value='${payRequestVo.userSeller}'/>
<input type='text' name='total_fee' value='${payRequestVo.totalFee}'/>
<input type='text' name='subject' value='${payRequestVo.subject}'/>
<input type='text' name='notify_url' value='${payRequestVo.notifyUrl}'/>
<input type='text' name='out_order_no' value='${payRequestVo.outOrderNo}'/>
<input type='text' name='partner' value='${payRequestVo.partner}'/>
<input type='text' name='return_url' value='${payRequestVo.returnUrl}'/>
<input type='submit' value='支付进行中...' />
</form>
</div>
<script type="text/javascript">
document.forms['paysubmit'].submit();
</script>
</body>
</html>
跳转到付款页面
付款回调方法,同步和异步
@RequestMapping("notify_url")
@IsLogin
public String notifyUrl(HttpServletRequest request, Model model,
PayCallbackDto payCallbackDto) {
String result = commonHanlerCallback(request, model, payCallbackDto);
return result;
}
@RequestMapping("return_url")
@IsLogin
public String returnUrl( HttpServletRequest request, Model model,
PayCallbackDto payCallbackDto) {
String result = commonHanlerCallback(request, model, payCallbackDto);
return result;
}
private String commonHanlerCallback(HttpServletRequest request, Model model,
PayCallbackDto payCallbackDto) {
Integer loginUserId = LoginIdentityUtil.getFromLoginId(request);
Integer resultCode = Constant.SUCCESS_CODE;
String resultMessage = "支付成功";
try {
Map<String, Object> result = orderService.handlerPayCallback(loginUserId, payCallbackDto);
model.addAllAttributes(result);
} catch (Exception e) {
resultCode = Constant.ERROR_CODE;
resultMessage = e.getMessage();
}
model.addAttribute("resultCode", resultCode);
model.addAttribute("resultMessage", resultMessage);
return "pay/success";
}
Controller层接受云通付返回的数据,用PayCallbackDto类接收
public class PayCallbackDto {
private BigDecimal total_fee; // 交易金额
private String out_order_no; // 商户订单号
private String sign; // 服务端校验码
private String trade_no; // 云通付交易订单号
private String trade_status; // 交易结果(TRADE_SUCCESS说明支付成功)
}
Service要走如下流程
1) 回调参数验证
2) 签名认证:将out_order_no、total_fee、trade_status、云通付PID、云通付KEY的值连接起来,进行md5加密,而后与sign进行对比
3) 支付状态验证trade_status= TRADE_SUCCESS
4) 订单验证
5) 订单金额比对
6) 更新订单状态
/**
* 支付回调处理
* @param loginUserId
* @param payCallbackDto
*/
public Map<String, Object> handlerPayCallback(Integer loginUserId, PayCallbackDto payCallbackDto) {
// 1) 回调参数验证
// 2) 签名认证:将out_order_no、total_fee、trade_status、云通付KEY、云通付PID 的值连接起来,进行md5加密,而后与sign进行对比
// 3) 支付状态验证trade_status= TRADE_SUCCESS
// 4) 订单验证
// 5) 订单金额比对
// 6) 更新订单状态
AssertUtil.isTrue(loginUserId == null || loginUserId < 1, "请登录");
String total_fee = payCallbackDto.getTotal_fee(); // 交易金额
AssertUtil.isTrue(total_fee == null, PayConstant.CALLBACK_FAILURE);
String out_order_no = payCallbackDto.getOut_order_no(); // 商户订单号
AssertUtil.isTrue(StringUtils.isBlank(out_order_no), PayConstant.CALLBACK_FAILURE);
String sign = payCallbackDto.getSign(); // 服务端校验码
AssertUtil.isTrue(StringUtils.isBlank(sign), PayConstant.CALLBACK_FAILURE);
String trade_no = payCallbackDto.getTrade_no(); // 云通付交易订单号
AssertUtil.isTrue(StringUtils.isBlank(trade_no), PayConstant.CALLBACK_FAILURE);
String trade_status = payCallbackDto.getTrade_status(); // 交易结果(TRADE_SUCCESS说明支付成功)
AssertUtil.isTrue(StringUtils.isBlank(trade_status), PayConstant.CALLBACK_FAILURE);
// 签名认证
Md5Util md5Util = new Md5Util();
// String key = PayConstant.KEY;
// String pid = PayConstant.PARTNER;
// String signMd5 = md5Util.encode(out_order_no + total_fee + trade_status + pid + key, null);
String preStr = out_order_no + total_fee + trade_status + PayConstant.PARTNER + PayConstant.KEY;
String signNew = md5Util.encode(preStr, null);
AssertUtil.isTrue(!signNew.equals(sign), "签名验证失败,请联系客服");
// 回调状态验证
AssertUtil.isTrue(!trade_status.equals(PayConstant.TRADE_SUCCESS), PayConstant.CALLBACK_FAILURE);
// 订单验证
Order order = orderDao.findOrderByOrderNo(out_order_no, loginUserId);
AssertUtil.isTrue(order == null, "订单异常,请联系客服");
AssertUtil.isTrue(order.getStatus() != OrderStatus.pendingPayment.getStatus(), "订单状态异常,请联系客服");
// 支付金额验证
BigDecimal amount = order.getAmount();
// 将金额精度精确到2位 四舍五入在转化为字符串
String priceStr = MathUtil.setScale(amount).toString();
AssertUtil.isTrue(!total_fee.equals(priceStr), "订单金额不准备去,请联系客服");
// 更新订单状态
orderDao.updateStatus(out_order_no, OrderStatus.pendingShipment.getStatus());
Map<String, Object> result = new HashMap<>();
result.put("orderNo", out_order_no);
result.put("money", priceStr);
return result;
}
注意
集成到项目是,发起支付请求时需要配置一个本地的hosts, 将云通付上绑定的域名配置到本地,因为第三方网关请求的都是访问域名, 以win7为例
a) 找到hosts的文件目录在C:\Windows\System32\drivers\etc
b) 以超级管理员的身份用记事本打开hosts
c) 配置一个host, 输入127.0.0.1 www.XXXX.com
d) 这样你访问项目的时候就可以用www.XXXX.com:8080访问了
验证签名时,要注意金额的小数点位数, 不然会提示签名失败
记得如果是拷贝到本地项目的话,要根据自己的项目情况修改PayConstant里面的回调url的路径