讲述开发之前,先吐槽一下支付宝的官方文档,做的真乱,看的一头雾水,没有微信那么简单、明了。支付宝接口的调用和普通接口的调用不一样,使用的是支付宝官方的SDK的sdkExecute方法。
SDK的下载地址如下:https://docs.open.alipay.com/54/106370/
接口官方的API地址:https://docs.open.alipay.com/204/105465/
1、App支付请求参数说明
接口功能:外部商户App唤起快捷SDK创建订单并支付。
请求参数是商户在与支付宝进行数据交互时,提供给支付宝的请求数据,以便支付宝根据这些数据进一步处理。
1.1公共参数
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例值 |
---|---|---|---|---|---|
app_id | String | 是 | 32 | 支付宝分配给开发者的应用ID | 2014072300007148 |
method | String | 是 | 128 | 接口名称 | alipay.trade.app.pay |
format | String | 否 | 40 | 仅支持JSON | JSON |
charset | String | 是 | 10 | 请求使用的编码格式,如utf-8,gbk,gb2312等 | utf-8 |
sign_type | String | 是 | 10 | 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 | RSA2 |
sign | String | 是 | 256 | 商户请求参数的签名串,详见签名 | 详见示例 |
timestamp | String | 是 | 19 | 发送请求的时间,格式"yyyy-MM-dd HH:mm:ss" | 2014-07-24 03:07:50 |
version | String | 是 | 3 | 调用的接口版本,固定为:1.0 | 1.0 |
notify_url | String | 是 | 256 | 支付宝服务器主动通知商户服务器里指定的页面http/https路径。建议商户使用https | https://api.xx.com/receive_notify.htm |
biz_content | String | 是 | - | 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递,具体参照各产品快速接入文档 |
1.2业务参数
参数 | 类型 | 是否必填 | 最大长度 | 描述 | 示例值 |
---|---|---|---|---|---|
body | String | 否 | 128 | 对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。 | Iphone6 16G |
subject | String | 是 | 256 | 商品的标题/交易标题/订单标题/订单关键字等。 | 大乐透 |
out_trade_no | String | 是 | 64 | 商户网站唯一订单号 | 70501111111S001111119 |
timeout_express | String | 否 | 6 | 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。 注:若为空,则默认为15d。 | 90m |
total_amount | String | 是 | 9 | 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] | 9.00 |
product_code | String | 是 | 64 | 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY | QUICK_MSECURITY_PAY |
goods_type | String | 否 | 2 | 商品主类型:0—虚拟类商品,1—实物类商品 注:虚拟类商品不支持使用花呗渠道 | 0 |
passback_params | String | 否 | 512 | 公用回传参数,如果请求时传递了该参数,则返回给商户时会回传该参数。支付宝会在异步通知时将该参数原样返回。本参数必须进行UrlEncode之后才可以发送给支付宝 | merchantBizType%3d3C%26merchantBizNo%3d2016010101111 |
promo_params | String | 否 | 512 | 优惠参数 注:仅与支付宝协商后可用 | {"storeIdType":"1"} |
extend_params | String | 否 | 业务扩展参数,详见下面的“业务扩展参数说明” | {"sys_service_provider_id":"2088511833207846"} | |
enable_pay_channels | String | 否 | 128 | 可用渠道,用户只能在指定渠道范围内支付 当有多个渠道时用“,”分隔 注:与disable_pay_channels互斥 | pcredit,moneyFund,debitCardExpress |
disable_pay_channels | String | 否 | 128 | 禁用渠道,用户不可用指定渠道支付 当有多个渠道时用“,”分隔 注:与enable_pay_channels互斥 | pcredit,moneyFund,debitCardExpress |
store_id | String | 否 | 32 | 商户门店编号。该参数用于请求参数中以区分各门店,非必传项。 | NJ_001 |
ext_user_info | ExtUserInfo | 否 | 外部指定买家,详见外部用户ExtUserInfo参数说明 |
其它扩展参数,我这里就不详细描述了。
2、代码实现
2.1基础类
AlipayConfig配置类,主要包含支付宝的配置信息
package com.hisap.xql.api.common.ali;
/**
* @Author: QijieLiu
* @Description: 支付宝配置信息
* @Date: Created in 10:39 2018/8/20
*/
public class AlipayConfig {
public static String APP_ID = "xxxxxx";
public static String APP_PRIVATE_KEY = "xxxxxx";//APP私钥
public static String APP_PUBLIC_KEY = "xxxxxx";//APP公钥
public static String ALIPAY_PUBLIC_KEY = "xxxxxx";//支付宝公钥
public static String UNIFIEDORDER_URL = "https://openapi.alipay.com/gateway.do";
public static String NOTIFY_URL = "http://xxx.xxx.xxx.xxx/XqlApi/xxx/paynotify";
public static String CHARSET = "UTF-8";
public static String FORMAT = "json";
public static String SIGNTYPE = "RSA2";
public static String TIMEOUT_EXPRESS = "30m";
}
ResponseJson基础类,主要与APP前端进行交互
package com.hisap.xql.api.common.bean;
public class ResponseJson {
// 结果码
private String code;
// 结果说明
private String message;
// 内容
private Object data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
2.2业务类
AliPayController类
package com.hisap.xql.api.controller;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alipay.api.internal.util.AlipaySignature;
import com.hisap.xql.api.common.ali.AlipayConfig;
import com.hisap.xql.api.common.bean.ResponseJson;
import com.hisap.xql.api.common.constant.CodeMsg;
import com.hisap.xql.api.common.utils.CommonUtil;
import com.hisap.xql.api.service.AliPayService;
/**
* @Author: QijieLiu
* @Description: 支付宝支付信息
* @Date: Created in 10:39 2018/8/20
*/
@Controller
@RequestMapping("/xxx")
public class AliPayController {
private static final Logger logger = LoggerFactory.getLogger(AliPayController.class);
@Autowired
private AliPayService aliPayService;
@RequestMapping("/xxx")
@ResponseBody
public ResponseJson unifiedorder(HttpServletRequest request,
HttpServletResponse response){
ResponseJson responseJson = new ResponseJson();
try{
String requestJson = IOUtils.toString(request.getInputStream(), "utf-8");
logger.info("支付宝统一下单接口请求数据json:" + requestJson);
responseJson = aliPayService.unifiedorder(requestJson);
logger.info("支付宝统一下单接口响应数据json:"
+ JSON.toJSONString(responseJson, SerializerFeature.WriteNullStringAsEmpty));
}catch(Exception e){
e.printStackTrace();
logger.error("支付宝统一下单接口服务端异常,异常信息---" + e.getMessage(), e);
return CommonUtil.createResponseJson(
CodeMsg.SERVER_ERROR_CODE, CodeMsg.SERVER_ERROR_MSG, new JSONObject());
}
return responseJson;
}
}
AliPayService接口类
package com.hisap.xql.api.service;
import java.math.BigDecimal;
import java.util.Map;
import com.hisap.xql.api.common.bean.ResponseJson;
/**
* @Author: QijieLiu
* @Description: 支付宝支付
* @Date: Created in 11:29 2018/8/20
*/
public interface AliPayService {
ResponseJson unifiedorder(String reqJson) throws Exception;
}
AliPayServiceImpl接口实现类
package com.hisap.xql.api.service.impl;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.hisap.xql.api.common.ali.AlipayConfig;
import com.hisap.xql.api.common.ali.AlipayRefund;
import com.hisap.xql.api.common.bean.ResponseJson;
import com.hisap.xql.api.common.constant.CodeMsg;
import com.hisap.xql.api.common.utils.Collections3;
import com.hisap.xql.api.common.utils.CommonUtil;
import com.hisap.xql.api.common.utils.StringUtil;
import com.hisap.xql.api.common.utils.VersionUtil;
import com.hisap.xql.api.dao.XqlOrderGoodsMapper;
import com.hisap.xql.api.model.XqlOrder;
import com.hisap.xql.api.model.XqlOrderGoods;
import com.hisap.xql.api.model.XqlOrderGoodsExample;
import com.hisap.xql.api.model.XqlVersion;
import com.hisap.xql.api.service.AliPayService;
import com.hisap.xql.api.service.CommonService;
import com.hisap.xql.api.service.ErpInterfaceService;
import com.hisap.xql.api.service.WeChatPayService;
import com.hisap.xql.api.service.XqlOrderService;
@Service
public class AliPayServiceImpl implements AliPayService {
private static final Logger logger = LoggerFactory
.getLogger(AliPayServiceImpl.class);
@Autowired
CommonService commonService;
@Autowired
XqlOrderService xqlOrderServiceImpl;
@Autowired
XqlOrderGoodsMapper xqlOrderGoodsMapper;
@Autowired
WeChatPayService weChatPayServiceImpl;
@Autowired
ErpInterfaceService erpInterfaceServiceImpl;
@Override
public ResponseJson unifiedorder(String reqJson) throws Exception {
ResponseJson responseJson = new ResponseJson();
if (StringUtil.isEmpty(reqJson)) {
return CommonUtil.createResponseJson(CodeMsg.PARAM_CHECK_ERROR_CODE, "请求参数不能为空", new JSONObject());
}
/**获取请求参数*/
JSONObject reqObj = JSON.parseObject(reqJson);
/**版本号比较*/
String appVersion = reqObj.getString("appVersion");
if (StringUtil.isEmpty(appVersion)) {
return CommonUtil.createResponseJson(CodeMsg.PARAM_CHECK_ERROR_CODE, "请求参数appVersion不能为空", new JSONObject());
}
XqlVersion xqlVersion = commonService.selectXqlVersion();
if (xqlVersion != null && VersionUtil.compareVersion(xqlVersion.getVersionId(), appVersion) > 0) {
return CommonUtil.createResponseJson(CodeMsg.FORCE_APP_VERSION_UPDATE_CODE, xqlVersion.getVersionUrl(), new JSONObject());
}
String orderNoStr = reqObj.getString("orderNo");
if (StringUtil.isEmpty(orderNoStr)) {
return CommonUtil.createResponseJson(CodeMsg.PARAM_CHECK_ERROR_CODE, "请求参数orderNo不能为空", new JSONObject());
}
BigDecimal orderNo = new BigDecimal(orderNoStr);
/** 订单主信息 */
XqlOrder xqlOrder = xqlOrderServiceImpl
.selectXqlOrderByOrderNo(orderNo);
if (xqlOrder == null) {
return CommonUtil.createResponseJson(CodeMsg.ORDER_NOT_EXIST_CODE,
CodeMsg.ORDER_NOT_EXIST_MSG, new JSONObject());
}
/** 订单明细信息 */
XqlOrderGoodsExample xqlOrderGoodsExample = new XqlOrderGoodsExample();
XqlOrderGoodsExample.Criteria criteria = xqlOrderGoodsExample
.createCriteria();
criteria.andOrderNoEqualTo(orderNo);
List<XqlOrderGoods> xqlOrderGoodsList = xqlOrderGoodsMapper
.selectByExample(xqlOrderGoodsExample);
if (Collections3.isEmpty(xqlOrderGoodsList)) {
return CommonUtil.createResponseJson(
CodeMsg.ORDER_ITEM_NOT_EXIST_CODE,
CodeMsg.ORDER_ITEM_NOT_EXIST_MSG, new JSONObject());
}
XqlOrderGoods xqlOrderGoods = xqlOrderGoodsList.get(0);
/** 验证库存与优惠券接口 */
responseJson = erpInterfaceServiceImpl.checkStockAndCouponBeforePay(orderNo);
if(!responseJson.getCode().equalsIgnoreCase(CodeMsg.SUCCESS_CODE)){
return responseJson;
}
if(xqlOrder.getOrderAmount() == 0){
XqlOrder xqlOrder1 = new XqlOrder();
xqlOrder1.setOrderNo(orderNo);
if(xqlOrder.getDeliveryType() == 0){
xqlOrder1.setOrderStatus(302);
xqlOrder1.setSelfDeliveryStatus((short) 0);
}else{
xqlOrder1.setOrderStatus(201);
}
xqlOrder1.setPayStatus((short) 1);
xqlOrderServiceImpl.updateXqlOrderByOrderNo(xqlOrder1);
return CommonUtil.createResponseJson(
CodeMsg.SUCCESS_CODE, CodeMsg.SUCCESS_MSG, new JSONObject());
}
// 实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(
AlipayConfig.UNIFIEDORDER_URL, AlipayConfig.APP_ID,
AlipayConfig.APP_PRIVATE_KEY, AlipayConfig.FORMAT,
AlipayConfig.CHARSET, AlipayConfig.APP_PUBLIC_KEY,
AlipayConfig.SIGNTYPE);
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setBody(xqlOrderGoods.getGoodsName());
model.setSubject(xqlOrderGoods.getGoodsName());
model.setOutTradeNo(orderNo.toString());
model.setTimeoutExpress(AlipayConfig.TIMEOUT_EXPRESS);
double totalAmount = (double)xqlOrder.getOrderAmount()/100;
model.setTotalAmount(String.valueOf(totalAmount));
model.setProductCode(xqlOrderGoods.getSku().toString());
request.setBizModel(model);
request.setNotifyUrl(AlipayConfig.NOTIFY_URL);
try {
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient
.sdkExecute(request);
// 就是orderString 可以直接给客户端请求,无需再做处理。
String message = response.getBody();
Map<String, String> resMap = new HashMap<String, String>();
resMap.put("payCode", message);
logger.info("支付宝统一下单返回数据:" + message);
responseJson = CommonUtil.createResponseJson(CodeMsg.SUCCESS_CODE,
CodeMsg.SUCCESS_MSG, resMap);
} catch (AlipayApiException e) {
e.printStackTrace();
logger.error("支付宝统一下单接口服务端异常,异常信息---" + e.getMessage(), e);
return CommonUtil.createResponseJson(CodeMsg.SERVER_ERROR_CODE,
CodeMsg.SERVER_ERROR_MSG, new JSONObject());
}
return responseJson;
}
}
好了,支付宝生成APP支付订单就已经完成了,下一章讲述支付宝异步结果通知接口开发。