java 对接支付宝支付
对接支付宝支付的前提:
1,商户开通支付能力
登录蚂蚁金服 开放平台:https://open.alipay.com/platform/home.htm
需要开通 的功能选项有:
创建应用,类型是:网页&移动应用
设置应用的RSA 等各项参数,界面如下:
支付宝推荐使用RSA2 加密方式,老版的加密方式只有RSA 和md5,没有RSA2.
本项目使用RSA2 加密方式
2,对接支付宝依赖的jar包
<!-- https://mvnrepository.com/artifact/net.guerlab/sdk-alipay-core -->
<dependency>
<groupId>net.guerlab</groupId>
<artifactId>sdk-alipay-core</artifactId>
<version>1.0.3</version>
</dependency>
我写了一个专门封装对接支付宝的Service 层
见代码:https://gitee.com/kunlunsoft/pay_service.git
项目结构:
发起支付 控制器(需根据实际情况修改):
下面的"/order/startPay"接口
package com.girltest.web.controller.pay; import com.common.annotation.NoLogin; import com.common.bean.BaseResponseDto; import com.common.dict.Constant2; import com.common.util.RedisHelper; import com.common.util.SystemHWUtil; import com.girltest.dao.AlipayNotifySuccessDao; import com.girltest.dao.BusinessOrderDao; import com.girltest.entity.BusinessOrder; import com.house.ujiayigou.thirdpart.alipay.config.AlipayConfig; import com.house.ujiayigou.thirdpart.alipay.info.PayFormInfo; import com.string.widget.util.RandomUtils; import com.string.widget.util.ValueWidget; import com.time.util.TimeHWUtil; import com.yunmasoft.service.pay.AlipayNotifySuccessService; import com.yunmasoft.service.pay.alipay.AliPayService; import com.yunmasoft.service.pay.alipay.PayOperation; import com.yunmasoft.service.pay.alipay.PayService; import oa.entity.AlipayNotifySuccess; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.math.BigDecimal; import java.util.Date; /** * Created by tonyjiang on 15/8/28. */ @Controller public class PayOrderController { @Resource(name = "alipay") private AliPayService aliPayService; public static org.slf4j.Logger HttpClientRestLogger = LoggerFactory.getLogger("pay_log"); // @Resource // private OrderDetailViewModel orderDetailViewModel; @Resource private AlipayNotifySuccessDao alipayNotifySuccessDao; @Resource private AlipayNotifySuccessService alipayNotifySuccessService; @Resource private BusinessOrderDao businessOrderDao; /*** * 抹掉小数点后面的零头 * * @param orderPrice * @return */ private static String removeDecimalPoint(BigDecimal orderPrice) { String formatPrice = ValueWidget.formatBigDecimal(orderPrice); return formatPrice; } private static void checkOrderStatus(int orderStatus) { if (orderStatus == Constant2.ORDERSTATUS_PAID_ALREADY) { //支付完成,调到支付成功页 // LogicBusinessException.throwException("alreadyPaid"); } else if (orderStatus == Constant2.ORDERSTATUS_CANCELLED) { //已经取消了订单 // LogicBusinessException.throwException("alreadyCancel"); } } /*** * 通过redis 获取订单的支付结果:是否支付成功 * @param request * @param response * @param httpSession * @param model * @param orderNo * @return */ @RequestMapping("/order/payOrderResult") @ResponseBody public String getOrderPayResult(HttpServletRequest request, HttpServletResponse response, HttpSession httpSession, Model model, @RequestParam(value = "orderNo", required = true) String orderNo) { BaseResponseDto baseResponseDto = new BaseResponseDto(PayService.isPaySuccess(orderNo)); return baseResponseDto.toJson(); } /*** * 下单页 * @param model * @param request * @param response * @param release * @return */ @RequestMapping(value = "/order/place", produces = SystemHWUtil.RESPONSE_CONTENTTYPE_JSON_UTF) public String json2(Model model, HttpServletRequest request, HttpServletResponse response , @RequestParam(required = false, defaultValue = "true") Boolean release) { String orderNo = "Xr20001" + RandomUtils.getNextInt(); model.addAttribute("orderNo", orderNo); return "pay/wap/placeorder"; } /*** * 步骤: * 1,获取当前用户信息 * 2,根据订单号查询订单详情 * 3,校验订单状态和支付方式 * 4,跳转到第三方支付 * @param request * @param response * @param httpSession * @param model * @param orderNo * @param callback * @return * @throws Exception */ @RequestMapping("/order/startPay") @NoLogin public String startPay(HttpServletRequest request, HttpServletResponse response, HttpSession httpSession, Model model, @RequestParam(value = "orderNo", required = true) String orderNo, @RequestParam(value = "callback", required = false) String callback , BigDecimal shouldPay) throws Exception { //p判断该订单号是否已经存在 AlipayNotifySuccess alipayNotifySuccess = this.alipayNotifySuccessService.getAlipayNotifySuccess(orderNo); if (null != alipayNotifySuccess) { model.addAttribute("errorMessage", "该订单号已经存在,orderNo:" + orderNo); return "pay/alipay"; } // 判断订单号是否存在 BusinessOrder businessOrder = this.businessOrderDao.get("orderNo", orderNo); if (null == businessOrder) { model.addAttribute("errorMessage", "该订单号不存在,orderNo:" + orderNo); return "pay/alipay"; } /* //保存订单 businessOrder = new BusinessOrder(); businessOrder.setOrderNo(orderNo); businessOrder.setTotalPrice(shouldPay); CreateTimeUtil.fillTime(businessOrder); this.businessOrderDao.add(businessOrder);*/ //获取当前用户信息 //7.应付金额 String strShouldPay = null; if (ValueWidget.isNullOrEmpty(shouldPay)) { shouldPay=businessOrder.getPrice(); } strShouldPay = ValueWidget.formatBigDecimal(shouldPay); //8. 账户 String sellerAccount = AlipayConfig.seller_email; //9.其它 // RedisHelper.getInstance().saveKeyCache(PayService.REDIS_KEY_STORE_ORDER_PAY_TIME, orderNo, TimeHWUtil.formatDate(new Date(), TimeHWUtil.YYYYMMDD_NO_LINE)); String orderName = businessOrder.getProductBaseInfo().getDisplayName(); //10. com.house.ujiayigou.thirdpart.alipay.info.PayFormInfo payFormInfo = new PayFormInfo(); payFormInfo.setOut_trade_no(orderNo); payFormInfo.setSubject(orderName); payFormInfo.setBody(orderName); payFormInfo.setTotal_amount(strShouldPay); payFormInfo.setSeller_id(sellerAccount); String form = aliPayService.preparePostRequest(payFormInfo); HttpClientRestLogger.error("form:" + form); model.addAttribute("form", form); return "pay/alipay"; } /** * 1,获取订单; * 2,价格; * 3,Identify; * 4,第三方支付相关接口service; * 5,callback * 6,coupon * 7,应付金额 * 8,账户 * 9,其它 * 10, * * @param access_token * @param orderNo * @param payType * @param callback * @return */ public String postThirdPayAction(String access_token, String orderNo, String payType, String callback) { //订单 /* OrderInfoBean orderInfo = orderBusiness.getOrderInfoByOrderNo(access_token, orderNo); if (orderInfo == null || orderInfo.getItems().size()==0) { LogicBusinessException.throwException("cannotMatchOrder"); } checkOrderStatus(orderInfo.orderStatus); if (orderInfo.orderStatus != Constant.ORDERSTATUS_ORDERS_SUBMITTED) { LogicBusinessException.throwException("20506"); }*/ //2.价格 BigDecimal price = null;//orderBusiness.getOrderMoney(orderInfo); if (price == null) { // LogicBusinessException.throwException("cannotPay"); } //3.Identify String identifier = null;//orderBusiness.getPayIdentify(orderNo, access_token, userInfo, price); PayOperation service = null;//(PayOperation) SpringMVCUtil.getBean( payType); if (service == null) { // LogicBusinessException.throwException("service is null"); } //5.callback if (ValueWidget.isNullOrEmpty(callback)) { callback = "https:/order/view?orderNo=" + orderNo /*+ "&orgId=" + orgId*/;//千万不要URL转码 } //6.coupon BigDecimal coupon = null; /*if (Constant.useDebugCouponValue) { coupon = new BigDecimal(Constant.couponValue); } else {*/ coupon = new BigDecimal(0); // } //7.应付金额 BigDecimal shouldPay = price.subtract(coupon.compareTo(price) < 0 ? coupon : new BigDecimal(0));// String strShouldPay = ValueWidget.formatBigDecimal(shouldPay); //8. 账户 String sellerAccount = AlipayConfig.seller_email; //9.其它 RedisHelper.getInstance().saveKeyCache(PayService.REDIS_KEY_STORE_ORDER_PAY_TIME, orderNo, TimeHWUtil.formatDate(new Date(), TimeHWUtil.YYYYMMDD_NO_LINE)); String orderName = "xxx服务"; //10. com.house.ujiayigou.thirdpart.alipay.info.PayFormInfo payFormInfo = new PayFormInfo(); payFormInfo.setOut_trade_no(orderNo); payFormInfo.setSubject(orderName); payFormInfo.setBody(orderName); payFormInfo.setTotal_amount(strShouldPay); payFormInfo.setSeller_id(sellerAccount); String form = service.preparePostRequest(payFormInfo); return form; } }
支付回调控制器(需根据实际情况修改):
下面的"/notify"接口
import com.common.dict.Constant2; import com.common.util.*; import com.yunmasoft.service.pay.alipay.PayOperation; import com.io.hw.json.HWJacksonUtils; import com.string.widget.util.ValueWidget; import oa.util.SpringMVCUtil; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; @Controller @RequestMapping("/pay") public class PayController { public static Logger logger = Logger.getRootLogger(); @RequestMapping(value = "notify", produces = {SystemHWUtil.RESPONSE_CONTENTTYPE_JSON_UTF}) @ResponseBody public String callback( @RequestParam(required = false) String method, @RequestParam(required = false) String out_trade_no/*订单号*/, @RequestParam(required = false) String trade_no, @RequestParam(required = false) String trade_status, @RequestParam(required = false) String extra_common_param, @RequestParam(required = false) String notifyUrl, @RequestParam(required = false) String body/*只有支付宝手机网站支付才有*/, HttpServletResponse response, HttpServletRequest request) { //userIdAndLoginnameAndOrgId 在请求参数中 // 1.获取订单号 if (ValueWidget.isNullOrEmpty(out_trade_no)) { out_trade_no = request.getParameter("outer_trade_no"); } // 2.畅捷支付没有extra_common_param,兼容畅捷支付 if (ValueWidget.isNullOrEmpty(extra_common_param) && !ValueWidget.isNullOrEmpty(notifyUrl)) {//added by huangweii Map argMap = RequestUtil.parseQueryString(notifyUrl); extra_common_param = (String) argMap.get("chanpay"); } logger.info(HWJacksonUtils.getJsonP(request.getParameterMap())); // 3.兼容extra_common_param为空的情况 boolean isAlipayMobile = false; if (ValueWidget.isNullOrEmpty(extra_common_param)) {//added by huangweii,extra_common_param为空,说明是xx支付 String extra_common_paramSuffix = RedisHelper.getInstance().getCache("store_" + out_trade_no + "_Identifier") + "::" + RedisHelper.getInstance().getCache("store_" + out_trade_no + "_userinfo"); if (ValueWidget.isNullOrEmpty(request.getParameter("out_trade_no"))) { extra_common_param = "chanpay::" + extra_common_paramSuffix; } else { isAlipayMobile = true; extra_common_param = "alipay::" + extra_common_paramSuffix; } } // 4. 缓存日志 // String currentCache = RedisHelper.getInstance().getKeyCache(Constant.REDIS_ID_STORE, out_trade_no) + "<br>"; // RedisHelper.getInstance().saveKeyCacheAndExpire(Constant.REDIS_ID_STORE, out_trade_no, currentCache +request.getRequestURI()+ extra_common_param + ",notifyUrl:" + notifyUrl); logger.info("extra_common_param:" + extra_common_param); // 获取支付宝POST过来反馈信息 Map requestParams = request.getParameterMap(); Map<String, String> params = WebServletUtil.getParamMap(requestParams); // params.put("userIdAndLoginnameAndOrgId", userIdAndLoginnameAndOrgId); // 5.获取service PayOperation service = null; if (extra_common_param != null && extra_common_param.contains("::")) { String[] info = extra_common_param.split("::"); service = (PayOperation) SpringMVCUtil.getBean(request, info[0]); } if (service == null) { return "fail"; } // 6.验签 String verity_ok = service.verify(WebServletUtil.getParamMap(requestParams)); if (!verity_ok.equals("success")) { logger.error(params.toString()); logger.error("verify fail"); return verity_ok; } // 7. 缓存日志 // currentCache = RedisHelper.getInstance().getKeyCache(Constant.REDIS_ID_STORE, out_trade_no) + "<br>"; // RedisHelper.getInstance().saveKeyCacheAndExpire(Constant.REDIS_ID_STORE, out_trade_no, currentCache + "verity->" + verity_ok); // 8.回调,调用确认订单接口 try { String pay_ok = service.payNotify(out_trade_no, trade_no, trade_status, params); if (pay_ok != null) { logger.debug(pay_ok + ""); return pay_ok; // 请不要修改或删除 } } catch (Exception ex) { logger.error("payNOtify error:", ex); return "fail"; } return "fail"; } @RequestMapping(value = "payResult") public String getPaymentResult(@RequestParam(required = false) String orderid, @RequestParam(required = false) String userid, HttpServletRequest request, HttpServletResponse response, Model model) throws IOException { logger.info("payResult orderid:" + orderid); // 获取支付宝POST过来反馈信息 Map requestParams = request.getParameterMap(); Map<String, String> params = WebServletUtil.getParamMap(requestParams); String orderId = orderid; if (ValueWidget.isNullOrEmpty(orderId)) { orderId = params.get("orderid"); } if (orderId == null) { orderId = params.get("out_trade_no"); } //畅捷支付点击[返回商家] TODO if (orderId == null) { orderId = params.get("orderId"); } logger.info("payResult params:" + params); String payResult = params.get("payResult"); String extra_common_param = params.get("extra_common_param"); logger.info("payResult extra_common_param:" + extra_common_param); if (!Constant2.CHANPAY_ORDER_RESULT_SUCCESS.equals(payResult)) { response.sendRedirect("/order/payOrder?orderId=" + orderId /*+ "&orgId=" + service.getOrgId(orderId)*/); return null; } response.sendRedirect("/order/payComplete?orderId=" + orderId /*+ "&orgId=" + service.getOrgId(orderId)*/); return null; } }
实际支付demo:
各文档:
电脑网站支付快速接入
https://docs.open.alipay.com/270/105899/
API 详细说明:
https://docs.open.alipay.com/270/alipay.trade.page.pay/
服务端SDK
https://docs.open.alipay.com/54/103419
RSA私钥及公钥生成
https://docs.open.alipay.com/58/103242/
RSA私钥及公钥生成 工具:
https://gitee.com/kunlunsoft/http_request_tool
上图中2048 对应RSA2
相关项目:
https://github.com/liuyu520/io0007
https://gitee.com/kunlunsoft/oa_framework
注意:
1,本项目使用新版支付宝支付
即时到账新老版本接口对比
https://docs.open.alipay.com/270/106759
2,新版支付宝支付,就算仅使用PC端网页支付,也需要创建一个应用,老版不需要.
3,电脑端网页支付应该使用:
AlipayTradePagePayRequest,而不是AlipayTradePayRequest;
4,对业务参数(非公共参数),进行 json 序列化时,
一定使用阿里自己的类:
JSONWriter writer = new JSONWriter();
String body=writer.write(model, true);