对接支付宝支付的前提:
1,商户开通支付能力
需要开通 的功能选项有:
创建应用,类型是:网页&移动应用
设置应用的RSA 等各项参数,界面如下:
支付宝推荐使用RSA2 加密方式,老版的加密方式只有RSA 和md5,没有RSA2.
本项目使用RSA2 加密方式
2,对接支付宝依赖的jar包
net.guerlab
sdk-alipay-core
1.0.3
我写了一个专门封装对接支付宝的Service 层
项目结构:
发起支付 控制器(需根据实际情况修改):
下面的"/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"接口
package com.girltest.web.controller.pay;
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) + "
";
// 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 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) + "
";
// 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 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私钥及公钥生成 工具:
上图中2048 对应RSA2
相关项目:
注意:
1,本项目使用新版支付宝支付
2,新版支付宝支付,就算仅使用PC端网页支付,也需要创建一个应用,老版不需要.
3,电脑端网页支付应该使用:
AlipayTradePagePayRequest,而不是AlipayTradePayRequest;
4,对业务参数(非公共参数),进行 json 序列化时,
一定使用阿里自己的类:
JSONWriter writer = new JSONWriter();
String body=writer.write(model, true);