Java之spring cloud框架实现支付宝支付功能
现在越来越多的企业愿意接入支付宝或微信的支付功能,因为受众面大,客户的信赖程度也高。
那么这篇文章简单来讲一讲我们如何在spring cloud框架下实现支付宝支付的功能。
常量配置
我们可以建立一个常量配置文件,例如就叫AlipayConfig
// 请求网关地址 public static final String URL = "https://openapi.alipay.com/gateway.do"; // 编码格式 public static final String CHARSET = "UTF-8"; // 返回格式 public static final String FORMAT = "json"; // 加密类型RSA2 public static final String SIGNTYPE = "RSA2"; // appid public static final String h5APPID="*****"; // 支付公钥 public static final String h5_ALIPAY_PUBLIC_KEY=""; // 应用私钥 public static final String h5_PRIVATE_KEY="";
配置回调地址和通知页面
回调地址和通知页面是在application.yml中配置的,代码如下:
阿里云支付回调(官方)这里配置回调用于处理支付成功后的逻辑 h5_alis_gf_notify_url: http://127.0.0.1:8888/uncheck/aliNotifyUrl 阿里云支付成功跳转(官方)这里配置支付完后跳转的页面 h5_alis_gf_query_url: https://127.0.0.1:8888/paySuccess
支付业务接口代码
这里主要定义了查询订单状态、统一下单拉起支付、支付完回调这3个接口
public interface AliPayAppService {//查询订单状态
Map<String,Object> getStatus(OrderVo orderVo);
//统一下单拉起支付
Map<String,Object>geth5AliPayOrderStr(OrderVo orderVo);
//支付完回调
String h5AliNotifyUrl(Map<String, String> conversionParams);
}
支付业务实现类
主要是对接口的业务实现,代码比较长
//支付回调地址 @Value(value="${h5_alis_gf_notify_url}") private String h5_alis_gf_notify_url; //支付成功地址 @Value(value="${h5_alis_gf_query_url}") private String h5_alis_gf_query_url; /** * * @param orderVo 订单实体类 * @return */ @Override public Map<String, Object> geth5AliPayOrderStr(OrderVo orderVo) { /*********************** 先校验订单有效性*********************************/ Map<String, Object> map = new HashMap<>(); // 最终返回加签之后的,app需要传给支付宝app的订单信息字符串 String orderString = "0"; logger.info("支付宝下单,商户订单号为:" + orderVo.geteName()); //这个类对应的表存储支付完回调信息的 Alipaymentorder alipaymentOrder = new Alipaymentorder(); if (orderVo.geteId() != null) { alipaymentOrder.setOrderId(orderVo.geteId().longValue()); // 商户订单主键 } alipaymentOrder.setOutTradeNo(orderVo.geteName());// 商户订单号 alipaymentOrder.setTradeStatus(0);// 交易状态 //这个金额正式的需要根据订单号去查 alipaymentOrder.setTotalAmount(new BigDecimal(0.01));// 订单金额 alipaymentOrder.setRefundFee(0.00); // 总退款金额 try { // 实例化客户端(参数:网关地址、商户appid、商户私钥、格式、编码、支付宝公钥、加密类型),为了取得预付订单信息 AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.h5APPID, AlipayConfig.h5_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.h5_ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); // SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式 AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); model.setSubject("此订单"); // 商品名称 model.setOutTradeNo(orderVo.geteName()); // 商户订单号 // model.setTimeoutExpress("30m"); //交易超时时间 model.setTotalAmount("0.01"); /****************************/ model.setProductCode("QUICK_WAP_WAY"); // 销售产品码(固定值) request.setBizModel(model); request.setNotifyUrl(h5_alis_gf_notify_url); // 异步回调地址(后台) request.setReturnUrl(h5_alis_gf_query_url);//设置支付成功跳转页面的地址 //ali_request.setReturnUrl(AlipayAppConfig.return_url); // 同步回调地址(APP) //Requestcontent 是一个请求报文表与支付业务无关 Requestcontent requestcontent=new Requestcontent(); requestcontent.setContent(request.toString());//报文内容 requestcontent.setContentType(Constants.CALLBACK_ALI_APP);//支付方式 requestcontent.setContenTime(new Date());//时间 requestcontent.setEntrydetailId(orderVo.geteName());//订单号 requestcontentMapper.insertSelective(requestcontent);//添加报文 // 这里和普通的接口调用不同,使用的是sdkExecute AlipayTradeWapPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(request); // 返回支付宝订单信息(预处理) if(alipayTradeAppPayResponse.isSuccess()){ // 如果商家订单表没有该订单数据则添加 if (alipaymentorderService.getAlipaymentorder(orderVo.geteName()).size() == 0) { alipaymentorderService.addAlipaymentorder(alipaymentOrder); //创建新的商户支付宝订单 } DateAndStrUtil strUtil = DateAndStrUtil.getDateAndStr(); //DateAndStrUtil 是一个时间工具类 requestTimestamp格式为yyyy-MM-dd HH:mm:ss String requestTimestamp = strUtil.dateToStr(new Date()); orderString = alipayTradeAppPayResponse.getBody(); map.put("orderString", "https://openapi.alipay.com/gateway.do?"+orderString+"×tamp="+requestTimestamp); //前端直接访问orderString", "https://openapi.alipay.com/gateway.do?"+orderString+"×tamp="+requestTimestamp 即可拉起支付 return Utils.packageResponseData(MapConstants.mapSuccessCode, "拉起成功", map); }else{ return Utils.packageResponseData(MapConstants.mapErrorCode, "拉起失败", map); } } catch (Exception e) { e.printStackTrace(); logger.info("与支付宝交互出错,未能生成订单,请检查代码!"); return Utils.packageResponseData(MapConstants.mapErrorCode, "拉起失败", map); } } /** *功能描述: 支付宝查询订单状态 * @author * @date 2020/7/2 * @param orderVo * @return java.util.Map<java.lang.String,java.lang.Object> */ @Override public Map<String, Object> getStatus(OrderVo orderVo) { Map<String,Object> map=new HashMap<>(); if(Strings.isNullOrEmpty(orderVo.geteName())) { return Utils.packageResponseData(MapConstants.mapNullCode, "订单号不能为空", map); } AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.h5APPID, AlipayConfig.h5_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.h5_ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE); // 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); request.setBizContent("{" + "\"out_trade_no\" : \"" + orderVo.geteName() + "\"}"); try { AlipayTradeQueryResponse response = alipayClient.execute(request); if (response.isSuccess()) { switch (response.getTradeStatus()) // 判断交易结果 { case "TRADE_FINISHED": // 交易结束并不可退款 map.put("status",3); break; case "TRADE_SUCCESS": // 交易支付成功 map.put("status",2); break; case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款 map.put("status",1); break; case "WAIT_BUYER_PAY": // 交易创建并等待买家付款 map.put("status",0); break; default: break; } return Utils.packageResponseData(MapConstants.mapSuccessCode, "查询成功", map); } else { logger.info("调用支付宝查询接口失败!"); return Utils.packageResponseData(MapConstants.mapErrorCode, "调用支付宝查询接口失败!", map); } } catch (Exception e) { e.printStackTrace(); return Utils.packageResponseData(MapConstants.mapErrorCode, "查询失败", map); } } //官方支付宝异步请求逻辑处理 @Override public String h5AliNotifyUrl(Map<String, String> conversionParams) { logger.info("官方支付宝异步请求逻辑处理"); // 签名验证(对支付宝返回的数据验证,确定是支付宝返回的) boolean signVerified = false; try { // 调用SDK验证签名 signVerified = AlipaySignature.rsaCheckV1(conversionParams, AlipayConfig.h5_ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE); } catch (AlipayApiException e) { logger.info("验签失败 !"); e.printStackTrace(); } // 对验签进行处理 if (signVerified) { System.out.println("支付宝回调验签通过"); // 验签通过 // 获取需要保存的数据 String appId = conversionParams.get("app_id"); Alipaymentorder alipaymentOrder=getAlipayMent(conversionParams); if (AlipayConfig.h5APPID.equals(appId)) { EntrydetailExample entrydetailExample=new EntrydetailExample(); entrydetailExample.createCriteria().andENameEqualTo(alipaymentOrder.getOutTradeNo()); List<Entrydetail> list=entrydetailMapper.selectByExample(entrydetailExample); alipaymentOrder.setUserId(list.get(0).geteUid()); //更新回调表中的内容 int returnResult = alipaymentorderService.updateAlipaymentorder(alipaymentOrder); // 更新交易表中状态 // 修改交易表状态,支付成功 if (returnResult > 0) { logger.info("======官方支付宝更新商户订单表成功======"); return "SUCCESS";//如果不反回将会持续回调24小时 } else { logger.info("更新表状态失败"); return "FAILED"; } } else { logger.info("支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id),不一致!返回fail"); return "FAILED"; } } else { // 验签不通过 logger.info("验签不通过 !"); return "FAILED"; } } //回调用到的getAlipayMent方法 public Alipaymentorder getAlipayMent(Map<String, String> conversionParams){ DateAndStrUtil dateAndStrUtil = new DateAndStrUtil(); String appId = conversionParams.get("app_id");// 支付宝分配给开发者的应用Id String notifyTime = conversionParams.get("notify_time");// 通知时间:yyyy-MM-dd HH:mm:ss String gmtCreate = conversionParams.get("gmt_create");// 交易创建时间:yyyy-MM-dd HH:mm:ss String gmtPayment = conversionParams.get("gmt_payment");// 交易付款时间 String tradeNo = conversionParams.get("trade_no");// 支付宝的交易号 String outTradeNo = conversionParams.get("out_trade_no");// 获取商户之前传给支付宝的订单号(商户系统的唯一订单号) String totalAmount = conversionParams.get("total_amount");// 订单金额:本次交易支付的订单金额,单位为人民币(元) String receiptAmount = conversionParams.get("receipt_amount");// 实收金额:商家在交易中实际收到的款项,单位为元 String buyerPayAmount = conversionParams.get("buyer_pay_amount");// 付款金额:用户在交易中支付的金额 String tradeStatus = conversionParams.get("trade_status");// 获取交易状态 // 支付宝官方建议校验的值(out_trade_no、total_amount、sellerId、app_id) List<Alipaymentorder> alipaymentOrderList = alipaymentorderService.getAlipaymentorder(outTradeNo); Alipaymentorder alipaymentOrder = alipaymentOrderList.get(0); alipaymentOrder.setNotifyTime(dateAndStrUtil.strToDate(notifyTime)); // 通知时间 alipaymentOrder.setGmtCreate(dateAndStrUtil.strToDate(gmtCreate)); // 交易创建时间 alipaymentOrder.setGmtPayment(dateAndStrUtil.strToDate(gmtPayment)); // 交易付款时间 //alipaymentOrder.setGmt_refund(DateUtils.parse(gmtRefund)); // 交易退款时间 //alipaymentOrder.setGmt_close(DateUtils.parse(gmtClose)); // 交易结束时间 alipaymentOrder.setTradeNo(tradeNo); // 支付宝交易号 //alipaymentOrder.setOut_biz_no(outBizNo); // 商户业务号(商户业务ID,主要是退款通知中返回退款申请的流水号) alipaymentOrder.setTotalAmount(new BigDecimal(totalAmount)); //订单金额:本次交易支付的订单金额,单位为人民币(元) alipaymentOrder.setReceiptAmount(new BigDecimal(receiptAmount)); // 实收金额:商家在交易中实际收到的款项,单位为元 //alipaymentOrder.setInvoiceAmount(Double.parseDouble(invoiceAmount)); // 开票金额:用户在交易中支付的可开发票的金额 alipaymentOrder.setBuyerPayAmount(new BigDecimal(buyerPayAmount)); // 付款金额:用户在交易中支付的金额 Utils.getStatus(alipaymentOrder,tradeStatus); return alipaymentOrder; }
util工具类
在实现业务逻辑的过程中,将一些通用的代码抽象到工具类里面
public class Utils {
public static void getStatus(Alipaymentorder alipaymentorder,String status){
switch (status) // 判断交易结果
{
case "TRADE_FINISHED": // 交易结束并不可退款
alipaymentorder.setTradeStatus(3);
break;
case "TRADE_SUCCESS": // 交易支付成功
alipaymentorder.setTradeStatus(2);
break;
case "TRADE_CLOSED": // 未付款交易超时关闭或支付完成后全额退款
alipaymentorder.setTradeStatus(1);
break;
case "WAIT_BUYER_PAY": // 交易创建并等待买家付款
alipaymentorder.setTradeStatus(0);
break;
default:
break;
}
}
/**
*
* Map转String
* @param map
* @return
*/
public static String getMapToString(Map<String,String> map){
Set<String> keySet = map.keySet();
//将set集合转换为数组
String[] keyArray = keySet.toArray(new String[keySet.size()]);
//给数组排序(升序)
Arrays.sort(keyArray);
//因为String拼接效率会很低的,所以转用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < keyArray.length; i++) {
// 参数值为空,则不参与签名 这个方法trim()是去空格
if ((String.valueOf(map.get(keyArray[i]))).trim().length() > 0) {
sb.append(keyArray[i]).append(":").append(String.valueOf(map.get(keyArray[i])).trim());
}
if(i != keyArray.length-1){
sb.append(",");
}
}
return sb.toString();
}
}
支付控制器
这是控制器的代码,作为支付服务的入口。
/** *功能描述:支付宝拉起支付 * @author 666 * @date 2020/7/6 * @return java.util.Map<java.lang.String,java.lang.Object> */ @RequestMapping(value="/geth5AliPayOrderStr",method= RequestMethod.POST) public Map<String,Object> geth5AliPayOrderStr(@RequestBody OrderVo orderVo){ return aliPayAppService.geth5AliPayOrderStr(orderVo); } /** *功能描述:查询订单状态 * @author 666 * @date 2020/7/2 * @param orderVo * @return java.util.Map<java.lang.String,java.lang.Object> */ @RequestMapping(value = "/getStatus", method = RequestMethod.POST) public Map<String, Object> getStatus(@RequestBody OrderVo orderVo) { return aliPayAppService.getStatus(orderVo); } /** * 支付宝官方回调 * @param request * @param response * @return */ @RequestMapping(value="/h5AliNotifyUrl",method= RequestMethod.POST) public String h5AliNotifyUrl(HttpServletRequest request, HttpServletResponse response) { logger.info("官方支付宝异步返回支付结果开始"); String map = aliPayAppService.h5AliNotifyUrl(Utils.map(request, response)); return map; }
到此,支付宝的支付功能就实现了。