最近研究了一下支付宝的支付流程,其它的倒没感觉出来,唯一的感受就是微信支付和支付宝支付这两个官方文档写的真是无法描述,不好好研究还真看不明白.所以将自己理解的支付宝支付流程描述出来.一来将自己学的东西记录下来,另一方面避免做这方面的朋友们浪费过多的时间.
支付宝调用流程开发前的准备工作
- 配置应用网关
- 应用网关里面填写的值就是商户后台的异步回调地址.也就是在支付宝付完款之后,由支付宝调用商户,便于商户验证订单各信息和更新订单状态
- 授权回调地址
- 授权回调地址的值是指用户在使用支付宝付款成功后从支付宝跳转到商户自己的页面.
- 接口加密方式
- 这个用于商户的签名加密,有RSA2和RSA两种算法类型,默认是RSA2.开发人员可以使用支付宝提供的生成方式自己生成,然后将公钥上传到支付宝开放平台.这里提供支付宝生成密钥的教程和下载地址
- 现在提供一张的截图,显示配置的位置.到达方式:蚂蚁金服开放文档—> 右上角账户中的账号管理—>开发者中心—>我的应用(应用) —> 选择自己将要配置的应用名称点击右边的查看—>应用信息(就是截图看到的页面了)
支付宝支付流程:
- 前端调用商户后台支付接口
- 商户后台支付接口进行一些必要的业务逻辑上的处理之后调用支付宝的支付接口(原始支付API).需要的参数详见支付宝支付接口的参数说明.支付宝会返回一个form表单.商户后台支付接口将表单返回给前台.
- 前台将表单提交给支付宝,唤起支付宝客户端进行支付.
- 支付成功后支付宝会根据原始支付API中传入的异步通知地址notify_url,通过POST请求的方式将支付结果作为参数通知到商户后台系统.
- 商户后台系统在接到支付宝的异步通知后要在验证自己本身业务逻辑之外严格验证通知数据的正确性.
- 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
- 判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额)
- 校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
- 验证app_id是否为该商户本身
- 上述1、2、3、4有任何一个验证不通过,则表明本次通知是异常通知,务必忽略.详情参见https://docs.open.alipay.com/203/105286/ 异步返回的验签
- 判断没有任何错误,打印success.否则支付宝服务器会不断重发通知,直至超过24小时22分钟.
pom.xml添加支付宝依赖
[java] view plain copy
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.1.1</version>
- </dependency>
- <dependency>
- <groupId>com.alipay</groupId>
- <artifactId>alipay-sdk</artifactId>
- <version>java20170307171631</version>
- </dependency>
支付宝方面的参数
代码展示
商户后台支付接口
[java] view plain copy
- /**
- * 支付宝支付
- * @param req
- * @param response
- * @throws OperationFailedException
- */
- @RequestMapping("alipay/Prepay")
- @ResponseBody
- public void aliPrepay(@RequestBody AliPrepayReq req, HttpServletResponse response) throws OperationFailedException {
- Set<ConstraintViolation<AliPrepayReq>> constraintViolationSet = validator.validate(req);
- if (constraintViolationSet.size() > 0) {
- throw new OperationFailedException(constraintViolationSet.iterator().next().getMessage());
- }
- LibraOrder libraOrder = libraService.findLibraOrderByHashCode(req.getOutTradeNo());
- Product product = productRepository.findOne(Long.valueOf(libraOrder.getProduct().getId()));
- if(product == null) { throw new OperationFailedException("该商品不存在"); }
- try{
- String form = aliPayService.generateAliPay(req);
- if(!"err".equals(form)){
- response.setContentType("text/html;charset=utf-8");
- response.getWriter().write(form);// 直接将完整的表单html输出到页面
- response.getWriter().flush();
- }
- }catch (Exception e){
- throw new OperationFailedException("支付失败");
- }
- }
[java] view plain copy
- /**
- * 支付宝方面订单号获取
- * @param aliPrepayReq
- * @return
- * @throws OperationFailedException
- */
- public String generateAliPay(AliPrepayReq aliPrepayReq) throws OperationFailedException {
- AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl,
- appId, rsaPrivateKey, "json", "UTF-8", rsaPublicKey, "RSA2"); //获得初始化的AlipayClient
- Map<String, String> param = new HashMap<>();
- AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();
- // 封装请求支付信息
- AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
- model.setOutTradeNo(aliPrepayReq.getOutTradeNo());
- model.setSubject(aliPrepayReq.getSubject());
- Integer totalAmount = aliPrepayReq.getTotalAmount();
- model.setTotalAmount(String.valueOf(totalAmount / 100.0));
- model.setBody(aliPrepayReq.getBody());
- model.setProductCode(aliPrepayReq.getProductCode());
- alipay_request.setBizModel(model);
- // 设置异步通知地址
- alipay_request.setNotifyUrl(notifyUrl);
- // 设置同步地址
- alipay_request.setReturnUrl(returnUrl);
- String form = "";
- try {
- LOGGER.info("alipay_request = " jsonService.toJson(alipay_request));
- // 调用SDK生成表单
- form = alipayClient.pageExecute(alipay_request).getBody();
- LOGGER.info("form = " form);
- } catch (Exception e) {
- throw new OperationFailedException("支付出错");
- }
- return form;
- }
异步回调接口
[java] view plain copy
- /**
- * 支付宝方面异步回调
- * @param request
- * @return
- * @throws OperationFailedException
- */
- @RequestMapping("alipay/notifyCallback")
- public String AlipayCallBack(HttpServletRequest request) throws OperationFailedException {
- //获取支付宝POST过来反馈信息
- Map<String,String> params = new HashMap<String,String>();
- Map requestParams = request.getParameterMap();
- for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
- String name = (String) iter.next();
- String[] values = (String[]) requestParams.get(name);
- String valueStr = "";
- for (int i = 0; i < values.length; i ) {
- valueStr = (i == values.length - 1) ? valueStr values[i]
- : valueStr values[i] ",";
- }
- //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
- //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
- params.put(name, valueStr);
- }
- LOGGER.info("params = " jsonService.toJson(params));
- boolean verify_result = aliPayService.rsaCheck(params);
- if(verify_result && aliPayService.dealWithAliPayChargeOrder(params)){
- return "success";
- }
- return "false";
- }
[java] view plain copy
- /**
- * 订单验证
- * @param params
- * @return
- */
- @Transactional
- public boolean dealWithAliPayChargeOrder(Map<String, String> params) {
- String outTradeNo = params.get("out_trade_no");
- LibraOrder libraOrder = libraOrderRepository.findByHashCode(outTradeNo);
- if (libraOrder == null) {
- LOGGER.info("订单" outTradeNo "不存在");
- return false;
- }
- boolean locked = distributeLocker.lock(USER_ALIPAY_DEAL_LOCK_KEY);
- try {
- if (locked) {
- String totalAmount = params.get("total_amount");
- double totalPrice = Double.parseDouble(totalAmount) * 100;
- if(libraOrder.getTotalPrice() != Math.round(totalPrice)){
- LOGGER.info("alipay和订单金额不一致");
- return false;
- }
- if(totalPrice < 0 || libraOrder.getTotalPrice() < 0){
- LOGGER.info("alipay或订单金额为负");
- return false;
- }
- //set done
- libraOrder.setPayAt(new Date());
- libraOrder.setUpdateAt(new Date());
- libraOrder.setStatus(ClientOrderStatus.PAYED);
- //后台订单状态初始化为 等待接待
- libraOrder.setServiceStatus(ServiceStatus.SERVICE_WAIT);
- libraOrderRepository.save(libraOrder);
- }
- } finally {
- if (locked) {
- //TODO order.getUserId()
- distributeLocker.unlock(USER_ALIPAY_DEAL_LOCK_KEY);
- }
- }
- return false;
- }
[java] view plain copy
- /**
- * 验证RSA签名
- * @param params
- * @return
- * @throws OperationFailedException
- */
- public boolean rsaCheck(Map<String, String> params) throws OperationFailedException {
- try {
- boolean verify_result = AlipaySignature.rsaCheckV1(params, rsaPublicKey, charset, signType);
- LOGGER.info("verify_result = " verify_result);
- return verify_result;
- } catch (AlipayApiException e) {
- throw new OperationFailedException("验证签名失败");
- }
- }
,