一个典型的支付流程如下
在这里,我们只考虑微信和支付宝两种支付方式。只涉及支付网关和商城部分的代码。
阿里和腾讯为提供各种服务端APP和相关开发文档。
https://docs.open.alipay.com/54
事先准备
- 企业或者个体户认证
- 支付宝微信的开发者账号
- 创建应用的公钥和私钥,妥善保管私钥。
手机网页(或者APP中常见的H5)支付如下
腾讯微信封杀了支付宝支付,所以如果需要在微信公众号中支付,你需要跳转到一个网页,再通过网页进行支付。
商家在网页中调用支付宝提供的网页支付接口调起支付宝客户端内的支付模块,商家网页会跳转到支付宝中完成支付,支付完后跳回到商家网页内,最后展示支付结果。若无法唤起支付宝客户端,则在一定的时间后会自动进入网页支付流程。
安卓系统在支付后支付宝回自动切回原APP,IOS必须用户手动切回。
手机APP支付
电脑网站支付
主要支付接口如下
- alipay.trade.page.pay 统一收单下单并支付页面接口
- alipay.trade.refund 统一收单交易退款接口
- alipay.trade.fastpay.refund.query 统一收单交易退款查询接口
- alipay.trade.query 统一收单线下交易查询接口
- alipay.trade.close 统一收单交易关闭接口
- alipay.data.dataservice.bill.downloadurl.query 查询对账单下载地址
JAVA代码
阿里提供的SDK如下
maven依赖如下
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>3.3.87.ALL</version>
</dependency>
发送请求
阿里同时提供了一个简单的应用例子
//实例化客户端
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");
//实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.open.public.template.message.industry.modify
AlipayOpenPublicTemplateMessageIndustryModifyRequest request = new AlipayOpenPublicTemplateMessageIndustryModifyRequest();
//SDK已经封装掉了公共参数,这里只需要传入业务参数
//此次只是参数展示,未进行字符串转义,实际情况下请转义
request.setBizContent(" {" +
" \"primary_industry_name\":\"IT科技/IT软件与服务\"," +
" \"primary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_code\":\"10001/20102\"," +
" \"secondary_industry_name\":\"IT科技/IT软件与服务\"" +
" }");
AlipayOpenPublicTemplateMessageIndustryModifyResponse response = alipayClient.execute(request);
//调用成功,则处理业务逻辑
if(response.isSuccess()){
//.....
}
接收回调
public void aliFeedbackReceiver(HttpServletRequest request, HttpServletResponse response) {
try {
Map<String, String> params = new HashMap<>();
Map requestParams = request.getParameterMap();
for (Object key : requestParams.keySet()) {
String name = (String) key;
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] + ",";
}
params.put(name, valueStr);
}
log.error("AliCallBcak:"+ JSON.toJSONString(params));
//切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
boolean signVerified;
if ("RSA".equals(params.get("sign_type"))) {
signVerified = AlipaySignature.rsaCheckV1(params, Constants.ALI_PUBLIC_KEY_RSA, params.get("charset"), params.get("sign_type"));
} else {
signVerified = AlipaySignature.rsaCheckV1(params, Constants.ALI_PUBLIC_KEY_RSA2, params.get("charset"), params.get("sign_type"));
}
if (signVerified ) {//否则验证失败
//二次验证、入库
Payment payment = ...//从本地数据库中获取这次支付的详细信息
System.out.println("通过验签");
if(payment!=null){//否则证明无此交易
System.out.println(payment.getAccountId());
PropertiesAli propertiesAli = propertiesMapper.selectAliPropertiesByAccountId(payment.getAccountId());
System.out.println(propertiesAli.toString());
//根据阿里开发文档要求,验证回调信息与数据库中订单金额、卖家支付宝用户号和开发者的app_id是否都相符,否则证明订单信息有错误。
if(!Strings.isNullOrEmpty(params.get("total_amount"))
&& Math.abs(payment.getOrderAmount()-Float.valueOf(params.get("total_amount")))<0.01
&& propertiesAli.getAppId().equals(params.get("app_id"))){
System.out.println("AliCallBcak : 二次验证通过");
payment.setPayId(params.get("buyer_id"));
payment.setPayAccount(params.get("buyer_logon_id"));
payment.setSerialNumber(params.get("trade_no"));
if(!Strings.isNullOrEmpty(params.get("buyer_pay_amount"))){
payment.setPayAmount(Float.valueOf(params.get("buyer_pay_amount")));
}
//通过返回的状态和字段判断对应的订单状态
if (params.get(TRADE_STATUS).equals(Constants.STATE_TRADE_CLOSED)
&& params.containsKey("refund_fee")) {
payment.setId(null);
payment.setState(PaymentStatus.PAYMENT_REFUNDSUCCESS.getField());
payment.setPayAmount(-Float.valueOf(params.get("refund_fee")));
paymentMapper.insertSelectiveNotExists(payment);
} else if(params.get(TRADE_STATUS).equals(Constants.STATE_TRADE_SUCCESS)){
payment.setState(PaymentStatus.PAYMENT_SUCCESS.getField());
paymentMapper.updateByLevel(payment);
} else if(params.get(TRADE_STATUS).equals(Constants.STATE_TRADE_FINISHED)){
payment.setState(PaymentStatus.PAYMENT_FINISH.getField());
paymentMapper.updateByLevel(payment);
} else {
payment.setState(PaymentStatus.PAYMENT_CLOSED.getField());
paymentMapper.updateByLevel(payment);
}
//落库,支付生效,插入paymentNote表,插入时判断是否已存在相同状态的回调。阿里可能多次发送回调信息
PaymentNote paymentNote = new PaymentNote();
paymentNote.setAccountId(payment.getAccountId());
paymentNote.setState(params.get(TRADE_STATUS));
paymentNote.setOrderId(payment.getOrderId());
paymentNote.setTenantOrderId(params.get("out_trade_no"));
paymentNote.setSerialNumber(params.get("trade_no"));
paymentNote.setProductName(payment.getProductName());
paymentNote.setPluginId(payment.getPluginId());
paymentNote.setUserid(payment.getUserid());
paymentNote.setPayId(payment.getPayId());
paymentNote.setPayAccount(payment.getPayAccount());
paymentNote.setOrderAmount(payment.getOrderAmount());
paymentNote.setPayAmount(payment.getPayAmount());
paymentNoteMapper.insertSelectiveIfNotExists(paymentNote);
System.out.println("更新note表");
response.getWriter().write("success");
//将回调传给业务方
callBackToTenant.toTenant(payment);
} else {
log.error("订单信息有误");
response.getWriter().write("failure");
}
} else {
log.error("无此订单");
response.getWriter().write("failure");
}
} else {
log.error("验证未通过");
response.getWriter().write("failure");
}
} catch (Exception e){
log.error("aliPaymentFeedback"+e);
}
定时任务
系统需要一些定时任务,比如定时想阿里查询系统中pending的订单状态,在支付大量失败是及时给开发者发送邮件警告。