完成 [接入准备](https://ideservice.alipay.com/cms/site/0j02ep) 后,商家/服务商可根据本文指引快速接入 **商家扣款-支付并签约**。
**说明**: - **商家扣款** 产品仅支持自研商家/服务商通过 [自研应用](https://ideservice.alipay.com/cms/site/02bijh#%E8%87%AA%E7%A0%94%E6%9C%8D%E5%8A%A1%E5%9E%8B%E5%BA%94%E7%94%A8) 或 [第三方应用](https://ideservice.alipay.com/cms/site/02bijh#%E4%B8%89%E6%96%B9%E6%9C%8D%E5%8A%A1%E5%9E%8B%E5%BA%94%E7%94%A8) 代调用(传入 `app_auth_token`)方式,调用 API 接入。 - **商家扣款** 产品暂不支持沙箱调试。 - **支付并签约场景**指用户在小程序/商家App支付的同时,完成扣款协议的签约授权。商家在支付接口传入签约相关参数 sdkExecute 方法拿到 response 返回值后,其传入给 App支付/小程序,完成同步支付与异步签约,通过异步通知或者查询接口确认签约结果。 - **商家扣款 **产品,接口中 product_code 字段固定传参为 GENERAL_WITHHOLDING。 # 支付并签约流程图 ## 从商家APP发起 ``` sequenceDiagram title sd商家代扣 - SDK支付并签约及消息部分 participant 用户 participant 商家客户端 participant 商家服务端 participant 支付宝服务端 participant 支付宝APP 用户->>商家客户端: 1.浏览商品点击购买 activate 商家客户端 商家客户端->>商家服务端: 2.请求商家服务端支付签约方法 activate 商家服务端 商家服务端->>商家服务端: 3.封装支付宝签约规则参数 商家服务端->>商家服务端: 4.使用sdkExecute方法调app支付接口 商家服务端-->>商家客户端: 5.返回支付签约字符串 deactivate 商家服务端 商家客户端->>支付宝APP: 6.拿支付签约字符串唤起支付宝钱包 activate 支付宝APP 支付宝APP->>支付宝服务端: 8.请求支付并签约 activate 支付宝服务端 支付宝服务端-->>支付宝APP: 7.展示支付签约页面 deactivate 支付宝服务端 支付宝APP-->>用户: 9.展示支付签约页面 deactivate 支付宝APP deactivate 商家客户端 用户->>支付宝APP: 10.点击付款并开通 activate 支付宝APP activate 支付宝APP 支付宝APP-->>商家客户端: 11.点击完成并附带签约结果 deactivate 支付宝APP %% 支付结果通知部分 alt 收到支付异步通知 支付宝服务端->>商家服务端: 12.推送支付结果 activate 商家服务端 商家服务端-->>支付宝服务端: 13.返回字符串success deactivate 商家服务端 else 未收到异步通知 商家服务端->>支付宝服务端: 14.收单交易查询 activate 支付宝服务端 支付宝服务端-->>商家服务端: 15.返回查询结果 deactivate 支付宝服务端 end %% 签约结果通知部分 alt 收到签约异步通知 支付宝服务端->>商家服务端: 16.推送签约结果 activate 商家服务端 商家服务端-->>支付宝服务端: 17.返回字符串success deactivate 商家服务端 else 未收到异步通知 商家服务端->>支付宝服务端: 18.请求代扣协议查询接口 activate 支付宝服务端 支付宝服务端-->>商家服务端: 19.返回签约结果 deactivate 支付宝服务端 end 商家服务端->>商家服务端: 20.存储支付宝签约信息 ``` ## 从支付宝小程序发起 ``` sequenceDiagram actor 用户 participant 小程序应用 participant 支付宝客户端 participant 商户服务端 participant 支付宝服务端 用户->>小程序应用: 1: 进入小程序 小程序应用->>支付宝服务端: 1.1: 调用my.getAuthCode获取授权码 支付宝服务端->>支付宝服务端: 1.1.1: 生成授权码 支付宝服务端-->>小程序应用: 1.1.2: 返回授权码authCode 小程序应用->>商户服务端: 1.2: 通过authCode获取用户信息 商户服务端->>支付宝服务端: 1.2.1: 调用alipay.system.oauth.token接口 支付宝服务端->>支付宝服务端: 1.2.1.1: 生成访问令牌 支付宝服务端-->>商户服务端: 1.2.1.2: 返回访问令牌和userId 商户服务端-->>小程序应用: 1.3: 返回用户信息 用户->>小程序应用: 2: 在小程序中下单 小程序应用->>商户服务端: 2.1: 调用my.request请求服务端 商户服务端->>支付宝服务端: 2.1.1: 调用alipay.trade.create创建交易 支付宝服务端->>支付宝服务端: 2.1.1.1: 创建交易 支付宝服务端-->>商户服务端: 2.1.1.2: 返回支付宝交易号tradeNo 商户服务端-->>小程序应用: 2.2: 返回交易号tradeNo 小程序应用->>支付宝客户端: 2.3: 调用my.tradePay唤起收银台 支付宝客户端->>支付宝服务端: 2.3.1: 请求支付 支付宝服务端->>支付宝服务端: 2.3.1.1: 支付流程 支付宝服务端-->>支付宝客户端: 2.3.1.2: 返回支付结果 支付宝客户端-->>小程序应用: 2.4: 支付接口回调 小程序应用-->>用户: 2.5: 展示支付结果 支付宝服务端->>商户服务端: 2.3.1.3: 异步发送支付通知 ``` ## 从商家PC端网页发起 ``` sequenceDiagram participant 用户 participant 商家客户端 participant 商家服务端 participant 支付宝服务端 participant 支付宝APP title: sd商户代扣-PC转二维码支付并签约及消息部分 用户->>商家客户端: 1: 浏览商品点击购买 activate 商家客户端 商家客户端->>商家服务端: 2: 请求商家服务端支付签约方法 activate 商家服务端 商家服务端->>商家服务端: 3: 封装支付签约规则参数 商家服务端->>商家服务端: 4: 使用sdkExecute方法调app支付接口生成签约字符串 商家服务端->>商家服务端: 5: 拼接scheme地址并转换成二维码 Note right of 商家服务端: 生成二维码字符串=scheme固定地址+encode签约字符串 商家服务端-->>商家客户端: 6: 返回PC二维码字符串 deactivate 商家服务端 商家客户端->>商家客户端: 7: 展示PC二维码 deactivate 商家客户端 用户->>商家客户端: 8: 用支付宝APP扫二维码 activate 商家客户端 商家客户端->>支付宝APP: 9: PC二维码字符串唤起支付宝app activate 支付宝APP deactivate 商家客户端 支付宝APP->>支付宝服务端: 10: 请求支付并签约 activate 支付宝服务端 支付宝服务端-->>支付宝APP: 11: 展示支付签约页面 deactivate 支付宝服务端 支付宝APP-->>用户: 12: 展示支付签约页面 deactivate 支付宝APP 用户->>支付宝APP: 13: 点击支付并开通 activate 支付宝APP 支付宝APP-->>商家客户端: 14: 点击完成并附带签约结果 deactivate 支付宝APP alt 收到支付异步通知 支付宝服务端->>商家服务端: 15: 推送支付结果(异步通知) activate 商家服务端 商家服务端-->>支付宝服务端: 16: 返回字符串success deactivate 商家服务端 else 未收到异步通知 商家服务端->>支付宝服务端: 17: 收单交易查询 activate 支付宝服务端 支付宝服务端-->>商家服务端: 18: 返回查询结果 deactivate 支付宝服务端 end alt 收到签约异步通知 支付宝服务端->>商家服务端: 19: 推送签约结果 activate 商家服务端 商家服务端-->>支付宝服务端: 20: 返回字符串success deactivate 商家服务端 else 未收到异步通知 商家服务端->>支付宝服务端: 21: 请求代扣协议查询接口 activate 支付宝服务端 支付宝服务端-->>商家服务端: 22: 返回签约结果 deactivate 支付宝服务端 end 商家服务端->>商家服务端: 23: 存储支付签约信息 ``` # 创建商家扣款支付并签约 ## 创建APP支付并签约 商家调用 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)传入用户支付信息及商家扣款信息,创建商家扣款支付订单。 ### 前期准备 接入 APP支付产品,详情查看 [准备文档](https://ideservice.alipay.com/cms/site/0izcsa)。 ### 创建交易订单 商家调用 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)传入用户支付信息及商家扣款信息,创建商家扣款支付订单。 ### 示例代码 ```java package com.java.sdk.demo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.AlipayConfig; import com.alipay.api.request.AlipayTradeAppPayRequest; import com.alipay.api.domain.SignParams; import com.alipay.api.domain.PeriodRuleParams; import com.alipay.api.domain.AccessParams; import com.alipay.api.domain.AlipayTradeAppPayModel; import com.alipay.api.response.AlipayTradeAppPayResponse; import com.alipay.api.FileItem; import java.util.Base64; import java.util.ArrayList; import java.util.List; public class AlipayTradeAppPay { public static void main(String[] args) throws AlipayApiException { // 初始化SDK AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig()); // 构造请求参数以调用接口 AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); // 设置商户订单号 model.setOutTradeNo("70501111111S001111119"); // 设置订单总金额 model.setTotalAmount("9.00"); // 设置订单标题 model.setSubject("订单商品名称"); // 设置产品码 model.setProductCode("QUICK_MSECURITY_PAY"); // 设置订单绝对超时时间 model.setTimeExpire("2016-12-31 10:05:00"); // 设置签约参数 SignParams agreementSignParams = new SignParams(); AccessParams accessParams = new AccessParams(); accessParams.setChannel("ALIPAYAPP"); agreementSignParams.setAccessParams(accessParams); PeriodRuleParams periodRuleParams = new PeriodRuleParams(); periodRuleParams.setPeriod(1L); periodRuleParams.setSingleAmount("10.99"); periodRuleParams.setPeriodType("MONTH"); agreementSignParams.setPeriodRuleParams(periodRuleParams); agreementSignParams.setSignNotifyUrl("http://www.merchant.com/receiveSignNotify"); agreementSignParams.setExternalLogonId("13888888888"); agreementSignParams.setPersonalProductCode("CYCLE_PAY_AUTH_P"); agreementSignParams.setExternalAgreementNo("test20190701"); agreementSignParams.setProductCode("GENERAL_WITHHOLDING"); agreementSignParams.setSignScene("INDUSTRY|XXXXXXX"); model.setAgreementSignParams(agreementSignParams); request.setBizModel(model); // 第三方代调用模式下请设置app_auth_token // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->"); AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); String orderStr = response.getBody(); System.out.println(orderStr); if (response.isSuccess()) { System.out.println("调用成功"); } else { System.out.println("调用失败"); // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接 // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response); // System.out.println(diagnosisUrl); } } private static AlipayConfig getAlipayConfig() { String privateKey = "<-- 请填写您的应用私钥,例如:MIIEvQIBADANB ... ... -->"; String alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->"; AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do"); alipayConfig.setAppId("<-- 请填写您的AppId,例如:2019091767145019 -->"); alipayConfig.setPrivateKey(privateKey); alipayConfig.setFormat("json"); alipayConfig.setAlipayPublicKey(alipayPublicKey); alipayConfig.setCharset("UTF-8"); alipayConfig.setSignType("RSA2"); return alipayConfig; } } ``` ### 签约重要参数(agreement_sign_params)说明 - period_rule_params:周期管控规则参数 ,必填。 - period_type:周期类型 ,枚举值为 DAY 和 MONTH。分别代表周期的单位为「天」或「月」
**说明**:周期类型使用**DAY**的时候,周期数 period**不允许小于 7(包含7)**,避免间隔日期太短,扣款过于频繁,没有周期限制的意义。 - period:必填,周期数,与 period_type 组合使用确定扣款周期,例如 period_type 为 DAY,period = 90,则扣款周期为 90 天。 - single_amount:单次扣款的正价金额,必填,单位为元。该价格会在用户的协议中对客展示,请务必正确传参。需要注意:商家每次发起扣款都不允许大于此金额。 - total_amount:订单总金额。首次支付金额,**不算在周期扣总金额里面**。 - product_code:商家扣款产品码固定为GENERAL_WITHHOLDING。 - personal_product_code:周期扣款模式个人签约产品码固定为 CYCLE_PAY_AUTH_P。 - sign_scene:签约场景码,具体参数请商家完成产品签约后,根据业务场景或用户购买商品的差异性对应新增模版及场景码。****说明**:商家需要与支付宝BD确认新增模版及场景码,签约接入时需要传入模版中实际填写的场景码。 - external_agreement_no:商家签约号,代扣协议中标示用户的唯一签约号(确保在商家系统中唯一)。用户支持一对多,即同一个商家下,同一个用户可以有多套签约关系。通过商家外部签约号和场景参数来区分签约协议。若签约时传入了签约号及场景参数,后续查询协议和解约时也必须传入。 - access_params:请按当前接入的方式进行填充,且输入值必须为文档中的参数取值范围。 - channel:目前支持以下值: - ALIPAYAPP :支付宝客户端 H5 页面签约。 - QRCODE:扫码签约。 - QRCODEORSMS:扫码签约或者短信签约。 - sign_notify_url:签约成功异步通知地址。支付并签约场景中,用户支付成功及签约成功都会触发异步通知。规则如下: - 若 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)只设置 notify_url 则两条异步通知都会发送到该地址。 - 若 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)分别设置 notify_url 及 sign_notify_url 则支付成功异步通知会发送到 notify_url,签约成功异步通知会发送到 sign_notify_url。 ### 唤起支付宝客户端并签约 集成方案按照App支付请求支付的模式即可,可查看 [客户端 SDK&DEMO](https://ideservice.alipay.com/cms/site/0j2uoc)。 ## 创建小程序支付并签约 使用步骤: 1. 在小程序中下单,获取交易号tradeNo 2. 唤起收银台完成支付 ### 前期准备 接入 JASPI支付产品 [JSAPI支付接入准备](https://ideservice.alipay.com/cms/site/0iz9fn)。 ### 创建交易订单 在服务端调用 [alipay.trade.create](https://ideservice.alipay.com/cms/site/0izg0p)(统一收单交易创建接口),获得支付宝交易号 tradeNO: #### 示例代码 ```java AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2"); AlipayTradeCreateRequest request = new AlipayTradeCreateRequest(); request.setNotifyUrl(""); JSONObject bizContent = new JSONObject(); bizContent.put("out_trade_no", "20210817010101003"); bizContent.put("total_amount", 0.01); bizContent.put("subject", "测试商品"); // UID参数未来计划废弃,存量商户可继续使用,新商户请使用openid。请根据应用-开发配置-openid配置选择支持的字段。 bizContent.put("buyer_id", "2088102146225135"); // 使用 openid 时: // bizContent.put("buyer_open_id", "074a1CcTG1LelxKe4xQC0zgNdId0nxi95b5lsNpazWYoCo5"); bizContent.put("timeout_express", "8d"); bizContent.put("product_code", "JSAPI_PAY"); //签约参数 JSONObject agreement_sign_params = new JSONObject(); agreement_sign_params.put("product_code", "GENERAL_WITHHOLDING"); agreement_sign_params.put("personal_product_code", "CYCLE_PAY_AUTH_P"); agreement_sign_params.put("sign_scene", "场景码xxxx"); agreement_sign_params.put("external_agreement_no", "外部协议号xxx"); agreement_sign_params.put("sign_notify_url", "异步通知地址xxx"); //周期规则参数,必填 JSONObject period_rule_params = new JSONObject(); period_rule_params.put("period_type", "MONTH"); period_rule_params.put("period", "1"); period_rule_params.put("single_amount", "0.01"); agreement_sign_params.put("period_rule_params", period_rule_params); JSONObject access_params = new JSONObject(); access_params.put("channel", "ALIPAYAPP"); agreement_sign_params.put("access_params", access_params); bizContent.put("agreement_sign_params", agreement_sign_params); //// 商品明细信息,按需传入 //JSONArray goodsDetail = new JSONArray(); //JSONObject goods1 = new JSONObject(); //goods1.put("goods_id", "goodsNo1"); //goods1.put("goods_name", "子商品1"); //goods1.put("quantity", 1); //goods1.put("price", 0.01); //goodsDetail.add(goods1); //bizContent.put("goods_detail", goodsDetail); //// 结算信息,按需传入 //JSONObject settleInfo = new JSONObject(); //JSONArray settleDetailInfos = new JSONArray(); //JSONObject settleDetail = new JSONObject(); //settleDetail.put("trans_in_type", "defaultSettle"); //settleDetail.put("amount", 0.01); //settleDetailInfos.add(settleDetail); //settleInfo.put("settle_detail_infos", settleDetailInfos); //bizContent.put("settle_info", settleInfo); //// 二级商户信息,按需传入 //JSONObject subMerchant = new JSONObject(); //subMerchant.put("merchant_id", "2088000603999128"); //bizContent.put("sub_merchant", subMerchant); //// 业务参数信息,按需传入 //JSONObject businessParams = new JSONObject(); //businessParams.put("busi_params_key", "busiParamsValue"); //bizContent.put("business_params", businessParams); //// 营销信息,按需传入 //JSONObject promoParams = new JSONObject(); //promoParams.put("promo_params_key", "promoParamsValue"); //bizContent.put("promo_params", promoParams); request.setBizContent(bizContent.toString()); AlipayTradeCreateResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println("调用成功"); } else { System.out.println("调用失败"); } ``` #### 部分参数说明 完整入参说明可查看 [alipay.trade.create(统一收单交易创建接口)](https://ideservice.alipay.com/cms/site/0izg0p)文档。 - subject:必填,商品的标题/交易标题/订单标题/订单关键字等。 不可使用特殊字符,例如`/,=,&`等。 - total_amount:必填,订单总金额,单位为元,精确到小数点后两位,取值范围 [0.01,100000000]。 - buyer_id(buyer_open_id):必填,买家支付宝用户的 ID,获取详情可查看 [用户授权文档](https://ideservice.alipay.com/cms/site/0j2r9x)。 - seller_id:卖家支付宝用户 ID。可登录 [账户中心](https://developers.alipay.com/dev/workspace/account-center/main-account-manage)>**主账号信息**>**账号 ID**获取。 - notify_url:接收的异步通知 http/https 路径地址。用户支付成功时,支付宝将向本地址发送异步通知。 - product_code:在小程序场景下使用时,需传入 JSAPI_PAY。 - agreement_sign_params:签约参数。其中,product_code 字段必填,且传入固定值 GENERAL_WITHHOLDING; **注意**:[alipay.trade.create(统一收单交易创建接口)](https://ideservice.alipay.com/cms/site/0izg0p)不支持 sign_notify_url 字段。 ### 唤起收银台支付 在小程序端使用 tradeNO,调用 [my.tradePay](https://ideservice.alipay.com/cms/site/0iziyf) 接口唤起支付宝收银台,引导用户完成支付。
**说明**:如有资金分账需求,可选择接入 [商家分账](https://ideservice.alipay.com/cms/site/0j2q5p) 产品。 #### 示例代码 ```javascript // .js my.tradePay({ // 调用统一收单交易创建接口(alipay.trade.create),获得返回字段支付宝交易号trade_no tradeNO: '201711152100110410533667792', success: (res) => { my.alert({ content: JSON.stringify(res), }); }, fail: (res) => { my.alert({ content: JSON.stringify(res), }); } }); ``` #### 部分参数说明 Object 类型,参数如下: | **参数** | **类型** | **必填** | **描述** | | --- | --- | --- | --- | | tradeNO | String | 是 | 接入小程序**JSAPI支付**时传入此参数。此参数为支付宝交易号,注意参数有大小写区分。 | | success | Function | 否 | 调用成功的回调函数。 | | fail | Function | 否 | 调用失败的回调函数。 | | complete | Function | 否 | 调用结束的回调函数(调用成功、失败都会执行)。 | **Function success**
success 回调函数会携带一个 Object 类型的对象,其属性如下: | **属性** | **类型** | **描述** | | --- | --- | --- | | resultCode | String | 支付结果码,下表。 | **结果码** | **结果码** | **描述** | **解决方案** | | --- | --- | --- | | 4 | 无权限调用(N22104)。 | 个人小程序应用没有开放小程序**JSAPI支付**能力。 | | 9000 | 订单处理成功。 | 不建议根据 my.tradePay 接口同步返回判断是否支付成功,9000 不能判定就是支付成功,请以异步通知(notify_url)返回的 trade_status(交易状态)为 TRADE_SUCCESS + [alipay.trade.query(统一收单交易查询接口)](https://ideservice.alipay.com/cms/site/0j0a27)查询订单是否支付成功实际返回的支付状态为准。 | | 8000 | 正在处理中。支付结果未知(有可能已经支付成功)。 | 请调用 [alipay.trade.query(统一收单交易查询接口)](https://ideservice.alipay.com/cms/site/0j0a27)查询商家订单列表中订单的支付状态,以查询接口实际返回的支付状态为准。 | | 4000 | 订单处理失败。 | tradeNO 调用 **JSAPI支付 **时必填
- 检查入参字段 tradeNO 是否编写正确,"NO"都是大写。
- tradeNO 的入参数据是[alipay.trade.create(统一收单交易创建接口)](https://ideservice.alipay.com/cms/site/0izg0p)返回的 “trade_no”,不是 “out_trade_no”。
| | 6001 | 用户中途取消。 |
- 请用户重新签约 / 支付。
- 检查 tradeNO 的入参是否为正常入参,参数数据为[alipay.trade.create(统一收单交易创建接口)](https://ideservice.alipay.com/cms/site/0izg0p)返回的“trade_no”。
- **alipay.trade.create**(统一收单交易创建接口)在小程序场景中 **buyer_id(buyer_open_id)**参数必填,且必须和前端唤起支付的支付宝账号一致。
| | 6002 | 网络连接出错。 | 检查网络连接后重试。 | | 6004 | 处理结果未知(有可能已经成功)。 | 请调用 [alipay.trade.query(统一收单交易查询接口)](https://ideservice.alipay.com/cms/site/0j0a27)查询商家订单列表中订单的支付状态,以查询接口实际返回的支付状态为准。 | #### 订单状态查询 商家可调用 [alipay.trade.query(统一收单交易查询接口)](https://ideservice.alipay.com/cms/site/0j0a27),通过商家网站唯一订单号 out_trade_no 或支付宝交易号 trade_no 查询对应订单支付情况。 ## 创建PC端网页支付并签约 商家调用 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)传入用户支付信息及商家扣款信息,创建商家扣款支付订单。 ### 前期准备 接入 APP支付产品,详情查看 [准备文档](https://ideservice.alipay.com/cms/site/0izcsa)。 ### 创建交易订单 商家调用 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)传入用户支付信息及商家扣款信息,创建商家扣款支付订单。 ### 示例代码 ```java package com.java.sdk.demo; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.AlipayConfig; import com.alipay.api.request.AlipayTradeAppPayRequest; import com.alipay.api.domain.SignParams; import com.alipay.api.domain.PeriodRuleParams; import com.alipay.api.domain.AccessParams; import com.alipay.api.domain.AlipayTradeAppPayModel; import com.alipay.api.response.AlipayTradeAppPayResponse; import com.alipay.api.FileItem; import java.util.Base64; import java.util.ArrayList; import java.util.List; public class AlipayTradeAppPay { public static void main(String[] args) throws AlipayApiException { // 初始化SDK AlipayClient alipayClient = new DefaultAlipayClient(getAlipayConfig()); // 构造请求参数以调用接口 AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest(); AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); // 设置商户订单号 model.setOutTradeNo("70501111111S001111119"); // 设置订单总金额 model.setTotalAmount("9.00"); // 设置订单标题 model.setSubject("订单商品名称"); // 设置产品码 model.setProductCode("QUICK_MSECURITY_PAY"); // 设置订单绝对超时时间 model.setTimeExpire("2016-12-31 10:05:00"); // 设置签约参数 SignParams agreementSignParams = new SignParams(); AccessParams accessParams = new AccessParams(); accessParams.setChannel("ALIPAYAPP"); agreementSignParams.setAccessParams(accessParams); PeriodRuleParams periodRuleParams = new PeriodRuleParams(); periodRuleParams.setPeriod(1L); periodRuleParams.setSingleAmount("10.99"); periodRuleParams.setPeriodType("MONTH"); agreementSignParams.setPeriodRuleParams(periodRuleParams); agreementSignParams.setSignNotifyUrl("http://www.merchant.com/receiveSignNotify"); agreementSignParams.setExternalLogonId("13888888888"); agreementSignParams.setPersonalProductCode("CYCLE_PAY_AUTH_P"); agreementSignParams.setExternalAgreementNo("test20190701"); agreementSignParams.setProductCode("GENERAL_WITHHOLDING"); agreementSignParams.setSignScene("INDUSTRY|XXXXXXX"); model.setAgreementSignParams(agreementSignParams); request.setBizModel(model); // 第三方代调用模式下请设置app_auth_token // request.putOtherTextParam("app_auth_token", "<-- 请填写应用授权令牌 -->"); AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); String orderStr = response.getBody(); System.out.println(orderStr); if (response.isSuccess()) { System.out.println("调用成功"); } else { System.out.println("调用失败"); // sdk版本是"4.38.0.ALL"及以上,可以参考下面的示例获取诊断链接 // String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response); // System.out.println(diagnosisUrl); } } private static AlipayConfig getAlipayConfig() { String privateKey = "<-- 请填写您的应用私钥,例如:MIIEvQIBADANB ... ... -->"; String alipayPublicKey = "<-- 请填写您的支付宝公钥,例如:MIIBIjANBg... -->"; AlipayConfig alipayConfig = new AlipayConfig(); alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do"); alipayConfig.setAppId("<-- 请填写您的AppId,例如:2019091767145019 -->"); alipayConfig.setPrivateKey(privateKey); alipayConfig.setFormat("json"); alipayConfig.setAlipayPublicKey(alipayPublicKey); alipayConfig.setCharset("UTF-8"); alipayConfig.setSignType("RSA2"); return alipayConfig; } } ``` ### 签约重要参数(agreement_sign_params)说明 - period_rule_params:周期管控规则参数 ,必填。 - period_type:周期类型 ,枚举值为 DAY 和 MONTH。分别代表周期的单位为「天」或「月」
**说明**:周期类型使用**DAY**的时候,周期数 period**不允许小于 7(包含7)**,避免间隔日期太短,扣款过于频繁,没有周期限制的意义。 - period:必填,周期数,与 period_type 组合使用确定扣款周期,例如 period_type 为 DAY,period = 90,则扣款周期为 90 天。 - single_amount:单次扣款的正价金额,必填,单位为元。该价格会在用户的协议中对客展示,请务必正确传参。需要注意:商家每次发起扣款都不允许大于此金额。 - total_amount:订单总金额。首次支付金额,**不算在周期扣总金额里面**。 - product_code:商家扣款产品码固定为GENERAL_WITHHOLDING。 - personal_product_code:周期扣款模式个人签约产品码固定为 CYCLE_PAY_AUTH_P。 - sign_scene:签约场景码,具体参数请商家完成产品签约后,根据业务场景或用户购买商品的差异性对应新增模版及场景码。****说明**:商家需要与支付宝BD确认新增模版及场景码,签约接入时需要传入模版中实际填写的场景码。 - external_agreement_no:商家签约号,代扣协议中标示用户的唯一签约号(确保在商家系统中唯一)。用户支持一对多,即同一个商家下,同一个用户可以有多套签约关系。通过商家外部签约号和场景参数来区分签约协议。若签约时传入了签约号及场景参数,后续查询协议和解约时也必须传入。 - access_params:请按当前接入的方式进行填充,且输入值必须为文档中的参数取值范围。 - channel:目前支持以下值: - ALIPAYAPP :支付宝客户端 H5 页面签约。 - QRCODE:扫码签约。 - QRCODEORSMS:扫码签约或者短信签约。 - sign_notify_url:签约成功异步通知地址。支付并签约场景中,用户支付成功及签约成功都会触发异步通知。规则如下: - 若 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)只设置 notify_url 则两条异步通知都会发送到该地址。 - 若 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)分别设置 notify_url 及 sign_notify_url 则支付成功异步通知会发送到 notify_url,签约成功异步通知会发送到 sign_notify_url。 ### 唤起支付宝客户端并签约 集成方案按照App支付请求支付的模式即可,可查看 [客户端 SDK&DEMO](https://ideservice.alipay.com/cms/site/0j2uoc)。 ### 生成支付并签约串 sdkExecute 生成支付签约串: ```java AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request); //生成唤起客户端。把signParams使用 UTF-8 字符集整体做一次 encode String signStr = URLEncoder.encode(response.getBody(), "UTF-8"); return 'alipays://platformapi/startApp?appId=60000157&orderStr='+signStr ``` 1. 将 alipay.trade.app.pay(app支付接口2.0)生成的请求字符串 encode处理之后拼接在 `alipays://platformapi/startApp?appId=60000157&orderStr=` 后面。 2. 把拼接好的字符串转换为二维码,在 PC 端页面展示。 3. 用户使用支付宝客户端进行扫码签约实现支付并签约。 # 签约成功后扣款 在用户和商户完成代扣签约后,商家系统可以获得对应的代扣协议号。当用户消费后,商户可以将用户的代扣协议号和订单信息一起通过 [alipay.trade.pay(统一收单交易支付接口)](https://ideservice.alipay.com/cms/site/0j06h2)发送代扣支付请求到支付宝,支付宝会先受理请求,再异步发起真实扣款操作,商家可从异步通知中获取支付结果。
**注意**:支付宝会先受理请求,然后再异步发起真实扣款操作。使用该能力前,商户理解并接受,有以下情形之一的,支付宝不再发起扣款,相关风险由商户自行承担,包括但不限于: 1. 扣款成功; 2. 交易超时关闭; 3. 超过合理扣款时间; 4. 发起扣款的时间不在允许扣款的时间范围内; 5. 可能存在安全风险,或其他支付宝认为不适合或不应再扣款的情形。 ## 扣款流程图 ``` sequenceDiagram participant 商户系统 participant 支付宝 商户系统->>支付宝: 1: 调用alipay.trade.pay发起支付,传入支付模式 支付宝-->>商户系统: 1.1: 返回支付结果 商户系统->>商户系统: 2: 根据返回结果处理,见下文code结果码和处理方式 支付宝->>商户系统: 3: 发送交易成功消息 商户系统->>商户系统: 3.1: 处理成功消息 支付宝->>商户系统: 4: 发送异步失败消息 商户系统->>商户系统: 4.1: 处理异步支付失败消息 ``` ### 发起扣款 - 扣款需要通过 pay_params 设置 异步支付类型(async_type) :NORMAL_ASYNC: 普通异步模式。此模式下,还需要设置 timeout_express ,目前商家扣款场景下,该参数配置最小单位为「d-天」。目前默认此参数为8天(通知后24小时等待+默认7天的扣款时间),商户也可在2-8天之间自主传参控制关单时间。 - 允许商家扣款的时间:在用户首次支付并签约后,系统会自动计算「下次预计扣款时间」=支付成功时间+扣款周期,下次预计扣款时间前48小时,商家可以发起扣款。在可扣款周期内,商户可随时发起扣款请求。 **注意:**为保障用户体验,扣费操作不可在晚上执行,请于北京时间 **7:00~22:00** 期间发起扣款。 ### 扣款执行流程 - 支付宝在受理支付请求后,会立刻发送「扣款预通知」给到用户,提醒用户即将发生扣款行为,确认用户的扣费意愿; - 「扣款预通知」的24小时以后,支付宝将自动对用户发起扣款,支付成功后会通知商户支付成功结果; - 支付宝将持续对用户发起扣款,默认持续7天,商户也可以通过传参「timeout_express」来缩短这一时间。若到期仍未扣款成功,则会通知商户支付失败,商户侧可重新发起扣款; - 商户重新发起扣款后,仍然按照上述流程执行,直到用户扣款成功,则本周期扣款结束,依据扣款成功时间,刷新下一周期的可扣款时间。 ## 同步返回参数说明 | **参数** | **类型** | **是否必填** | **最大长度** | **描述** | **示例值** | | --- | --- | --- | --- | --- | --- | | code | String | 是 | ~ | 详见下文 结果码和处理方式。 | 40004  | | msg | String | 是 | ~ | 返回码描述 | Business Failed | | sub_code | String | 否 | ~ | 子返回码 | AGREEMENT_NOT_EXIST | | sub_msg | String | 否 | ~ | 子返回码描述 | 协议不存在 | | sign | String | 是 | 256 | 商户请求参数的签名串,详见 [签名](https://ideservichttps://ideservice.alipay.com/cms/site/02khjme.alipay.com/cms/site/02khjm) | DZXh8eeTuAHoYE3w1 J+POiPhfDxOYBfUNn1 lkeT/V7P4zJdyojWEa6 IZs6Hz0yDW5Cp/viufU b5I0/V5WENS3OYR8zR edqo6D+fUTdLHdc+E FyCkiQhBxIzgngPdPdf p1PIS7BdhhzrsZHbRq b7o4k3Dxc+AAnFauu4 V6Zdwczo= | #### code 结果码和处理方式 根据公共返回参数中的 code,这笔交易可能有四种状态:支付成功(10000),支付失败(40004),等待用户付款(10003)和未知异常(20000)。 | **结果码** | **说明** | **处理方式** | | --- | --- | --- | | 10000 | 支付成功 | 记录交易结果并在显示支付成功,进入后续的业务处理。 | | 40004 | 支付失败 | 记录交易结果并显示错误信息(display_message)。 | | 10003 | 支付处理中 | 根据交易返回结果处理,async_payment_mode = NORMAL_ASYNC_PAY 表明异步受理成功。 商户需要等待支付宝成功结果或者失败结果通知。
如果 async_payment_mode 没有返回,则表明异步受理结果未知,商户可以重试。 | | 20000 | 未知异常 | **一般等待1分钟后,保持相同的参数重试。** | ### 注意事项 商家扣款场景下使用 [alipay.trade.pay(统一收单交易支付接口)](https://ideservice.alipay.com/cms/site/0j06h2)时: - product_code:必填,产品码。固定为GENERAL_WITHHOLDING。 - subject:必填,订单标题。不可使用特殊字符,如 /,=,& 等。 - agreement_params:必填,签约信息。 - agreement_no:必填,支付宝系统中用以唯一标识用户签约记录的编号(用户签约成功后的协议号)。 - 如果不填卖家 seller_id,则默认为商家签约账号对应的支付宝用户 ID。 ## 支付异步通知参数 支付成功异步通知参数说明具体请查看 [异步通知说明](https://ideservice.alipay.com/cms/site/0j02er)。 ## 签约异步通知说明 签约异步通知仅当用户签约成功时触发。商家以 POST 接收方式接收异步签约通知,优先接收接口 [alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)中传的 sign_notify_url,若没传则取[alipay.trade.app.pay(app支付接口2.0接口)](https://ideservice.alipay.com/cms/site/0j092b)中传的 notify_url,若前两者均没传则取**应用网关 **地址(商家必须在[开放平台](https://openhome.alipay.com/platform/home.htm) **应用信息**中配置**应用网关**)。 ### 商家验签 在通知返回参数列表中除去 sign、sign_type 两个参数外,凡是通知返回回来的参数皆是待验签的参数。 ### 异步通知示例 根据 status 触发条件,只有签约成功和解约成功才会返回异步,签约失败和解约失败都不会触发异步。 ```json charset=utf-8¬ify_time=2020-04-24 11:28:20&alipay_user_id=2088*****9864&alipay_open_id=074a1CcTG1LelxKe4xQC0zgNdId0nxi95b5lsNpazWYoCo5&sign=ooWBuCDIQSumOpaGBcQz+aoAqyGh3W6EqA/gmyPYwLJ2REFijY***9XPTApI9YglZyMw+ZMhd3kb0mh4RAXMrb6me&external_agreement_no=138528528786000&version=1.0&sign_time=2020-04-24 11:28:20¬ify_id=2020042400222112820072971427431169¬ify_type=dut_user_sign&agreement_no=20205224633661163886&invalid_time=2115-02-01 00:00:00&auth_app_id=2014*****222&personal_product_code=CYCLE_PAY_AUTH_P&valid_time=2020-04-24 11:28:20&login_token=1948bbcaac503bb499a1c62dbb584d0f_86&app_id=2014*****22&sign_type=RSA2&sign_scene=INDUSTRY|DEFAULT_SCENE&status=NORMAL&alipay_logon_id=159******10&next_deduct_time=2022-06-15 //用户Id类字段根据选用的ID标准(UID或openid)选择示例 ``` ### 重要参数说明 - status:协议状态,枚举支持。 - NORMAL:正常。 - UNSIGN:解约。 - external_agreement_no:标示用户的唯一签约协议号,商家自定义。仅签约接口传入时返回。 - agreement_no:支付宝系统中用以唯一标识用户签约记录的编号。 - notify_type:异步通知类型,枚举支持。 - dut_user_sign:当 status = NORMAL 表示签约成功。 - dut_user_unsign:当 status = UNSIGN 表示解约成功。 - sign_scene:签约场景码。 - personal_product_code:协议产品码。 - alipay_user_id:用户的支付宝账号对应的支付宝唯一用户号。新商户建议使用open_id替代该字段。对于新商户,user_id字段未来计划逐步回收,存量商户可继续使用。如使用open_id,请确认 应用-开发配置-openid配置管理 已启用。无该配置项,可查看 [openid配置申请](https://ideservice.alipay.com/cms/site/0ai9ok)。 - alipay_logon_id:用户的支付宝登录账号。 - next_deduct_time:周期扣协议,预计下次扣款时间。 - cycle_deduct_mode:周期扣款模式,用于区分周期扣款模式中的「普通」和「通知后扣款」模式 - 「普通」和「通知后扣款」模式基于支付宝BD在对应模板中的配置,本文的接入均为「通知后扣款」模式。 # 辅助功能 ## 商家扣款协议查询 商家可调用 [alipay.user.agreement.query(支付宝个人代扣协议查询接口)](https://ideservice.alipay.com/cms/site/0j01kl)查询用户是否完成签约操作,用户实际签约结果请以查询接口返回值为准。 - 通过协议号查询协议详情,此时请求参数中填入协议号(agreement_no),其它业务参数均可不填。 - 如果在支付宝个人协议页面签约接口接口中传递了`sign_scene`+`external_agreement_no` 2 个参数,那在对应的协议查询接口中传递`personal_product_code`+`sign_scene`+`external_agreement_no` 3 个参数即可查询到内容。 - 如果在支付宝个人协议页面签约接口中没有传递`sign_scene`+`external_agreement_no`2 个参数,那在对应的协议查询接口中传递`personal_product_code`+(`alipay_logon_id`和`alipay_user_id(alipay_open_id)`2 个参数中的任何一个参数)即可查询到内容。 ## 商家扣款协议解约 ### 商家解约 开发者可调用 [alipay.user.agreement.unsign(支付宝个人代扣协议解约接口)](https://ideservice.alipay.com/cms/site/0j06h0)完成协议解约。 - 通过协议号解约,此时请求参数中填入协议号(agreement_no),其它业务参数均可不填。 - 通过`personal_product_code`+`sign_scene+external_agreement_no`+`alipay_logon_id+alipay_user_id(alipay_open_id)`来解约,入参值按照商家调 [alipay.user.agreement.page.sign(支付宝个人协议页面签约接口)](https://ideservice.alipay.com/cms/site/0j01kk)中的对应值传入;如果传入 external_agreement_no,则 alipay_logon_id 与 alipay_user_id(alipay_open_id)可不传,否则 alipay_logon_id 与alipay_user_id(alipay_open_id)必须传其中一个。 ### 用户解约 用户可主动在支付宝客户端进行解约(无需商家侧接口调用),解约成功后支付宝将发送解约信息给商家应用在 [支付宝开放平台](https://openhome.alipay.com/platform/home.htm) 中配置的 **应用网关 **地址。通知的内容格式请查看 [alipay.user.agreement.unsign(支付宝个人代扣协议解约接口)](https://ideservice.alipay.com/cms/site/0j06h0)中的异步通知格式。
**注意:** - 当用户通过支付宝 APP 解约成功后,支付宝发送给商家的异步通知参数**编码方式**优先采用签约应用(APPID)在开放平台上配置的编码方式,若无配置默认为**GBK**编码。 - 若应用类型为生活号请查看[生活号接入文档](https://ideservice.alipay.com/cms/site/0j2x6u)激活开发者模式。 ### 解约异步通知说明 解约异步通知仅当用户解约成功或商家调解约接口成功时触发。商家以 POST 方式接收异步解约通知,如果是用户解约成功通知地址是 **应用网关** 地址(商家必须在 [开放平台](https://openhome.alipay.com/platform/home.htm) **应用信息** 中配置 **应用网关**)。如果是商家调用 [alipay.user.agreement.unsign(支付宝个人代扣协议解约接口)](https://ideservice.alipay.com/cms/site/0j06h0)解约优先接收接口中传的 notify_url,如果没传则取 **应用网关** 地址。 #### 异步通知示例 ```json https://api.xx.com/receive_notify.htm?sign_modify_time=2020-01-07+03%3A55%3A59 ¬ify_time =2020-01-07+03%3A56%3A00 &unsign_time =2020-01-07+03%3A55%3A59 &alipay_user_id =2088702557905710 &sign =$$$ &product_code =CYCLE_PAY_AUTH_P ¬ify_id =2020010700222035600068631413009142 &scene =INDUSTRY%7CAPPSTORE ¬ify_type =dut_user_unsign &agreement_no =20170903122633145771 &external_sign_no =EhdtGwb5y4oP5KvSALDcSF &sign_type =RSA &status =UNSIGN ``` **注意**:UNSIGN(解约)是只有解约(解约接口或者客户端解约)才会触发返回的状态,当解约后调用 [alipay.user.agreement.query(支付宝个人代扣协议查询接口)](https://ideservice.alipay.com/cms/site/0j01kl)查询协议,会提示 **用户协议不存在(USER_AGREEMENT_NOT_EXIST)**,不会返回 status=UNSIGN 的状态。 ## 订单支付后续 - 使用 [alipay.trade.refund(统一收单交易退款接口)](https://ideservice.alipay.com/cms/site/0j02es)完成退款。 - 使用 [alipay.trade.cancel(统一收单交易撤销接口)](https://ideservice.alipay.com/cms/site/0j01kn)完成撤销。 - 使用 [alipay.trade.close(统一收单交易关闭接口)](https://ideservice.alipay.com/cms/site/0j02et)完成关闭交易订单。 ### 下载对账单 对账单可通过 [alipay.data.dataservice.bill.downloadurl.query(查询对账单下载地址接口)](https://ideservice.alipay.com/cms/site/0j02eu)。 # 接口报错 接口异常信息及解决方案,详情可查看 [常见问题](https://ideservice.alipay.com/cms/site/0j01kh)。