一、配置
配置
-
用户注册
首先用公司相关信息在蚂蚁金服开放平台注册一个支付宝账户(https://openhome.alipay.com/platform/home.htm)
然后在进入开放平台后进入开发者中心>网页&移动应用接入中
接着和支付宝签约,接入支付功能(手机网站支付)
-
配置和获取相关开发信息
进入申请的支付功能页面,配置授权回调地址和密钥
ok,现在我们获取了APPID、支付宝网关、授权回调地址、商户私钥、应用公钥、支付宝公钥等相关信息,开发时我将会把他们写到配置文件中
另外,我们还需要一个支付宝的SDK,请大家自行下载
到此,所需的全部准备工作完成,大家可以愉快的开发了
二、开发
支付逻辑
-
支付逻辑
- 商户系统调用支付宝预下单接口alipay.trade.precreate,获得该订单二维码图片地址。
- 发起轮询获得支付结果:等待5秒后调用交易查询接口alipay.trade.query通过支付时传入的商户订单号(out_trade_no)查询支付结果(返回参数TRADE_STATUS),如果仍然返回等待用户付款(WAIT_BUYER_PAY),则再次等待5秒后继续查询,直到返回确切的支付结果(成功TRADE_SUCCESS 或 已撤销关闭TRADE_CLOSED),或是超出轮询时间。在最后一次查询仍然返回等待用户付款的情况下,必须立即调用交易撤销接口alipay.trade.cancel将这笔交易撤销,避免用户继续支付。
- 这里是列表文本除了主动轮询,也可以通过接受异步通知获得支付结果。
- 和微信支付差异的地方就在支付宝返回查询用户付款状态的方式,支付宝除了主动轮询查询支付状态,还可以异步获取。在项目里我们将这两种方式交叉使用。
公共请求参数
-
公共请求参数
与支付无关的业务逻辑
-
与支付无关的业务逻辑
这部分代码已经在上篇文章微信支付中贴出,这里就不再重新贴一遍了
封装固定参数
- 封装固定参数
package com.andata.conf;
/* *
*类名:AlipayConfig
*功能:基础配置类
*详细:设置帐户有关信息及返回路径
*提示:如何获取安全校验码和合作身份者ID
*1.用您的签约支付宝账号登录支付宝网站(www.alipay.com)
*2.点击“商家服务”(https://b.alipay.com/order/myOrder.htm)
*3.点击“查询合作者身份(PID)”、“查询安全校验码(Key)”
*安全校验码查看时,输入支付密码后,页面呈灰色的现象,怎么办?
*解决方法:
*1、检查浏览器配置,不让浏览器做弹框屏蔽设置
*2、更换浏览器或电脑,重新登录查询。
*/
public class AlipayConfig {
//ServiceURL
public static String ServiceUrl="https://openapi.alipay.com/gateway.do";
//APP支付宝支付业务:app_id
public static String app_id = "20180xxxxxxxxxxxxxx";
// 商户的私钥,使用支付宝自带的openssl工具生成。
public static String private_key = "xxxxxxxxxxxxxxxxxxx";
// 应用的公钥,无需修改该值
public static String ali_public_key = "xxxxxxxxxxxxxxxxxxxxx";
// 支付宝的公钥,去open.alipay.com对应应用下查看。
public static String zfb_public_key = "xxxxxxxxxxxxxxxxxxxxx";
// 字符编码格式 目前支持 gbk 或 utf-8
public static String input_charset = "UTF-8";
}
AliPayController
package com.andata.controller;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCancelRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeCancelResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.andata.conf.AlipayConfig;
import com.andata.pojo.ProductOrders;
import com.andata.service.ProOrdersService;
import com.andata.util.QRCodeUtil;
import net.sf.json.JSONObject;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Controller
public class AliPayController {
@Resource
private ProOrdersService proOrdersService;
/**
* 支付逻辑
* 1、商户系统调用支付宝预下单接口alipay.trade.precreate,获得该订单二维码图片地址。
* 2、发起轮询获得支付结果:等待5秒后调用交易查询接口alipay.trade.query通过支付时传入的商户订单号(out_trade_no)查询支付结果(返回参数TRADE_STATUS),
如果仍然返回等待用户付款(WAIT_BUYER_PAY),则再次等待5秒后继续查询,直到返回确切的支付结果(成功TRADE_SUCCESS 或 已撤销关闭TRADE_CLOSED),
或是超出轮询时间。在最后一次查询仍然返回等待用户付款的情况下,必须立即调用交易撤销接口alipay.trade.cancel将这笔交易撤销,避免用户继续支付。
* 3、除了主动轮询,也可以通过接受异步通知获得支付结果
*/
/**
* 阿里订单取消接口
* 请求返回值示例:
* {"alipay_trade_cancel_response":{"code":"40004","msg":"Business Failed","sub_code":"ACQ.INVALID_PARAMETER","sub_msg":"参数无效","out_trade_no":"20150320010101002",
* "retry_flag":"N"},"sign":""}
* @return
* @throws AlipayApiException
*/
@RequestMapping(value = "aliremove.do")
@ResponseBody
private Map<String, Object> remove(@Param("orderid")String orderid) throws AlipayApiException {
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.ServiceUrl, AlipayConfig.app_id, AlipayConfig.private_key, "json", AlipayConfig.input_charset, AlipayConfig.zfb_public_key, "RSA2"); //获得初始化的AlipayClient
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();//创建API对应的request类
Map<String,Object> returnmap=new HashMap<>();
try {
//判断订单是否完成支付,完成支付的订单禁止撤销
int i = this.proOrdersService.selectOrderStatus(orderid);
if (i==1)
{
returnmap.put("type","0");
returnmap.put("data","订单已支付,撤销订单失败");
}else {
Map<String,Object> map=new HashMap<>();
map.put("out_trade_no",orderid);
String returndata = JSONObject.fromObject(map).toString();
request.setBizContent(returndata); //设置业务参数
AlipayTradeCancelResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
JSONObject query_response = JSONObject.fromObject(response.getBody()).getJSONObject("alipay_trade_cancel_response");
String code = query_response.getString("code");
String msg = query_response.getString("msg");
if (code.equals("10000") && msg.equals("Success")) {
System.out.println("阿里订单取消接口调用成功!");
returnmap.put("type","1");
returnmap.put("data","撤销订单成功");
}else {
System.out.println("撤销订单请求失败");
returnmap.put("type","0");
returnmap.put("data",query_response.getString("sub_msg"));
System.out.println(query_response);//返回失败信息
}
//根据response中的结果继续业务逻辑处理
}
}catch (Exception e)
{
System.out.println("撤销订单异常");
e.printStackTrace();
}
return returnmap;
}
/**
* 阿里交易查询接口
* out_trade_no 支付时传入的商户订单号,与trade_no必填一个
* trade_no 支付时返回的支付宝交易号,与out_trade_no必填一个
* 请求返回值示例:
* {"alipay_trade_query_response":{"code":"10000","msg":"Success","buyer_logon_id":"159****4027","buyer_pay_amount":"0.01","buyer_user_id":"2088012351746164",
* "fund_bill_list":[{"amount":"0.01","fund_channel":"PCREDIT"}],"invoice_amount":"0.01","out_trade_no":"020910312752381","point_amount":"0.00","receipt_amount":"0.01",
* "send_pay_date":"2018-02-09 10:31:45","total_amount":"0.01","trade_no":"2018020921001004160275738069","trade_status":"TRADE_SUCCESS"},
* "sign":""}
* @return
* @throws Exception
*/
@RequestMapping(value="/aliget")
@ResponseBody
private Map<String, Object> get(@Param("orderid")String orderid) throws AlipayApiException{
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.ServiceUrl, AlipayConfig.app_id, AlipayConfig.private_key, "json", AlipayConfig.input_charset, AlipayConfig.zfb_public_key, "RSA2"); //获得初始化的AlipayClient
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();//创建API对应的request类
Map<String,Object> map=new HashMap<>();
map.put("out_trade_no",orderid);
String returndata = JSONObject.fromObject(map).toString();
request.setBizContent(returndata); //设置业务参数
AlipayTradeQueryResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
JSONObject jsonObject = JSONObject.fromObject(response.getBody()).getJSONObject("alipay_trade_query_response");
String code = jsonObject.getString("code");
String msg = jsonObject.getString("msg");
Map<String,Object> returnmap=new HashMap<>();
if (code.equals("10000") && msg.equals("Success"))
{
System.out.println("交易查询请求成功");
String trade_status = jsonObject.getString("trade_status");
if (trade_status.equals("TRADE_SUCCESS"))
{
returnmap.put("type","1");
returnmap.put("data","支付成功");
}else {
returnmap.put("type","0");
returnmap.put("data",trade_status);
}
}else {
System.out.println("交易查询请求失败");
returnmap.put("type","0");
returnmap.put("data",jsonObject.getString("sub_msg"));
System.out.println(jsonObject);//返回失败信息
}
return returnmap;
}
/**
* @name 预下单请求,阿里获取二维码接口
* @throws AlipayApiException
* @Param out_trade_no 商户订单号,64个字符以内、只能包含字母、数字、下划线;需保证在商户端不重复
* @Param total_amount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] 如果同时传入了【打折金额】,【不可打折金额】,
* 【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
* @Param subject 订单标题
* @Param store_id 商户门店编号
* @Param timeout_express 该笔订单允许的最晚付款时间,逾期将关闭交易。
* 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
*/
@RequestMapping(value="/alipay.do")
@ResponseBody
private Map list(HttpServletResponse responses, @Param("orderid")String orderid) throws AlipayApiException{
AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.ServiceUrl, AlipayConfig.app_id, AlipayConfig.private_key, "json", AlipayConfig.input_charset, AlipayConfig.zfb_public_key, "RSA2"); //获得初始化的AlipayClient
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();//创建API对应的request类
Map<String,Object> map=new HashMap<>();
if (orderid.isEmpty())
{
map.put("type","2");
map.put("data","订单号不能为空");
return map;
}
//设置回调地址
request.setNotifyUrl("http://www.andata.com.cn/aliPayCallBack.do");
//根据订单号查询订单信息
ProductOrders productOrders = this.proOrdersService.selectByOrderId(orderid);
Map<String,Object> maps=new HashMap<>();
maps.put("out_trade_no",productOrders.getOrdernumber());
maps.put("total_amount","0.01");
maps.put("subject","小编机器人纠错");
maps.put("store_id","XB001");
maps.put("timeout_express","1m");
//把订单信息转换为json对象的字符串
String postdata = JSONObject.fromObject(maps).toString();
request.setBizContent(postdata);
AlipayTradePrecreateResponse response = alipayClient.execute(request);
String body = response.getBody();
JSONObject jsonObject = JSONObject.fromObject(body);
String qr_code = jsonObject.getJSONObject("alipay_trade_precreate_response").getString("qr_code");
//流输出
ServletOutputStream sos = null;
try {
sos = responses.getOutputStream();
//生成二维码
QRCodeUtil.encode(qr_code, sos);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
/**
* 支付宝支付成功回调
*/
@RequestMapping(value = "aliPayCallBack.do")
@ResponseBody
public void aliPayCallBack(HttpServletResponse response, HttpServletRequest request) {
System.out.println("支付宝回调成功");
try {
//获取支付宝POST过来反馈信息
Map<String,String> params = new HashMap<String,String>();
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);
}
boolean flag = AlipaySignature.rsaCheckV1(params, AlipayConfig.zfb_public_key, AlipayConfig.input_charset,"RSA2");
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
String order_no = request.getParameter("out_trade_no"); // 获取订单号
// String trade_no = request.getParameter("trade_no"); // 支付宝交易号
// String total_amount = request.getParameter("total_amount"); // 获取总金额
// String subject = new String(request.getParameter("subject")
// .getBytes("ISO-8859-1"), "gbk");// 商品名称、订单名称
// String body = "";
// if (request.getParameter("body") != null) {
// body = new String(request.getParameter("body").getBytes(
// "ISO-8859-1"), "gbk");// 商品描述、订单备注、描述
// }
// String buyer_email = request.getParameter("buyer_email"); // 买家支付宝账号
String trade_status = request.getParameter("trade_status"); // 交易状态
// 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
if (flag==true) {// 验证成功
try{
int i = this.proOrdersService.updateByOrderId(order_no);
if (i==1)
{
System.out.println("支付宝订单支付成功!");
}
}catch (Exception e)
{
System.out.println("支付宝支付异常");
e.printStackTrace();
}
}
response.getWriter().write("success");
} catch (Exception e) {
e.printStackTrace();
}
}
}
现在,基本的代码都贴完了,生成二维码的逻辑和微信一样,拿到code_url后,用二维码生成工具类生成二维码,把他贴到网页上需要支付的页面,就成功了~