名词 | 解释和使用场景 |
预授权冻结 | 用户授权商家冻结其支付宝内指定支付渠道的一定数量资金 |
预授权解冻 | 商家将用户冻结金额解冻为可用渠道资金 |
解冻转支付 | 商户创建交易订单,将用户预授权冻结的金额用于交易 |
被扫预授权 | 商户使用扫码枪扫描用户付款码,冻结用户资金 |
主扫预授权 | 用户使用支付宝APP扫商户二维码主动授权冻结资金 |
预授权是指顾客消费与结算不在同一时间内完成时(如酒店、租赁行业),商户预先向用户发卡机构索要授权(如冻结用户一定资金),待确定与用户结算时,再从预先授权且冻结的资金中完成授权支付。间连预授权产品为银行机构提供了相应预授权能力。如酒店行业中,用户入住时出示支付宝付款码或者使用支付宝APP扫描商户提供的预授权二维码进行预授权,授权成功后会冻结用户一部分资金;当用户准备离店时,商家服务员根据用户实际消费情况,直接从用户授权冻结的资金中完成解冻转支付交易,此时用户无需再次出示支付宝APP,用户扣款资金将从预授权资金中划拨给商户,如有剩余冻结资金,商户通过预授权解冻能力将剩余冻结资金归还给用户。
预授权包含预授权冻结、解冻转支付两个阶段,只有转支付后,用户的资金才会发生扣款并进入商户(银行机构)。第一阶段为冻结用户一部分资金,第二阶段为将冻结资金用于交易结算,且商户需将剩余冻结资金解冻并返还用户。需要注意的是,目前间连预授权不支持追加冻结,即不支持先冻结500元再追加冻结200元(如果有这类场景,可以先解冻500元,再使用新订单冻结700元),且冻结金额需要大于等于结算支付金额。
主扫预授权场景中,预授权解冻转支付流程与被扫模式一致,区别点就在于第一阶段的预授权冻结流程,由用户主动扫码确认预授权金额并输入密码完成冻结交易,商家接收预授权完成的结果。具体流程如下:
签约内容:由支付宝业务经理直接为银行机构签约“当面资金授权机构代理收单V1”,即间连预授权产品。针对同一个银行支付宝会员号pid,可同时签约间连当面付、间连预授权等多个间连产品,各产品之间互不影响,这种情况可满足银行使用同一个账号完成当面付和预授权的收单,即使用相同appid、清算账号相同、账单也为同一账单。同时,银行也可以申请新的pid,并使用独立的pid来签约间连预授权,从而与间连当面付产品账号完全隔离开。因此,银行机构需要维护好账号和产品的对应关系。
测试账号:在银行机构完成正式账号签约前,可使用支付宝沙箱环境直接进行测试(了解沙箱环境)。
对账SFTP申请:由于支付宝账单是以pid维度出具,所以如果银行已签约间连当面付,且使用相同pid签约间连预授权,则SFTP无需变更,对账单中会包含间连预授权交易,通过对账单中的“交易类型”字段可区分间连当面付与间连预授权的交易。
其他准备工作:如银行机构已对接间连当面付,则准备工作基本完成,否则请参考《间连业务接入指南》准备工作
在进行间连预授权之前,同样需要进行子商户入驻(子商户同间连当面付对应的子商户,无需重复入驻)。子商户入驻成功后,才可以以子商户维度进行间连预授权冻结、解冻转支付二阶段交易。
受理机构首先调用支付宝分级商户入驻接口(ant.merchant.expand.indirect.create),完成商户入驻后,支付宝会返回唯一标识该商户的编号(sub_merchant_id简称smid),受理机构可通过(ant.merchant.expand.indirect.query)接口查询入驻信息,通过(ant.merchant.expand.indirect.modify)接口修改信息,目前无删除接口和查询列表接口,也无管理系统可直接进行入驻等操作。
预授权冻结阶段分为主扫和被扫两种模式,目的均为冻结用户资金,完成预授权冻结交易,记录授权码和用户uid等信息,为第二阶段解冻转支付准备。
受理机构首先调用资金授权冻结接口(alipay.fund.auth.order.freeze),通过扩展字段extra_param中的子字段secondaryMerchantId传子商户信息,进行用户资金冻结,支付宝同步返回冻结成功或失败。如资金冻结成功(状态字段status为SUCCESS),支付宝会返回支付宝资金授权订单号(auth_no)、授权用户id(payer_logon_id)等信息,受理机构必须记录好该信息以备第二阶段解冻转支付使用。对于冻结金额大于1000元或风控系统需要验证用户身份的情况下,支付宝APP会唤起收银台让用户输入密码确认授权冻结,此时支付宝返回码code为10003,表示用户授权中,受理机构应使用资金授权操作查询接口(alipay.fund.auth.operation.detail.query)每隔5秒钟查询一次授权冻结的状态,如依然是用户授权中,则再等待5秒钟继续使用查询接口查询,直至1分钟后超时(推荐每隔5秒查询一次,共进行12次查询),如超时前最后一次查询依然返回用户授权中,则马上调用资金授权撤销接口(alipay.fund.auth.operation.cancel)将该笔预授权冻结交易给撤销。整体接入流程类似当面付条码支付流程。如接口返回系统异常,也类似当面付处理流程,首先通过查询接口查询授权冻结的状态,并根据查询结果进行后续操作。
受理机构首先调用资金授权发码接口(alipay.fund.auth.order.voucher.create),通过扩展字段extra_param中的子字段secondaryMerchantId传子商户信息,生成预授权订单二维码,然后由用户使用支付宝APP扫描二维码输入密码确认授权冻结。用户授权成功后,支付宝会向受理机构通知授权成功,通知以表单形式,通知地址由发码接口的notify_url字段确定。受理机构接受通知,并根据订单号和预授权状态进行相应处理,必须记录支付宝资金授权订单号(auth_no)和授权用户id(payer_logon_id)等信息,为第二阶段转支付做准备。
通知以表单方式,通过POST方法,提交到发码接口的notify_url字段指定的通知地址,通知请求样例如下,通知特性参考当面付“异步通知特性”。
operation_id=20170922345015779802&auth_no=2017092210002001980263817241&sign_type=RSA&payee_user_id=2088911212416201&out_order_no=orderFreeze0000014&auth_app_id=2015051100069170¬ify_type=fund_auth_freeze&payer_user_id=2088002470788888&version=1.0&amount=0.01&rest_amount=0.01¬ify_time=2017-09-22 21:16:21&status=SUCCESS&charset=utf-8&operation_type=FREEZE&total_unfreeze_amount=0.00&sign=***&payee_logon_id=zfb***@test.com&gmt_create=2017-09-22 21:15:39&total_freeze_amount=0.01&payer_logon_id=cli***@test.com&out_request_no=requestNo0000014&app_id=2015051100069170&total_pay_amount=0.00¬ify_id=632f1457e6b251d74041567d9fa43acnka&gmt_trans=2017-09-22 21:15:39
预授权解冻转支付阶段使用当面付条码支付接口(alipay.trade.pay),但是需要注意产品码字段product_code需要传预授权产品码PRE_AUTH,表示进行间连预授权交易;通过支付宝预授权号(auth_no)选择冻结转支付的交易;同时必须上传付款方(buyer_id,填写预授权冻结阶段支付宝返回的payer_logon_id)和收款方(seller_id,目前填写受理机构的pid)字段;由于间连预授权冻结阶段已传了子商户信息,支付宝已关联至支付授权订单号,所以解冻转支付时无需再上送子商户信息(即此时无需上传sub_merchant)。但是由于监管要求,间连预授权也需要根据实际情况上传门店号(store_id)或者终端号(terminal_id),两者必须根据实际情况至少上传一个。
由于预授权冻结金额大于等于结算金额,当冻结金额大于结算金额时,用户剩余冻结金额需要根据业务情况及时通过解冻接口退还给用户(以防止用户拿不到剩余冻结资金而投诉),受理机构可进行多次转支付操作,也可进行多次解冻操作。同一订单的多次转支付和解冻操作通过请求流水号out_request_no来标识。
预授权阶段包含冻结、发码、查询、撤销、解冻共五个接口,如顾客冻结金额有误或希望追加冻结金额,可使用资金授权解冻接口(alipay.fund.auth.order.unfreeze)将原冻结资金解冻(调用资金解冻时,使用auth_no确认需要解冻的预授权订单,支持多次解冻,通过out_request_no区分同一笔冻结交易的多次解冻。如全单解冻,则解冻金额填写冻结金额,out_request_no填写一个全新流水号,与冻结接口的流水号不能相同),然后受理机构可使用新订单号,输入新的金额,重新发起一笔新的授权冻结请求。对于解冻转支付的交易,如业务需要退款,则可以使用退款接口(alipay.trade.refund)进行间连预授权交易的退款,同时也可以使用交易查询接口(alipay.trade.query)查询该笔交易的状态。
1、银行机构除了及时在解冻转支付后将用户剩余冻结资金及时解冻,也一定要提供独立解冻功能用于完成用户冻结资金的解冻,以免发生用户无法解冻资金,造起用户不必要的投诉。
2、由于网银渠道并未向支付宝提供预授权接口,所以支付宝间连预授权产品中,如果使用银行卡渠道进行预授权操作,则会通过网银渠道支付加退款的方式来实现。而对于支付宝体系内的支付渠道,如花呗、余额、余额宝等,则支持预授权冻结资金的能力,从用户体验考虑,建议商户首推花呗渠道进行预授权冻结。
3、预授权冻结、解冻、转支付、退款均会在支付宝app账单中产生一条记录,但由于预授权冻结、解冻并未发生用户账务变动,所以支付宝app账单中金额字段并不会出现正负号,同时,由于资金并未发生变动,所以对账单中也不会出现未转支付的预授权冻结交易流水。
/** * 测试间连预授权冻结 */ @Test public void testOrderFreeze() throws Exception { long id = 9; AlipayFundAuthOrderFreezeModel model = new AlipayFundAuthOrderFreezeModel(); model.setAuthCode("2898999993611797832"); // 填写顾客支付宝APP的付款码 model.setAuthCodeType("bar_code"); // 填写固定值bar_code model.setOutOrderNo("orderFreeze00000" + id); // 预授权冻结交易商户订单号,商户系统唯一标识一笔预授权 model.setOutRequestNo("requestNo00000" + id); model.setOrderTitle("测试酒店预授权冻结" + id); // 预授权标题,用于展示在支付宝账单 model.setAmount("0.02"); // 预授权金额,注意需要大于等于结算支付金额 model.setPayeeUserId("2088911212416201"); // 银行的pid,用于开启花呗渠道预授权 model.setPayTimeout("5m"); // 预授权冻结超时时间,超时则自动关闭冻结交易 model.setExtraParam("{'secondaryMerchantId':'2088721598386023'}"); // 填写间连子商户信息 model.setProductCode("PRE_AUTH"); // 填写固定值PRE_AUTH AlipayFundAuthOrderFreezeRequest request = new AlipayFundAuthOrderFreezeRequest(); request.setBizModel(model); AlipayFundAuthOrderFreezeResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权订单查询 */ @Test public void testOrderFreezeQuery() throws Exception { long id = 9; AlipayFundAuthOperationDetailQueryModel model = new AlipayFundAuthOperationDetailQueryModel(); model.setOutOrderNo("orderFreeze00000" + id); // 间连预授权冻结交易的商户订单号 model.setOutRequestNo("requestNo00000" + id); // 间连预授权冻结交易的请求流水号 AlipayFundAuthOperationDetailQueryRequest request = new AlipayFundAuthOperationDetailQueryRequest(); request.setBizModel(model); AlipayFundAuthOperationDetailQueryResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权撤销 */ @Test public void testOrderFreezeCancel() throws Exception { long id = 9; AlipayFundAuthOperationCancelModel model = new AlipayFundAuthOperationCancelModel(); model.setOutOrderNo("orderFreeze00000" + id); // 间连预授权冻结交易的商户订单号 model.setOutRequestNo("requestNo00000" + id); // 间连预授权冻结交易的请求流水号 model.setRemark("预授权撤销" + id); AlipayFundAuthOperationCancelRequest request = new AlipayFundAuthOperationCancelRequest(); request.setBizModel(model); AlipayFundAuthOperationCancelResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权解冻 */ @Test public void testOrderUnfreeze() throws Exception { long id = 9; AlipayFundAuthOrderUnfreezeModel model = new AlipayFundAuthOrderUnfreezeModel(); model.setAuthNo("2017092210002001980263116358"); // 需要解冻的支付宝预授权订单号,由预授权冻结接口返回 model.setOutRequestNo("UnfreezeRequestNo00000x" + id); // 需要填写解冻流水号,与冻结流水号不同 model.setAmount("0.01"); model.setRemark("预授权解冻" + id); AlipayFundAuthOrderUnfreezeRequest request = new AlipayFundAuthOrderUnfreezeRequest(); request.setBizModel(model); AlipayFundAuthOrderUnfreezeResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试创建间连预授权主扫二维码 */ @Test public void testOrderVoucherCreate() throws Exception { long id = 9; AlipayFundAuthOrderVoucherCreateModel model = new AlipayFundAuthOrderVoucherCreateModel(); model.setOutOrderNo("orderFreeze00000" + id); // 预授权冻结交易商户订单号,商户系统唯一标识一笔预授权 model.setOutRequestNo("requestNo00000" + id); model.setOrderTitle("测试主扫预授权冻结" + id); // 预授权标题,用于展示在支付宝账单 model.setAmount("0.01"); // 预授权金额,注意需要大于等于结算支付金额 model.setPayeeUserId("2088911212416201"); // 银行的pid,用于开启花呗渠道预授权 model.setPayTimeout("10m"); // 预授权冻结超时时间,超时则自动关闭冻结交易 model.setExtraParam("{'secondaryMerchantId':'2088721598386023'}"); // 填写间连子商户信息 model.setProductCode("PRE_AUTH"); // 填写固定值PRE_AUTH AlipayFundAuthOrderVoucherCreateRequest request = new AlipayFundAuthOrderVoucherCreateRequest(); request.setNotifyUrl("http://www.web.com/notify/preauth"); // 接收支付宝推送的授权成功通知 request.setBizModel(model); AlipayFundAuthOrderVoucherCreateResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权解冻转支,使用当面付接口,但使用PRE_AUTH即预授权的产品码 */ @Test public void testTradePay() throws Exception { long id = System.currentTimeMillis(); AlipayTradePayModel model = new AlipayTradePayModel(); model.setOutTradeNo("pay00000" + id); // 预授权解冻转支付流水号,为新的商户交易流水号 model.setScene("bar_code"); // 填写固定值bar_code model.setProductCode("PRE_AUTH"); // 填写固定值PRE_AUTH model.setSubject("一姜酒店预授权支付" + id); // 解冻转支付标题,用于展示在支付宝账单 model.setTotalAmount("0.01"); // 结算支付金额 model.setSellerId("2088911212416201"); // 填写银行的pid model.setBuyerId("2088002470735983"); // 填写预授权用户uid,通过预授权冻结接口payer_logon_id字段获取 model.setBody("预授权解冻转支付" + id); // 可填写备注信息 model.setStoreId("test_store_id"); // 填写实际交易发生的门店编号 model.setTerminalId("test_terminal_id"); // 填写实际交易发生的终端编号 model.setTimeoutExpress("1m"); // 填写解冻转支付交易的超时时间 model.setAuthNo("2017092110002001980262377897"); // 填写预授权冻结交易号 AlipayTradePayRequest request = new AlipayTradePayRequest(); request.setBizModel(model); AlipayTradePayResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权交易查询 */ @Test public void testTradeQuery() throws Exception { AlipayTradeQueryModel model = new AlipayTradeQueryModel(); model.setOutTradeNo("pay000001505986243499"); AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); request.setBizModel(model); AlipayTradeQueryResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试间连预授权交易退款 */ @Test public void testTradeRefund() throws Exception { AlipayTradeRefundModel model = new AlipayTradeRefundModel(); model.setOutTradeNo("pay000001505986243499"); model.setRefundAmount("0.01"); model.setRefundReason("预授权退款"); AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); request.setBizModel(model); AlipayTradeRefundResponse response = client.execute(request); logger.info("response: {}", response.getBody()); Assert.assertTrue(response.isSuccess()); }
/** * 测试下载对账单 */ @Test public void testReportDownload() throws Exception { AlipayDataDataserviceBillDownloadurlQueryModel model = new AlipayDataDataserviceBillDownloadurlQueryModel(); model.setBillType("trade"); // 下载业务账单 model.setBillDate("2017-09-21"); // 注意只能查询一天前的对账单 AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest(); request.setBizModel(model); AlipayDataDataserviceBillDownloadurlQueryResponse response = client.execute(request); logger.info("response: {}", response.getBody()); logger.info("download url: {}", response.getBillDownloadUrl()); Assert.assertTrue(response.isSuccess()); }
注意:查看API接口文档之前,请一定先了解预授权整体接入流程,不要直接就看API文档,对于接口参数的使用,可以参考调用示例的传参,磨刀不误砍材工。
接口名称(中/英文名) | 描述 | 使用场景 |
资金授权冻结接口 alipay.fund.auth.order.freeze |
被扫授权场景下资金冻结 | 收银员使用扫码设备读取用户支付宝钱包“付款码”后,将二维码或条码信息和订单信息通过本接口上送至支付宝发起资金授权冻结。 |
资金授权发码接口 alipay.fund.auth.order.voucher.create |
主扫场景下资金冻结 | 收银员通过收银台或商户后台调用支付宝接口,生成授权冻结二维码后,展示给用户,由用户扫描二维码完成资金冻结。 |
资金授权撤销接口 alipay.fund.auth.operation.cancel |
授权资金冻结的反交易接口 | 此接口为处理异常流程时提供的反交易接口,如需要正常解冻业务,请使用资金授权解冻接口。当预授权超时或者返回结果未知时,可根据资金授权操作查询接口查询,如重复查询依然显示未知结果时,才可调用该撤销接口。 |
资金授权解冻接口 alipay.fund.auth.order.unfreeze |
提供资金解冻能力,支持部分和全额解冻 | 当资金授权冻结完成后的一段时间内,由于业务原因需解冻资金,商家可通过资金授权解冻接口将授权资金解冻,支付宝将在收到解冻请求并验证成功后,按解冻规则将冻结资金解冻,原路退回支付渠道 |
资金授权操作查询接口 alipay.fund.auth.operation.detail.query |
通过该接口查询授权操作明细信息,如该订单授权总额,已转支付金额,剩余冻结金额等 | 任何时候均可使用授权查询接口查询当前授权冻结订单状态 |
交易创建并支付接口 alipay.trade.pay |
创建支付宝交易订单,并使用预授权资金完成转支付交易 | 当商家需要对用户授权资金进行解冻支付时,商家可通过该接口将用户授权资金支付给卖家,支付宝将在收到请求后创建交易并将授权资金支付给受理商户 |
交易同步退款接口 alipay.trade.refund |
用于对已完成转支付的交易订单进行退款操作,支持部分和全额退款 | 当预授权转支付交易发生后的一段时间内,由于业务原因需要退款,受理商户可通过退款接口将支付款退还给买家,支付宝将在收到退款请求并验证成功后,按退款规则将支付款按原路退还至支付渠道 |
交易订单查询接口 alipay.trade.query |
查询转支付交易订单的状态,不是查询预授权冻结的状态 | 任何时候均可使用交易订单查询接口查询当前转支付交易的订单状态 |
结果码 | 说明 | 处理方式 |
10000 | 授权成功 | 记录授权结果并在客户端显示授权成功,进入后续的业务处理。 |
40004 | 授权失败 | 记录授权结果并在终端显示错误信息 |
10003 | 等待用户授权 | 发起轮询流程,即等待5秒后调用资金授权操作查询接口,通过授权时传入的商户授权订单号(out_order_no)和资金操作流水号(out_request_no)查询授权结果(返回参数status),如果仍然返回等待用户授权(INIT),则再次等待5秒后继续查询,直到返回确切的授权结果(成功SUCCESS 或 已关闭CLOSED),或是超出轮询时间(建议轮询时间为60s)。在最后一次查询仍然返回等待用户授权的情况下,必须立即调用资金授权撤销接口将这笔授权操作撤销,避免用户继续操作。 |
20000 | 未知异常 | 调用查询接口确认授权结果,并根据查询结果进行处理。 |
基于法律法规、监管政策等原因,我公司有权采取变更或暂停接口使用、终止合作等措施。