接入指南-条码支付
接入设计
在开始开发前,您需要明确接入的方式或者所需物料等。应用接入方式分为两类:门店直连方式 和 商户/系统商后台转发 方式。
开发者可以点击下载 支付宝支付物料 或者直接进行 物料系统申请。
门店直连方式
门店直连方式接入即商家收银台直接通过公网向支付宝发起收款。个人商户或者单独门店建议采用该方式接入。
流程图
商户/系统商后台转发方式
商户/系统商后台转发方式接入即商家收银台先向商家后台发起收款请求,商家后台再将请求转发给支付宝后台。建议由多家门店的商户或大型商户这类有能力开发自有后台的商户采用。
流程图
安全设计
支付宝为了保证交易安全采取了一系列安全手段以保证交易安全。详情请参见 应用安全开发指南。
采用 HTTPS 协议传输交易数据,防止数据被截获、解密。
采用 RSA/RSA2 非对称密钥,明确交易双方的身份,保证交易主体的正确性和唯一性。
条码定时刷新,防止被拍照。
防止截屏(截屏后二维码失效)。
接口调用
开发者需要确认自己的应用在审核通过后显示 已上线,同时完成当面付功能的签约后,才能顺利调用以下接口。否则会有缺少权限的报错。
调用流程
创建交易并支付(如图,1-1.1.3 步所示);
根据返回的结果,确定支付状态,从而进行相应处理(包括必要时关闭交易), 如图 opt 区块中所示,第 2 步表示支付成功, 返回支付成功页;
第 3 步表示返回的参数为待用户付款(code=10003)或者系统异常(code=20000)、网络超时等场景下处理方式;
第 4 步表示轮询结束仍未处理成功,需要撤销订单;
第 5 步表示返回支付失败(code=40004) 时, 需要重新检查参数,重新发起支付。
详情请参见 异常处理。
支付
商家系统将用户付款码与订单信息一起通过 alipay.trade.pay(统一收单交易支付接口) 请求到支付宝,并从接口同步返回中获取支付结果。根据公共返回参数中的 code,这笔交易可能有四种状态:支付成功(10000),支付失败(40004),等待用户付款(10003)和未知异常(20000)。
示例代码
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",APP_ID,APP_PRIVATE_KEY,"json",CHARSET, ALIPAY_PUBLIC_KEY,"RSA2"); //获得初始化的AlipayClient
AlipayTradePayRequest request = new AlipayTradePayRequest(); //创建API对应的request类
request.setBizContent ( "{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"scene\":\"bar_code\"," +
"\"auth_code\":\"28763443825664394\"," + //即用户在支付宝客户端内出示的付款码,使用一次即失效,需要刷新后再去付款
"\"subject\":\"Iphone6 16G\"," +
"\"store_id\":\"NJ_001\"," +
"\"timeout_express\":\"2m\"," +
"\"total_amount\":\"88.88\"" +
"}" ); //设置业务参数
AlipayTradePayResponse response = alipayClient.execute( request ); //通过alipayClient调用API,获得对应的response类
System.out.print(response.getBody());
// 根据response中的结果继续业务逻辑处理
重要入参说明
out_trade_no:商户订单号,需保证在商家系统中唯一。
scene:支付场景,条码付场景固定为
bar_code
。auth_code:用户付款码,25-30 开头的长度为 16-24 位的数字,实际字符串长度以开发者获取的付款码长度为准;付款码使用一次即失效,即使支付失败也需刷新。
subject:商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等。
store_id:商户门店编号。
total_amount:订单金额。
timeout_express:交易超时时间。
重要出参说明
trade_no:支付宝交易号。
code:网关返回码,详情请参见 公共错误码。支付接口需注意如下值:
10000:支付成功。建议记录交易结果并在客户端显示支付成功,进入后续的业务处理。
40004:支付失败。建议记录交易结果并在客户端显示错误信息(display_message)。
10003:等待用户付款。建议发起轮询流程:等待 5 秒后调用 交易查询接口 alipay.trade.query。 通过支付时传入的商户订单号(out_trade_no)查询支付结果(返回参数 TRADE_STATUS ),如果仍然返回等待用户付款(WAIT_BUYER_PAY),则再次等待 5 秒后继续查询,直到返回确切的支付结果(成功 TRADE_SUCCESS 或 已撤销关闭TRADE_CLOSED),或是超出轮询时间。在最后一次查询仍然返回等待用户付款的情况下,必须立即调用 交易撤销接口 alipay.trade.cancel 将这笔交易撤销,避免用户继续支付。
20000:未知异常。建议调用查询接口确认支付结果,详情请参见 异常处理。
查询交易
商家可调用 alipay.trade.query(统一收单线下交易查询)接口,通过商户网站唯一订单号 out_trade_no 或支付宝交易号 trade_no 查询对应订单支付情况。
示例代码
xxxxxxxxxx
AlipayClient alipayClient = new DefaultAlipayClient ("https://openapi.alipay.com/gateway.do",APP_ID,APP_PRIVATE_KEY,"json",CHARSET,ALIPAY_PUBLIC_KEY,"RSA2"); //获得初始化的AlipayClient
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); //创建API对应的request类
request . setBizContent ( "{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"trade_no\":\"2014112611001004680073956707\"}" ); //设置业务参数
AlipayTradeQueryResponse response = alipayClient.execute(request); //通过alipayClient调用API,获得对应的response类
System.out.print ( response.getBody());
//根据response中的结果继续业务逻辑处理
重要入参说明
out_trade_no:支付时传入的商户订单号,与 trade_no 必填一个。
trade_no:支付时返回的支付宝交易号,与 out_trade_no 必填一个。
撤销交易
支付交易返回失败或支付系统超时,商家可调用 alipay.trade.cancel(统一收单交易撤销接口)通过商户网站唯一订单号 out_trade_no 或支付宝交易号 trade_no 撤销交易。
使用说明
如果此订单用户支付失败,支付宝将关闭此订单。
如果此订单用户支付成功,支付宝将退还订单资金给用户。
仅发生支付系统超时或者支付结果未知时可调用本接口撤销交易,其他正常支付的单如需实现相同功能请调用 alipay.trade.refund(统一收单交易退款接口)。
示例代码
xxxxxxxxxx
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();//创建API对应的request类
request.setBizContent("{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"trade_no\":\"2014112611001004680073956707\"}"); //设置业务参数
AlipayTradeCancelResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
System.out.print(response.getBody());
//根据response中的结果继续业务逻辑处理
重要入参说明
out_trade_no:支付时传入的商户订单号,与 trade_no 必填一个。
trade_no:支付时返回的支付宝交易号,与 out_trade_no 必填一个。
重要出参说明
retry_flag:是否需要重试,Y/N。
action:本次撤销触发的交易动作。
close:关闭交易,无退款 。
refund:产生了退款。
退款
当交易发生之后一段时间内,由于业务原因(如金额错误,用户退款或者对账不平等等)需要退款时,商家可以调用 alipay.trade.refund(统一收单交易退款接口)通过商户网站唯一订单号 out_trade_no 或支付宝交易号 trade_no,将对应订单支付款退还给买家,支付宝将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。支持全额或部分退款。
调用流程
使用说明
退款的途径按照支付途径原路返回,交易超过约定时间(签约时设置的可退款时间)的订单无法进行退款。
支付渠道为花呗、余额等退款即时到账。银行卡的退款时间以银行退款时间为准,一般情况下 2 小时内可到账。
商家也可以在商家中心(b.alipay.com)中退款。
退款是否成功可以根据同步响应的
fund_change
参数来判断,返回值为 Y 则表示退款成功。退款接口会根据外部请求号 out_request_no 幂等返回,因此同一笔交易需要多次部分退款时,必须使用不同的 out_request_no。
示例代码
xxxxxxxxxx
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2"); //获得初始化的AlipayClient
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();//创建API对应的request类
request.setBizContent("{" +
"\"out_trade_no\":\"20150320010101001\"," +
"\"trade_no\":\"2014112611001004680073956707\"," +
"\"out_request_no\":\"1000001\"," +
"\"refund_amount\":\"1.00\"}"); //设置业务参数
AlipayTradeRefundResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
System.out.print(response.getBody());
//根据response中的结果继续业务逻辑处理
重要入参说明
out_trade_no:支付时传入的商户订单号,与 trade_no 必填一个。
trade_no:支付时返回的支付宝交易号,与 out_trade_no 必填一个。
out_request_no:本次退款请求流水号,部分退款时必传。
refund_amount:本次退款金额。
重要出参说明
refund_fee:该笔交易已退款的总金额。
退款到银行卡通知
退款存在退到银行卡场景下时,支付宝会调用 alipay.trade.refund.depositback.completed(收单退款冲退完成通知)接口根据银行回执消息发送退款完成信息至应用网关地址。
使用说明
开发者需登录 开放平台 进入对应应用详情页,在 消息服务 > FROM 蚂蚁 中订阅 alipay.trade.refund.depositback.completed(收单退款冲退完成通知)。
更多 From 蚂蚁消息详情参见 From 蚂蚁消息服务使用。
消息示例
xxxxxxxxxx
ISV_GATEWAY_URL?charset=GBK&biz_content=
{
"trade_no":"2014112611001004680073956707","out_trade_no":"20150320010101001","out_request_no":"HZ01RF001","dback_status":"S","dback_amount":"1.01","bank_ack_time":"2020-06-02 14:03:48","est_bank_receipt_time":"2020-06-02 14:03:48"
}
&msg_method=alipay.trade.refund.depositback.completed&utc_timestamp=1516797622752&version=1.1&sign_type=RSA2¬ify_id=d275fec564e62af6bedbcee73f3f05fi5x&app_id=2013121700999429&sign=I+Y/lvqYUEEc10EPdpntRhFIQ==
重要参数说明
trade_no:支付宝交易订单号。
dback_status:银行卡冲退状态。
S - 成功。
F - 失败,银行卡冲退失败,资金自动转入用户支付宝余额。
dback_amount:银行卡冲退金额。
bank_ack_time:银行响应时间,格式为 yyyy-MM-dd HH:mm:ss。
est_bank_receipt_time:预估银行入账时间,格式为 yyyy-MM-dd HH:mm:ss。
对账
商家/服务商可通过接口下载指定日期(当天除外)的业务明细账单文件,并结合自身业务系统实现自动对账。
接口流程
商户系统调用 alipay.data.dataservice.bill.downloadurl.query(查询对账单下载地址),传入指定日期,获得该日期账单文件的下载地址;
商户系统通过 HTTP 方式后台访问账单下载链接,将账单 csv 文件下载到本地后自行处理。注意该下载链接仅 30 秒,在得到链接后系统需要立刻下载账单文件。
示例代码
xxxxxxxxxx
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", APP_ID, APP_PRIVATE_KEY, "json", CHARSET, ALIPAY_PUBLIC_KEY, "RSA2");//获得初始化的AlipayClient
AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();//创建API对应的request类
request.setBizContent("{" +
"\"bill_type\":\"trade\"," +
"\"bill_date\":\"2016-04-05\"}"); //设置业务参数
AlipayDataDataserviceBillDownloadurlQueryResponse response = alipayClient.execute(request);//通过alipayClient调用API,获得对应的response类
System.out.print(response.getBody());
//根据response中的结果继续业务逻辑处理
重要入参说明
bill_type:固定传入 trade。
bill_date:需要下载的账单日期,最晚是当期日期的前一天。
重要出参说明
bill_download_url:账单文件下载地址,有效时长:30 秒。
下载账单文件示例代码
xxxxxxxxxx
//将接口返回的对账单下载地址传入urlStr
String urlStr = "http://dwbillcenter.alipay.com/downloadBillFile.resource?bizType=X&userId=X&fileType=X&bizDates=X&downloadFileName=X&fileId=X";
//指定希望保存的文件路径
String filePath = "/Users/fund_bill_20160405.csv.zip";
URL url = null;
HttpURLConnection httpUrlConnection = null;
InputStream fis = null;
FileOutputStream fos = null;
try {
url = new URL(urlStr);
httpUrlConnection = (HttpURLConnection) url.openConnection();
httpUrlConnection.setConnectTimeout(5 * 1000);
httpUrlConnection.setDoInput(true);
httpUrlConnection.setDoOutput(true);
httpUrlConnection.setUseCaches(false);
httpUrlConnection.setRequestMethod("GET");
httpUrlConnection.setRequestProperty("CHARSET", "UTF-8");
httpUrlConnection.connect();
fis = httpUrlConnection.getInputStream();
byte[] temp = new byte[1024];
int b;
fos = new FileOutputStream(new File(filePath));
while ((b = fis.read(temp)) != -1) {