<!-- # 支付宝支付接入 --> <!-- alipay-memo --> 最近几天给快应用接入了支付宝支付,记录一下做个备忘。 使用的是 [Alipay SDK][1] Java 版的证书方式调用。 流程图:  ## 1. 创建订单 服务端调用支付宝接口创建订单,代码直接参考[官方 Alipay SDK 文档][1]即可。 ```java //构造client CertAlipayRequest certAlipayRequest = new CertAlipayRequest(); //设置网关地址 certAlipayRequest.setServerUrl("https://openapi.alipay.com/gateway.do"); //设置应用Id certAlipayRequest.setAppId(app_id); //设置应用私钥 certAlipayRequest.setPrivateKey(privateKey); //设置请求格式,固定值json certAlipayRequest.setFormat("json"); //设置字符集 certAlipayRequest.setCharset(charset); //设置签名类型 certAlipayRequest.setSignType(sign_type); //设置应用公钥证书路径 certAlipayRequest.setCertPath(app_cert_path); //设置支付宝公钥证书路径 certAlipayRequest.setAlipayPublicCertPath(alipay_cert_path); //设置支付宝根证书路径 certAlipayRequest.setRootCertPath(alipay_root_cert_path); //构造client AlipayClient alipayClient = new DefaultAlipayClient(certAlipayRequest); //构造API请求 AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); //发送请求 AlipayTradeQueryResponse response = alipayClient.certificateExecute(request); ``` 比较奇怪的是返回的响应中,除了 *body* 字段外,其它的都是 `null` 。 <!-- 订单 P219200688300000000 的下单结果: --> 响应内容: ```json { "body": "alipay_root_cert_sn=687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6&alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_cert_sn=7eb546098e0ad9016f69143243868330&app_id=2021002100000000&biz_content=%7B%22body%22%3A%22%22%2C%22goods_type%22%3A%220%22%2C%22out_trade_no%22%3A%22P219200688300000000%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22subject%22%3A%22%E5%85%850.01%E5%BE%9720000%E4%B9%A6%E5%B8%81%22%2C%22timeout_express%22%3A%2230m%22%2C%22total_amount%22%3A%220.01%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay¬ify_url=https%3A%2F%2Ftest.hyperion-novel-qa-app.mokamrp.com%2Fali%2Fpay%2Fnotify%2Forder%2F2021002100000000&sign=lfiiodhqqp1pwrwioo7olb1e8ftsmhewhziezsycanis75jo9yna4k6czh%2b2ezmo9ebsqfb3o%2fug7ga22b37qoff6pgkklhwjdxpunkqqhbyagkalc5l9zyf6u2s7czdcryt%2flvba3crdt339mdlebfqivzx%2bb2u6loss%2fqwbmfanba0ztq%2bd%2frydc%2bvvleftdrojvycqi8dcqbcfdp%2djcn71j3iywredjsu8htjoqbdorl7le46sdvaav0o%2bh%2f%2bo7vov6dgke31%2bqegokiwqp5bq%2b1nsc2jzd6tkrnlzqc30qijnr0myb%2bk%2bpvrqzsq7lqa4vxbebo6cetsh5ifwg%3d%3D&sign_type=RSA2×tamp=2021-11-26+13%3A49%3A45&version=1.0" } ``` ## 2. 查询订单 查询订单代码参考[alipay.trade.query][2] 。需要注意的是,如果使用证书的方式,应调用 `certificateExecute` 方法,否则会报错。 ```java AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2"); AlipayTradeQueryRequest request = new AlipayTradeQueryRequest(); request.setBizContent("{" + " \"out_trade_no\":\"20150320010101001\"," + " \"trade_no\":\"2014112611001004680 073956707\"," + " \"query_options\":[" + " \"trade_settle_info\"" + " ]" + "}"); AlipayTradeQueryResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println("调用成功"); } else { System.out.println("调用失败"); } ``` ### 2.1 *ACQ.TRADE_NOT_EXIST* 交易不存在 支付宝在客户端未支付的情况下(如未能拉起支付宝APP、未付款、付款失败),使用 *out_trade_no* 查询订单时,返回的代码为 *40004*,错误代码为 *ACQ.TRADE_NOT_EXIST* (交易不存在)。 这个流程使用起来感觉有些怪异,调用创建订单接口时支付宝的服务端应该是会保存订单信息的,结果却返回交易不存在。不知道是不是此时真的没有创建订单,还是因为别的什么原因,故意显示成这样的。 这导致开发者在同步订单状态时有点麻烦,好在创建订单接口提供了一个订单相对超时时间参数(*timeoutExpress*),官方示例中使用的是 30 分钟。还有另外一个订单绝对超时时间(*timeExpire*),代码备注中显示推荐使用这个参数。 ```java model.setTimeoutExpress("30m"); ``` <!-- 订单 P219200688300000000 的查询结果: --> 客户端未支付时查询订单返回的响应内容如下: ```json { "msg": "Business Failed", "pointAmount": "0.00", "code": "40004", "invoiceAmount": "0.00", "body": "{\"alipay_trade_query_response\":{\"code\":\"40004\",\"msg\":\"Business Failed\",\"sub_code\":\"ACQ.TRADE_NOT_EXIST\",\"sub_msg\":\"交易不存在\",\"buyer_pay_amount\":\"0.00\",\"invoice_amount\":\"0.00\",\"out_trade_no\":\"P219200688300000000\",\"point_amount\":\"0.00\",\"receipt_amount\":\"0.00\"},\"alipay_cert_sn\":\"78aa9258336cb49f0000000000000000\",\"sign\":\"nxrfqos5/dvvgq5qhpaaqp8uqkxsfugqlh2veggpjxcg0yopo12pitd4jrcvvduh7tfedk/rdd5o7bkrdcqt0h8a6aaocnu7wbhjim9ptlrhcr3ilostjwt7bpstfadlfw108dfbpvxbcz7c3j1ahk+om0hmtxjccr+qfqj2+j3jfjebhvpgbpl9jsiu3frgbysddvqu20iszet6fvufhwsfnkjnbkq+gmjlkujhxxgmavam/dk0mh9eddqxtsxnbggr1d6nnbwcagaaukky/+9zk9wkac5zhzcuxhj820vyumkhz3ojccyhfagfs544rs+iwquoey8ccla0rcimta==\"}", "params": { "biz_content": "{\"out_trade_no\":\"P219200688300000000\"}" }, "receiptAmount": "0.00", "subCode": "ACQ.TRADE_NOT_EXIST", "outTradeNo": "P219200688300000000", "buyerPayAmount": "0.00", "subMsg": "交易不存在" } ``` ### 2.2 交易成功 #### 2.2.1 支付成功异步通知 客户端付款成功后,支付宝会发送异步通知到创建订单时指定的 *notifyUrl* 异步通知地址。 代码参考[官方文档][4]中的服务端验证异步通知信息参数示例进行验签。接收参数是个 `Map<String, String>` 型的实例,使用起来不太方便,设计成这样估计是为了兼容性。 ```java //获取支付宝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); } //切记alipayPublicCertPath是应用公钥证书路径,请去open.alipay.com对应应用下载。 //boolean AlipaySignature.rsaCertCheckV1(Map<String, String> params, String publicKeyCertPath, String charset,String signType) boolean flag = AlipaySignature.rsaCertCheckV1(params,alipayPublicCertPath,charset,"RSA2"); ``` 请求Body如下(为方便查看,添加了换行,实际的请求体中没有换行): ```json gmt_create=2021-11-26+13%3A50%3A07 &charset=UTF-8 &seller_email=ywu1112%40***.com &subject=%E5%85%850.01%E5%BE%9720000%E4%B9%A6%E5%B8%81 &sign=oihr%2fajqzzwy2lwoih%2b%2b0tg2vlesrl5odpxpujju1jy%2bxt6ckdtzliqetz75hqzpddkckotsb5qavnzrptax1hspeqshd7unjw25cnptsr4zult9eogzzen501kbntuexhbjwnaogbxplkqskrauwrl5ngavhaxqviyftitzgjlml%2bahc2z6qptrddcbjhcwu4znvjhqfup2ppxymw8ccxwribbsrvi62iizk6r7cdc7p7swlldrdvi9djaadaa2fe4shkqsciddz6hnpolu%2fuxe94albqqeytn4%2bqwkhvzdp%2ffzus3wovgjkgldwl9cnjh9wbjj0youhwpe7sjcyq%3d%3d &buyer_id=2088000000000000 &invoice_amount=0.01¬ify_id=2021112600222135008052701400000000 &fund_bill_list=%5B%7B%22amount%22%3A%220.01%22%2C%22fundChannel%22%3A%22ALIPAYACCOUNT%22%7D%5D¬ify_type=trade_status_sync &trade_status=TRADE_SUCCESS &receipt_amount=0.01 &app_id=2021002100000000 &buyer_pay_amount=0.01 &sign_type=RSA2 &seller_id=2088341000000000 &gmt_payment=2021-11-26+13%3A50%3A08¬ify_time=2021-11-26+13%3A50%3A08 &version=1.0 &out_trade_no=P219200688300000000 &total_amount=0.01 &trade_no=2021112622001452000000000000 &auth_app_id=2021002100000000 &buyer_logon_id=******%40***.com &point_amount=0.00 ``` #### 2.2.2 订单查询结果 成功支付后再次查询订单,响应如下: <!-- 订单 P219200688300000000 的查询结果: --> ```json { "msg": "Success", "pointAmount": "0.00", "code": "10000", "tradeNo": "2021112622001452000000000000", "invoiceAmount": "0.00", "body": "{\"alipay_trade_query_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"buyer_logon_id\":\"*********.com\",\"buyer_pay_amount\":\"0.00\",\"buyer_user_id\":\"2088000000000000\",\"invoice_amount\":\"0.00\",\"out_trade_no\":\"P219200688300000000\",\"point_amount\":\"0.00\",\"receipt_amount\":\"0.00\",\"send_pay_date\":\"2021-11-26 13:50:08\",\"total_amount\":\"0.01\",\"trade_no\":\"2021112622001452000000000000\",\"trade_status\":\"TRADE_SUCCESS\"},\"alipay_cert_sn\":\"78aa9258336cb49f0000000000000000\",\"sign\":\"ymlqcmmttjsjiuhslujxuzw2zvfi+5p47bqt7h9diclv7pgzc9qhyrnzdkthqfypnknxcum/guusmecubro8udky4bkgelkj+ep8m2iyjtx1+gujq7d2kr/owgc4amxs/vcybjp3/ymcv1/jaduyazyif+xhwif9nk4ayajixrgrdorevfkfcwtysxjasiqr4qaq/fyzgxaqdotpc4s6uczciaeafifd8hpizcejgr425wr/yrdr3kni0umehoatgxeeq1seyfk7c6d/83qcjoq7adxnmdjbgawhuv/8ph3gpbguf0/5lornb1ctd4odpjblrj6slmoehbp0dlizhg==\"}", "params": { "biz_content": "{\"out_trade_no\":\"P219200688300000000\"}" }, "buyerLogonId": "*********.com", "receiptAmount": "0.00", "totalAmount": "0.01", "outTradeNo": "P219200688300000000", "tradeStatus": "TRADE_SUCCESS", "buyerPayAmount": "0.00", "buyerUserId": "2088000000000000", "sendPayDate": 1637905808000 } ``` 需要吐个槽的是 *tradeStatus* 字段,有一个 *TRADE_CLOSED* 的状态,即表示 未付款交易超时关闭,又表示 支付完成后全额退款,明明是两个状态,为啥要合成一个? ## 3. 退款 退款代码参考[统一收单交易退款接口][5]上面的示例,同查询订单一样,如果使用证书方式,应改为调用 *certificateExecute* 方法。 ```java AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2"); JSONObject bizContent = new JSONObject(); bizContent.put("trade_no", "2021081722001419121412730660"); bizContent.put("refund_amount", 0.01); bizContent.put("out_request_no", "HZ01RF001"); //// 返回参数选项,按需传入 //JSONArray queryOptions = new JSONArray(); //queryOptions.add("refund_detail_item_list"); //bizContent.put("query_options", queryOptions); AlipayTradeRefundRequest request = new AlipayTradeRefundRequest(); request.setBizContent(bizContent.toString()); AlipayTradeRefundResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println("调用成功"); } else { System.out.println("调用失败"); } ``` <!-- 支付宝订单 P219200688300000000 退款结果: --> 退款接口响应如下: ```json { "gmtRefundPay": 1637905816000, "msg": "Success", "refundFee": "0.01", "fundChange": "Y", "code": "10000", "tradeNo": "2021112622001452000000000000", "body": "{\"alipay_trade_refund_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"buyer_logon_id\":\"*********.com\",\"buyer_user_id\":\"2088000000000000\",\"fund_change\":\"Y\",\"gmt_refund_pay\":\"2021-11-26 13:50:16\",\"out_trade_no\":\"P219200688300000000\",\"refund_fee\":\"0.01\",\"send_back_fee\":\"0.00\",\"trade_no\":\"2021112622001452000000000000\"},\"alipay_cert_sn\":\"78aa9258336cb49f0000000000000000\",\"sign\":\"fhpud+sccff9iltxgjz6bp8dpojauvrbpe4q2xlhpz83+3ilhrwyhhgqldu7gmx6fd7ugh9rbdguxphqrcgtjbq/hf2eqzlx+cbhtko+o4wpddaraa636fbudizerrg7+ht8uqcapx/+jr3byu7zksgdapjl7fhpntndvfkbn7thbvjbolp/nkcgo+eo8ebr/1tfff2k1smoc5otgtzqmc4wvbrom78bczrgxdbkkniq0tcvo5sr7yyxosxptfhzmw7uzjdwqzusc8rspvlx9wmdmhlc1vng1pm7mce27hdv7zwjta8afkd32cs7haftajkyk8ocov1q2womzgxpqa==\"}", "params": { "biz_content": "{\"trade_no\":\"2021112622001452000000000000\",\"refund_amount\":0.01,\"out_request_no\":\"FNR219200810000000000\"}" }, "buyerLogonId": "*********.com", "sendBackFee": "0.00", "outTradeNo": "P219200688300000000", "buyerUserId": "2088000000000000" } ``` ### 3.1 退款异步通知 由于退款同步结果中内容可能不太准确,这边仍然使用异步通知的方式同步退款状态。退款通知的验签同支付成功时的异步通知一样。 支付宝退款接口没有 *notifyUrl* 参数,其异步通知仍然使用的是创建订单时 *notifyUrl* 参数指定的异步通知地址。 请求Body如下(为方便查看,添加了换行,实际的请求体中没有换行): ```json gmt_create=2021-11-26+13%3A50%3A07 &charset=UTF-8 &gmt_payment=2021-11-26+13%3A50%3A08 &seller_email=ywu1112%40***.com¬ify_time=2021-11-26+13%3A50%3A16 &subject=%E5%85%850.01%E5%BE%9720000%E4%B9%A6%E5%B8%81 &gmt_refund=2021-11-26+13%3A50%3A15.858 &sign=vavtzne9tk8z7mswrzuljp4b3tvaluypc7srccwiyke0stvpmkg4lmefqzvswgfojgaxzpvtl5yvw%2fyomndl%2btt%2bujhjs%2fiz%2ff4zkbhtaw1skx5gqahcwgmjtmpo7irvvb8ioqx2dsdgjsvcfms3pfnvpuwcqmuo7%2b0toafzi6m%2bu%2bugr%2bclkhq%2frglslejaskdin%2fyapuyasgqyrnl2xf0%2f4ybehobgajdongoux2ckvs6advzvrgi16k2%2fjs4u8o984zvbhknwzynv6ahqzr9fm9nletprfuct%2bhiv61ymwd36fobh%2bhvl6c9a%2bjk0facatd6bvy9u9tihebglig%3d%3d &buyer_id=2088000000000000 &out_biz_no=FNR219200810000000000 &version=1.0¬ify_id=2021112600222135016052000000000000¬ify_type=trade_status_sync &out_trade_no=P219200688300000000 &total_amount=0.01 &trade_status=TRADE_CLOSED &refund_fee=0.01 &trade_no=2021112622001452000000000000 &auth_app_id=2021002100000000 &gmt_close=2021-11-26+13%3A50%3A15 &buyer_logon_id=******%40***.com &app_id=2021002100000000 &sign_type=RSA2 &seller_id=2088341000000000 ``` ### 3.2 退款查询结果 退款查询代码参照[统一收单交易退款查询][6]。 ```java AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do","app_id","your private_key","json","GBK","alipay_public_key","RSA2"); JSONObject bizContent = new JSONObject(); bizContent.put("trade_no", "2021081722001419121412730660"); bizContent.put("out_request_no", "HZ01RF001"); //// 返回参数选项,按需传入 //JSONArray queryOptions = new JSONArray(); //queryOptions.add("refund_detail_item_list"); //bizContent.put("query_options", queryOptions); AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest(); request.setBizContent(bizContent.toString()); AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request); if(response.isSuccess()){ System.out.println("调用成功"); } else { System.out.println("调用失败"); } ``` <!-- 支付宝订单 P219200688300000000 退款查询结果: --> 退款查询响应如下: ```json { "gmtRefundPay": 1637905816000, "outRequestNo": "FNR219200810000000000", "msg": "Success", "code": "10000", "tradeNo": "2021112622001452000000000000", "refundStatus": "REFUND_SUCCESS", "body": "{\"alipay_trade_fastpay_refund_query_response\":{\"code\":\"10000\",\"msg\":\"Success\",\"gmt_refund_pay\":\"2021-11-26 13:50:16\",\"out_request_no\":\"FNR219200810000000000\",\"out_trade_no\":\"P219200688300000000\",\"refund_amount\":\"0.01\",\"refund_detail_item_list\":[{\"amount\":\"0.01\",\"fund_channel\":\"ALIPAYACCOUNT\"}],\"refund_status\":\"REFUND_SUCCESS\",\"send_back_fee\":\"0.01\",\"total_amount\":\"0.01\",\"trade_no\":\"2021112622001452000000000000\"},\"alipay_cert_sn\":\"78aa9258336cb49f0000000000000000\",\"sign\":\"gr0izd7wohfjqi7qhiifgdqeqxvfmun7hze64/kh5yhlc/kwov7zy42nfjf0+1je0cuu9pwp1pry5vlhii04qronuo77j9i1/4aailfan9gxyckgy8j8x4bd9iweqoohruf7up/mobvowpcgullt3sudciapyaxcdj7chbnwwd7dmbivq2t57rvvers62p+cvwdrccoepge5q/ljdbx8e12h5uouijmo8r8ch5k49flxwax/rf3hjy507b4ojuxozcuij+ay2qd2+4ng8iua0hm91iv8dfeyett8/xpfnw2dnpcwivfaqypnxrd63m0gg+u8rtqzq+s77ntgxqwtsq==\"}", "params": { "biz_content": "{\"query_options\":[\"gmt_refund_pay\",\"refund_detail_item_list\"],\"trade_no\":\"2021112622001452000000000000\",\"out_request_no\":\"FNR219200810000000000\"}" }, "sendBackFee": "0.01", "refundDetailItemList": [ { "amount": "0.01", "fundChannel": "ALIPAYACCOUNT" } ], "totalAmount": "0.01", "outTradeNo": "P219200688300000000", "refundAmount": "0.01" } ``` [1]:https://opendocs.alipay.com/open/54/103419 (服务端 SDK(通用版)) [2]:https://opendocs.alipay.com/apis/api_1/alipay.trade.query?scene=23 (alipay.trade.query(统一收单线下交易查询)) [3]:https://opendocs.alipay.com/open/204/01dcc0 (服务端接入流程) [4]:https://opendocs.alipay.com/open/54/106370 (App 支付服务端 DEMO&SDK) [5]:https://opendocs.alipay.com/apis/028pxn (alipay.trade.refund(统一收单交易退款接口)) [6]:https://opendocs.alipay.com/apis/028xqc (统一收单交易退款查询) Loading... 版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://www.liujiajia.me/2021/11/26/alipay-memo ← 上一篇 下一篇 → 提交