上一篇讲了微信支付的三种支付方式(扫码支付、App支付、小程序支付)的接入,今天继续说说以电商项目为代表的Java后端支付宝支付接入及实现。
支付宝支付接入实现在接入前,我们先看看支付宝支付的官方文档:https://open.alipay.com/developmentDocument.htm
本次我主要讲的是“App支付”及“电脑网站支付(扫码支付)”的接入实现。一,准备工作需要有一个支付宝账号,然后登录进入支付宝开放平台,如果之前没有申请入驻过开放平台,第一次进入后需要填相关资料申请,再进入开发者中心,选择创建移动应用。
在选择“支付接入”后,填写对应的资料并添加相应的功能。等申请的应用通过后,再配置相应的开发者设置选项。包含回调地址、接口加密方式及配置密钥。
密钥配置可参考官方文档:https://docs.open.alipay.com/291/106103/。
二,业务流程及原理
扫码支付业务流程图:
App支付业务流程图:
支付宝支付(扫码支付和App支付)业务流程大致总结如下: 1,客户在商户平台下单,生成订单信息;2,调用商户平台的支付接口,商户后台封装参数通过SDK发起支付请求;3,扫码支付方式支付宝后台返回一个html片段(form表单代码),输出到tml页面,展示付款二维码;App支付方式时返回App端发起付款请求的参数,后台再传给App,App端通过请求SDK调起支付宝支付服务;4,扫码支付方式时,客户可用手机的支付宝直接扫码支付或点击右边的“登录账户付款”,登录支付宝支付;app支付时在上一步已经调起了付款服务,只需输入密码付款。5,付款成功后,会跳转到支付宝支付成功的页面,3s跳转到商户的支付成功展示的页面;同时支付宝后台通过异步线程调用回调接口,更新订单付款记录的状态。注意:回调接口只要成功要给支付宝返回“success”,否则支付宝后台认为调用不成功,会多次重复调用。三,编码接入实现
首先,我们需要到支付宝开放平台下载Java版的SDK,然后可以手动将下载到本地的jar导入到maven库,然后在pom文件里配置,依赖如下:
<!-- 支付宝SDK -->
<dependency>
<groupId>com.alipay</groupId>
<artifactId>sdk-java</artifactId>
<version>${alipay-sdk-java.version}</version>
</dependency>
注:我这里的版本号是 20180104135026
使用SDK很是方便,它里面已经封装了签名&验签、HTTP 接口请求等基础功能。只需要我们根据SDK提供的方法传参使用。
AlipayClient alipayClient = new DefaultAlipayClient(URL,APP_ID,APP_PRIVATE_KEY,FORMAT,CHARSET,ALIPAY_PUBLIC_KEY,SIGN_TYPE);
Service接口:
public interface PaymentService {
/**
* <p>支付宝扫码支付</p>
*
* @param orderNo
* @param money
* @return
* @throws Exception
*/
Map<String, String> aliQrPayment(String orderNo, double money) throws Exception;
/**
* <p>支付宝App支付</p>
*
* @param orderNo
* @param money
* @return
* @throws Exception
*/
Map<String,String> aliAppPayment(String orderNo, double money) throws Exception;
/**
* <p>支付宝回调服务</p>
*
* @param orderId
* @param tradeNo
* @return
* @throws Exception
*/
int aliNotify(String orderNo,String tradeNo) throws Exception;
}
Service实现类代码(部分):
@Service(value = "paymentService")
public class PaymentServiceImpl implements PaymentService {
private static Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);
@Autowired
private PaymentRecordMapper paymentRecordMapper;
@Override
public Map<String, String> aliQrPayment(String orderNo, double money) throws Exception {
LOGGER.info("【支付宝扫码支付】 订单编号="+orderId+",支付金额="+money);
double payAmount = PayUtil.getPayAmountByEnv(PROJECT_ENV,money);
Map<String,String> retMap = new HashMap<String, String>();
//添加或更新支付记录到数据库
int results = this.addOrUpdatePaymentRecord(orderNo, payAmount, PayConstant.PAY_METHOD_ALI, PayConstant.PAY_TRADE_TYPE_QR, false, null,null);
if(results < 0){
retMap.put("returnCode", "FAIL");
retMap.put("returnMsg", "此订单已支付!");
LOGGER.info("【支付宝扫码支付】 此订单已支付!");
}else if(results == 0){
retMap.put("returnCode", "FAIL");
retMap.put("returnMsg", "支付记录生成或更新失败!");
LOGGER.info("【支付宝扫码支付】 支付记录生成或更新失败!");
}else{
//扫码支付
String param = PayConstant.ALI_PAY_WEB_PARAMS;
//返回页面
String returnUrl = "http://" + PayConfig.PC_SHOP_DOMAIN + PayConstant.ALI_PAY_RETURN_URL+orderNo;
//调用SDK获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", PayConfig.ALI_APP_ID, PayConfig.ALI_PAY_PRIVATE_KEY, "json", "UTF-8", PayConfig.ALI_PAY_PUBLIC_KEY, "RSA");
AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();//创建API对应的request
alipayRequest.setReturnUrl(returnUrl);
alipayRequest.setNotifyUrl(this.getNotifyUrl(PayConstant.PAY_TYPE_ALI));
alipayRequest.setBizContent("{" +
"\"out_trade_no\":\""+ orderId +"\"," +
"\"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
"\"total_amount\":\""+payAmount+"\"," +
"\"subject\":\""+param+"\"," +
"\"body\":\""+param+"\"" +
"}");
String form = alipayClient.pageExecute(alipayRequest).getBody(); //调用SDK生成表单
//封装返回实体
retMap.put("returnCode", PayConstant.SUCCESS);
retMap.put("returnMsg", PayConstant.OK);
retMap.put("body", form);
}
return retMap;
}
@Override
@Transactional(readOnly=false,rollbackFor={Exception.class})
public Map<String,String> aliAppPayment(String orderNo, double money) throws Exception{
LOGGER.info("【支付宝App支付】开始下单, 订单编号="+orderId);
Map<String,String> resultsMap = new HashMap<String,String>();
//根据不同服务环境生成支付金额
double payAmount = PayUtil.getPayAmountByEnv(PROJECT_ENV, money);
//添加或更新支付记录
int results = this.addOrUpdatePaymentRecord(.....);
if(flag < 0){
resultsMap.put("returnCode", "FAIL");
resultsMap.put("returnMsg", "此订单已支付!");
LOGGER.info("【支付宝App支付】 此订单已支付!");
}else if(flag == 0){
resultsMap.put("returnCode", "FAIL");
resultsMap.put("returnMsg", "支付记录生成或更新失败!");
LOGGER.info("【支付宝App支付】 支付记录生成或更新失败!");
}else{
//获得初始化的AlipayClient
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
PayConfig.ALI_APP_ID, PayConfig.ALI_PAY_PRIVATE_KEY, "json", "UTF-8", PayConfig.ALI_PAY_PUBLIC_KEY, "RSA");
//实例化具体API对应的request类
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody(BaseConstants.PLATFORM_COMPANY_NICKAME);
model.setSubject(BaseConstants.PLATFORM_COMPANY_PREFIX+orderNo);
model.setOutTradeNo(orderNo);
model.setTimeoutExpress("24h");
model.setTotalAmount(String.valueOf(money));
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
request.setNotifyUrl(this.getNotifyUrl(PayConstant.PAY_TYPE_ALI));
//这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderString = response.getBody();
resultsMap.put("payParams", orderString);
resultsMap.put("returnCode", "SUCCESS");
resultsMap.put("returnMsg", "OK");
}
return resultMap;
}
@Override
@Transactional(readOnly=false,rollbackFor={Exception.class})
public int aliNotify(String orderNo,String tradeNo) throws Exception{
LOGGER.info("【支付宝支付回调】回调数据:"+"orderNo="+orderNo+",tradeNo="+tradeNo);
//TODO 更新支付订单记录业务代码
//此处添加自己的更新数据业务代码
return 0;
}
}
支付Controller接口层:
@RestController
@RequestMapping(value = "/api/payment/")
public class PaymentController {
private static Logger logger = LoggerFactory.getLogger(PaymentController.class);
@Value("${spring.profiles.active}")
private String PROJECT_ENV;
@Value("${error.page}")
private String ERROR_PAGE;
@Autowired
private PaymentService paymentService;
@Autowired
private RedisCacheService cacheService;
/**
* 支付宝扫码支付接口
*
* @param request
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="aliQrPay", method=RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
public JSONObject qrPay(HttpServletRequest request) throws Exception {
String requestStr = RequestStr.getRequestStr(request);
if (StringUtils.isEmpty(requestStr)) {
throw new ParamException();
}
JSONObject jsonObj = JSONObject.parseObject(requestStr);
if(StringUtils.isEmpty(jsonObj.getString("orderNo"))){
throw new ParamException();
}
//验证订单是否存在
String orderNo = jsonObj.getString("orderNo");
JSONObject json = getPayOrder(orderNo);
if(json == null){
return AjaxUtil.renderFailMsg("订单不存在!");
}else if(json.getDouble("payPrice") == null || json.getDouble("payPrice") < 0.01){
return AjaxUtil.renderFailMsg("订单有误,请确认!");
}else if(json.getInteger("orderStatus") != OrderConstant.PORDER_STATUS_YTJ){
String msg = orderInfo.getpStatus() == OrderConstant.PORDER_STATUS_YFK?"此订单已支付!":"无效的订单,请确认!";
return AjaxUtil.renderFailMsg(msg);
}else{
//支付宝扫码支付
Map<String, String> resMap = paymentService.aliQrPayment(orderNo, json.getDouble("payPrice"));
if(PayConstant.SUCCESS.equals(resMap.get("returnCode")) && PayConstant.OK.equals(resMap.get("returnMsg"))){
String code = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
cacheService.setCacheToRedis(code, resMap.get("body"), BusinessConstant.IMAGE_VERIFY_CODE_EXPIRE);
logger.info("【Web扫码支付服务】支付宝支付下单成功!");
return AjaxUtil.renderSuccessMsg(code,"ok");
}
logger.info("【Web扫码支付服务】支付宝支付下单失败!原因:"+resMap.get("returnMsg"));
return AjaxUtil.renderFailMsg(resMap.get("returnMsg"));
}
}
/**
* 支付宝App支付接口
*
* @param request
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="aliAppPay", method=RequestMethod.POST, produces = {"application/json;charset=UTF-8"})
public JSONObject aliAppPay(HttpServletRequest request) throws Exception {
String requestStr = RequestStr.getRequestStr(request);
if (StringUtils.isEmpty(requestStr)) {
throw new ParamException();
}
JSONObject jsonObj = JSONObject.parseObject(requestStr);
if(StringUtils.isEmpty(jsonObj.getString("orderNo"))){
throw new ParamException();
}
//验证订单是否存在
String orderNo = jsonObj.getString("orderNo");
JSONObject json = getPayOrder(orderNo);
if(json == null){
return AjaxUtil.renderFailMsg("订单不存在!");
}else if(json.getDouble("payPrice") == null || json.getDouble("payPrice") < 0.01){
return AjaxUtil.renderFailMsg("订单有误,请确认!");
}else if(json.getInteger("orderStatus") != OrderConstant.PORDER_STATUS_YTJ){
String msg = orderInfo.getpStatus() == OrderConstant.PORDER_STATUS_YFK?"此订单已支付!":"无效的订单,请确认!";
return AjaxUtil.renderFailMsg(msg);
}else{
//支付宝app支付
Map<String, String> resMap = paymentService.aliAppPayment(orderNo, json.getDouble("payPrice"));
if("SUCCESS".equals(resMap.get("returnCode")) && "OK".equals(resMap.get("returnMsg"))){
//统一下单成功
resMap.remove("returnCode");
resMap.remove("returnMsg");
logger.info("【App支付服务】支付宝支付单成功!");
return AjaxUtil.renderSuccessMsg(resMap);
}
logger.info("【App支付服务】支付宝支付单失败!原因:"+resMap.get("returnMsg"));
return AjaxUtil.renderFailMsg(resMap.get("returnMsg"));
}
}
/**
* <p>支付宝扫码支付获取二维码页面</p>
*
* @param request
* @param response
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="getAliPayHtml", method=RequestMethod.GET, produces = {"application/json;charset=UTF-8"})
public void getAlipayInfo(HttpServletRequest request,HttpServletResponse response) throws Exception{
//获取生成二维码的url
try {
//获取参数
String code = request.getParameter("payCode");
if(!StringUtils.isEmpty(code) && cacheService.isExistKey(code)){
//调用业务层
String html = (String) cacheService.getCacheByKey(code);
response.setContentType("text/html;charset=" + "UTF-8");
response.getWriter().write(html);//直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
}else{
String html = error_page;
response.setContentType("text/html;charset=" + "UTF-8");
response.getWriter().write(html);//直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
}
}catch(Exception e){
logger.error(e.getMessage());
String html = error_page;
response.setContentType("text/html;charset=" + "UTF-8");
response.getWriter().write(html);//直接将完整的表单html输出到页面
response.getWriter().flush();
response.getWriter().close();
}
}
/**
* 支付宝支付完成回调
*
* @param request
* @param response
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value="aliNotify")
public String aliNotify(HttpServletRequest request,HttpServletResponse response) throws Exception {
String tradeStatus = request.getParameter("trade_status");//支付状态
if (tradeStatus.equals("TRADE_FINISHED") || tradeStatus.equals("TRADE_SUCCESS")) {
String orderNo= request.getParameter("out_trade_no"); //商户订单号
String tradeNo = request.getParameter("trade_no"); //支付宝订单号
//String total_fee = request.getParameter("total_amount");//支付金额
if(paymentService.aliNotify(orderNo,tradeNo) > 0){
logger.info("【支付宝支付回调响应】 响应内容:success!");
return "success";
}
}
logger.info("【支付宝支付回调响应】 响应内容:fail!");
return "fail";
}
}
四,测试
选择要购买的商品,然后下单,再去发起支付。
点击去支付,调用Java后台的支付接口
注:可能有人发现支付时金额怎么从625变成0.01了,这是后台做了处理,方便测试环境测试。支付成功后,跳转到支付宝付款成功页面。
本文章只是给大家讲解原理和接入思路及实现,所以文章里只放了核心业务代码。还有好多工具类及配置等代码,全放到这里来肯定也不现实,如果有需要完整代码的可关注公众号获取。获取方式扫码关注公众号;找到“关于我”>>>"联系我" ;添加我个人微信获取;
推荐阅读:
SpringBoot电商项目实战 — ElasticSearch接入实现
SpringBoot电商项目实战 — 商品的SPU/SKU实现
扫码关注公众号,发送关键词获取相关资料:
发送“Springboot”领取电商项目实战源码;
发送“SpringCloud”领取cloud学习实战资料;