2019独角兽企业重金招聘Python工程师标准>>>
一、基础说明
现在开发一个电商APP,最少不了的就是支付,目前最常见的支付有微信支付和支付宝支付,先来介绍支付宝APP支付,其实支付宝的文档说明已经很清楚了,里面有很多demo,你还可以通过沙箱环境去调试支付。蚂蚁金服开放平台地址:https://open.alipay.com/developmentAccess/developmentAccess.htm
下面先贴出一张流程图:
第一步:用户提交支付,APP去带着订单信息请求服务端
第二步:服务端对订单信息进行处理后去请求支付宝(这一步可以设置异步回调和同步回调地址)获取签名后的订单信息,我的理解就是预支付订单信息,(支付宝不存在预付单,可以理解为预付单)
第三步:返回签名后的订单信息给APP客户端
第四步:APP客户端携带预支付订单信息唤起支付宝APP进行支付
第五步:支付完成后,支付宝会同步回调和异步回调,其中异步回调是最重要的,也是回调到服务端的信息,同步回调因为客户端APP和网络的原因,不是十分可靠。
第六步:服务端接收到支付宝异步回调请求,根据请求信息去更新订单信息
第七步:APP客户端通过支付宝交易查询接口去查询刚刚支付的订单状态
第八步:APP客户端根据第七步的结果,处理相应的业务逻辑。例如交易成功显示页面A,交易失败显示页面B。
- 服务端支付实现
作为服务端,我们需要做的是第二步、第三步、第五步,下面详细介绍这几步该如何实现。
第二步:APP客户端提交订单信息到服务端,服务端首先对订单信息进行校验,校验通过后再调用alipay.trade.order.settle(统一收单交易结算接口),操作成功后会返回必要的预支付信息。
public String orderAliPay(String payAmount, String outTradeNo) {
String orderStr = "";
try {
// 实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient(aliPayUrl, APP_ID,
APP_PRIVATE_KEY, "json", CHARSET, APP_PUBLIC_KEY, "RSA2");
// 实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
// SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
// model.setPassbackParams(URLEncoder.encode(body.toString()));
// 描述信息 添加附加数据
model.setSubject("x"); // 商品标题
model.setOutTradeNo(outTradeNo); // 商家订单编号
model.setTimeoutExpress("30m"); // 超时关闭该订单时间
model.setTotalAmount(payAmount); // 订单总金额
model.setProductCode("QUICK_MSECURITY_PAY"); // 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY
request.setBizModel(model);
request.setNotifyUrl(notifyUrl); // 回调地址
try {
// 这里和普通的接口调用不同,使用的是sdkExecute
AlipayTradeAppPayResponse response = alipayClient
.sdkExecute(request);
orderStr = response.getBody();
logger.info("支付宝支付预处理-商户订单号:" + outTradeNo);
logger.info("支付宝支付预处理-结果:" + response.isSuccess());
// 可以直接给客户端请求,无需再做处理。
} catch (AlipayApiException e) {
e.printStackTrace();
}
} catch (Exception e) {
logger.error("Exception", e);
}
return orderStr;
}
参数说明:
payAmount支付金额
outTradeNo:订单流水号(系统唯一)
aliPayUrl 支付宝支付接口https://openapi.alipay.com/gateway.do
APP_ID 支付宝分配给开发者的应用ID
APP_PRIVATE_KEY 应用私密匙
APP_PUBLIC_KEY 应用公密匙
notifyUrl 异步回调地址 必须外网环境下可以正常访问的地址
正确的返回结果是这样的:
{
"alipay_trade_order_settle_response": {
"code": "10000",
"msg": "Success",
"trade_no": "2015070921001004130000127421"
},
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}
把这个信息返回给APP开发人员就行了。如果返回信息错误,就要参考具体的返回码:https://docs.open.alipay.com/api_1/alipay.trade.order.settle
https://docs.open.alipay.com/54/106370/
- 服务端回调实现
@RequestMapping(value = "/aliPayNotify", produces = "text/html;charset=UTF-8", method = RequestMethod.POST)
public String aliPayNotify(HttpServletRequest request,HttpServletResponse response) {
logger.info("支付宝支付完成回调,进入 aliPayNotify 方法");
Map
// 获取支付宝POST过来反馈信息
Map
logger.info("遍历支付成功后支付宝返回的参数requestParams,进行设值");
for (Iterator
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
// 乱码解决,这段代码在出现乱码时使用。
// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
获取返回信息后,需要对信息进行签名进行验证:
//获取支付宝POST过来反馈信息
Map
Map requestParams = request.getParameterMap();
for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
String name = (String) iter.next();
String[] values = (String[]) requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i]
: valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
//boolean AlipaySignature.rsaCheckV1(Map
boolean flag = AlipaySignature.rsaCheckV1(params, alipaypublicKey, charset,"RSA2")
签名校验通过后再进行订单更新业务逻辑处理,注意支付宝建议服务端返回success状态
支付宝是用POST方式发送通知信息,因此该页面中获取参数的方式,如:request.Form(“out_trade_no”) $_POST[‘out_trade_no’];
支付宝主动发起通知,该方式才会被启用;
只有在支付宝的交易管理中存在该笔交易,且发生了交易状态的改变,支付宝才会通过该方式发起服务器通知(即时到账交易状态为“等待买家付款”的状态默认是不会发送通知的);
服务器间的交互,不像页面跳转同步通知可以在页面上显示出来,这种交互方式是不可见的;
第一次交易状态改变(即时到账中此时交易状态是交易完成)时,不仅会返回同步处理结果,而且服务器异步通知页面也会收到支付宝发来的处理结果通知;
程序执行完后必须打印输出“success”(不包含引号)。如果商户反馈给支付宝的字符不是success这7个字符,支付宝服务器会不断重发通知,直到超过24小时22分钟。一般情况下,25小时以内完成8次通知(通知的间隔频率一般是:4m,10m,10m,1h,2h,6h,15h);
程序执行完成后,该页面不能执行页面跳转。如果执行页面跳转,支付宝会收不到success字符,会被支付宝服务器判定为该页面程序运行出现异常,而重发处理结果通知;
cookies、session等在此页面会失效,即无法获取这些数据;
该方式的调试与运行必须在服务器上,即互联网上能访问;
该方式的作用主要防止订单丢失,即页面跳转同步通知没有处理订单更新,它则去处理;
当商户收到服务器异步通知并打印出success时,服务器异步通知参数notify_id才会失效。也就是说在支付宝发送同一条异步通知时(包含商户并未成功打印出success导致支付宝重发数次通知),服务器异步通知参数notify_id是不变的。
支付宝APP支付大概就是这样子,不懂得可以留言我。