1. 接口规则
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付APIv3接口。该版本API的具体规则请参考APIv3接口规则。
2. 开发准备
2.1. 搭建和配置开发环境
开发者应当依据自身的编程语言来构建并配置相应的开发环境。
3. 快速接入
3.1. 业务流程图
步骤1 商户创建一个支付分计划,该计划包含了一组为用户提供的服务,方便商家让用户一次性对整个计划的所有扣费内容完成确认
调用接口时请确保传入的服务ID有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻
支付分计划有效期(单位天)和支付分计划扣费次数确保小于等于服务规定的最大支付分计划有效期(单位天)和支付分计划扣费次数
支付分计划明细列表中计划明细数量确保等于支付分计划扣费次数
支付分系统会根据支付分计划明细列表中的计划明细顺序给计划明细按顺序从1开始编号,每个计划明细请确保计划明细原支付金额(单位分)大于等于计划明细实际支付金额(单位分),且每个计划明细的计划明细实际支付金额(单位分)需要小于等于按顺序的上一个计划明细
步骤2 用户在商户侧下单购买计划时,商户发起支付分计划用户签约,此时首先我们需要创建一个支付分用户的签约计划
在创建支付分用户的签约计划前,请确保对应的支付分计划已经创建且没有被终止,可以通过查询支付分计划接口查询并确认计划状态
调用接口时请确保传入的服务ID有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻,请保障调用此接口的服务和对应待签约的支付分计划创建时的服务一致
请保障支付分计划ID对应计划状态为NORMAL
请保障传入的OpenID对应的用户是待签约的用户
notify_url字段传入的URL用于接收后续签约成功的通知和该签约计划用户主动取消的通知
sign_plan_detail字段根据创建支付分计划时传入的计划详情数量和顺序传入对应计划详情后续使用时(既创建对应支付分服务订单时)的商户侧单号,请保证唯一
创建用户的签约计划成功后会返回package字段用于作为拉起支付分签约页的参数
步骤3 拉起支付分签约页,让用户进行计划签约 我们通过以下前端方法可调起微信客户端(注意区分场景),用户在调起页面中完成支付分计划的签约
发起支付分计划用户签约后,我们需要确认用户签约是否成功,共有两种方法:
用户签约计划后,如果主动取消已签约的计划,会根据创建用户签约计划中的notify_url参数发起取消签约的回调——取消签约回调通知API
调用接口时请确保传入的服务ID有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻,请保障调用此接口的服务和对应创建用户的签约计划时的服务一致
调用接口时请确保传入的签约计划ID所对应计划的状态有效,可以通过查询用户的签约计划接口查询指定用户的签约计划
创建已签约计划待使用计划详情对应的服务订单时,请确保待创建订单的计划详情前置计划详情已经取消或者已完结且用户支付成功,可以通过查询用户的签约计划接口查询指定用户的签约计划详情信息
创建已签约计划待使用计划详情对应的服务订单时,请确保待创建订单传入的商户侧单号out_trade_no和创建用户的签约计划时所传的计划详情对应的商户侧单号一致
步骤5 商户为用户提供服务,待服务结束后,商户调用完结订单接口完结当前订单 调用完结支付分订单API接口后,微信支付分就会发起对用户的扣款,但是在用户扣款过程中可能会出现一些特殊情况,下面列举了几种特殊情况以及对应的处理方法,供大家参考:
扣款过程中,用户通过其它方式完成了支付,希望微信支付分停止继续扣款
处理方法:商户调用同步服务订单信息接口将订单支付成功状态同步给微信支付分,微信支付分将停止继续扣款的操作
用户中途取消服务,不继续进行
处理方法:商户调用取消支付分服务订单接口,订单取消后对应计划详情也会对应取消
如遇到网络、服务器等原因造成无法正常接收扣款成功通知,一般有两种解决方法:
主动查单,通过查询支付分订单API 接口主动查询扣款情况
次日对账,通过申请交易账单API接口申请交易账单,再通过后台接口下载账单API下载交易账单
3.2. API接入(含示例代码)
本文档展示了如何使用微信支付服务端 SDK 快速接入微信支付分产品,完成与微信支付对接的部分。
3.2.1. 【服务端】创建支付分计划
步骤说明: 商户创建一个支付分计划,该计划包含了一组为用户提供的服务,方便商家让用户在一次性对整个计划的所有扣费内容完成确认。
1public void CreatePayScorePlan() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/plan/payscore-plans");
4
5 String reqdata = "{"
6 + "\"service_id\":\"500001\","
7 + "\"appid\":\"wxd678efh567hg6787\","
8 + "\"plan_name\":\"羽毛球课3节\","
9 + "\"plan_duration\":300,"
10 + "\"deduction_quantity\":3,"
11 + "\"total_original_price\":54000,"
12 + "\"total_actual_price\":30000,"
13 + "\"plan_detail_list\":[{"
14 + "\"original_price\":18000,"
15 + "\"actual_price\":18000,"
16 + "\"plan_detail_name\":\"第一节课\"},"
17 + "{"
18 + "\"original_price\":18000,"
19 + "\"plan_discount_description\":\"优惠60元\","
20 + "\"actual_price\":12000,"
21 + "\"plan_detail_name\":\"第二节课\"},"
22 + "{"
23 + "\"original_price\":18000,"
24 + "\"plan_discount_description\":\"送一节课\","
25 + "\"actual_price\":0,"
26 + "\"plan_detail_name\":\"第三节课\"}"
27 + "],"
28 + "\"merchant_plan_no\":\"1217752501201407033233388881\","
29 + "}";
30 StringEntity entity = new StringEntity(reqdata,"utf-8");
31 entity.setContentType("application/json");
32 httpPost.setEntity(entity);
33 httpPost.setHeader("Accept", "application/json");
34
35 CloseableHttpResponse response = httpClient.execute(httpPost);
36 try {
37 int statusCode = response.getStatusLine().getStatusCode();
38 if (statusCode == 200) {
39 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
40 } else if (statusCode == 204) {
41 System.out.println("success");
42 } else {
43 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
44 throw new IOException("request failed");
45 }
46 } finally {
47 response.close();
48 }
49}
重要入参说明:
调用接口时请确保传入的服务ID{service_id}有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻
支付分计划有效期(单位天){plan_duration}和支付分计划扣费次数{deduction_quantity}确保小于等于服务规定的最大支付分计划有效期(单位天)和支付分计划扣费次数
支付分计划明细列表{plan_detail_list}中计划明细数量确保等于支付分计划扣费次数{deduction_quantity}
支付分系统会根据支付分计划明细列表中的计划明细顺序给计划明细按顺序从1开始编号,每个计划明细请确保计划明细原支付金额(单位分){original_price}大于等于计划明细实际支付金额(单位分){actual_price},且每个计划明细的计划明细实际支付金额(单位分){actual_price}需要小于等于按顺序的上一个计划明细
3.2.2. 【服务端】查询支付分计划
步骤说明: 可以通过查询支付分计划接口确认支付分计划状态和具体信息。
1public void GetPayScorePlan() throws Exception{
2
3 URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/payscore/plan/payscore-plans/merchant-plan-no/1217752501201407033233388881");
4
5
6 HttpGet httpGet = new HttpGet(uriBuilder.build());
7 httpGet.setHeader("Accept", "application/json");
8
9
10 CloseableHttpResponse response = httpClient.execute(httpGet);
11
12 try {
13 int statusCode = response.getStatusLine().getStatusCode();
14 if (statusCode == 200) {
15 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
16 } else if (statusCode == 204) {
17 System.out.println("success");
18 } else {
19 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
20 throw new IOException("request failed");
21 }
22 } finally {
23 response.close();
24 }
25}
重要入参说明:
3.2.3. 【服务端】停止支付分计划
步骤说明: 商户不希望计划继续签约时,可以调用停止支付分计划接口,计划停止后无法继续签约,已签约的计划可以继续完成服务,既创建计划详情对应的支付分服务订单。
1public void StopPayScorePlan() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/plan/payscore-plans/merchant-plan-no/1217752501201407033233388881/stop");
4
5 String reqdata = "";
6 StringEntity entity = new StringEntity(reqdata,"utf-8");
7 entity.setContentType("application/json");
8 httpPost.setEntity(entity);
9 httpPost.setHeader("Accept", "application/json");
10
11 CloseableHttpResponse response = httpClient.execute(httpPost);
12 try {
13 int statusCode = response.getStatusLine().getStatusCode();
14 if (statusCode == 200) {
15 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
16 } else if (statusCode == 204) {
17 System.out.println("success");
18 } else {
19 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
20 throw new IOException("request failed");
21 }
22 } finally {
23 response.close();
24 }
25}
重要入参说明:
条件参数merchant_plan_no是在接口URL中传递的,请勿传到body中去。
3.2.4. 【服务端】创建用户的签约计划
步骤说明: 用户在商户侧下单购买计划时,商户发起支付分计划用户签约,此时首先我们需要创建一个支付分用户的签约计划以提供给用户进行签约。
1public void CreatePayScoreUserSignPlan() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/sign-plan/user-sign-plans");
4
5 String reqdata = "{"
6 + "\"service_id\":\"500001\","
7 + "\"plan_id\":\"01000033210033539462000000008179\","
8 + "\"appid\":\"wxd678efh567hg6787\","
9 + "\"openid\":\"XCU234_afgew355\","
10 + "\"merchant_sign_plan_no\":\"1696995693438\","
11 + "\"notify_url\":\"https://www.weixin.qq.com/wxpay/pay.php\","
12 + "\"sign_plan_detail\":[{"
13 + "\"plan_detail_no\":1,"
14 + "\"merchant_plan_detail_no\":\"1696995697789\"},"
15 + "{"
16 + "\"plan_detail_no\":2,"
17 + "\"merchant_plan_detail_no\":\"1696995697790\"},"
18 + "{"
19 + "\"plan_detail_no\":3,"
20 + "\"merchant_plan_detail_no\":\"1696995697791\"}"
21 + "]"
22 + "}";
23 StringEntity entity = new StringEntity(reqdata,"utf-8");
24 entity.setContentType("application/json");
25 httpPost.setEntity(entity);
26 httpPost.setHeader("Accept", "application/json");
27
28
29 CloseableHttpResponse response = httpClient.execute(httpPost);
30
31 try {
32 int statusCode = response.getStatusLine().getStatusCode();
33 if (statusCode == 200) {
34 System.out.println("success,return body = " + EntityUtils.toString (response.getEntity()));
35 } else if (statusCode == 204) {
36 System.out.println("success");
37 } else {
38 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
39 throw new IOException("request failed");
40 }
41 } finally {
42 response.close();
43 }
44}
重要入参说明:
调用接口时请确保传入的服务ID{service_id}有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻,请保障调用此接口的服务和对应待签约的支付分计划创建时的服务一致
请保障传入的OpenID对应的用户是待签约的用户
sign_plan_detail字段根据创建支付分计划时传入的计划详情数量和顺序传入对应计划详情后续使用时(既创建对应支付分服务订单时)的商户侧单号{merchant_plan_detail_no},请保证唯一
3.2.5. 【客户端】跳转签约计划页
微信支付根据用户不同的使用场景(App、小程序、微信内H5)分别提供了对应跳转签约计划页的方法,请根据场景进行选择,详见:
3.2.6. 【服务端】签约计划成功通知
步骤说明: 当用户完成计划签约时,微信会把相关信息异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
| 注意 支付结果通知是以POST 方法访问商户设置的通知URL,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情 加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考 《微信支付API v3签名验证》。 支付通知HTTP应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx 商户成功接收到回调通知后应返回成功的HTTP应答码为200或204 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。
|
|
3.2.7. 【服务端】查询用户的签约计划
步骤说明: 可以通过查询用户的签约计划接口确认用户签约计划的信息。
1public void GetPayScoreUserSignPlan() throws Exception{
2
3 URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/payscore/sign-plan/user-sign-plans/merchant-sign-plan-no/1217752501201407033233388881");
4
5
6 HttpGet httpGet = new HttpGet(uriBuilder.build());
7 httpGet.setHeader("Accept", "application/json");
8
9
10 CloseableHttpResponse response = httpClient.execute(httpGet);
11
12 try {
13 int statusCode = response.getStatusLine().getStatusCode();
14 if (statusCode == 200) {
15 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
16 } else if (statusCode == 204) {
17 System.out.println("success");
18 } else {
19 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
20 throw new IOException("request failed");
21 }
22 } finally {
23 response.close();
24 }
25}
重要入参说明:
3.2.8. 【服务端】取消用户的签约计划
步骤说明: 商户想要终止用户签约计划后续计划详情的服务,可以调用取消用户的签约计划接口,签约计划被取消后,剩余未使用的计划详情将无法提供服务,既创建对应的服务订单,已经创建了服务订单的计划详情不受影响可以正常完结、取消。
1public void StopPayScoreUserSignPlan() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/sign-plan/user-sign-plans/merchant-sign-plan-no/1217752501201407033233388881/stop");
4
5 String reqdata = "{"
6 + "\"stop_reason\":\"用户协商终止\""
7 + "}";
8 StringEntity entity = new StringEntity(reqdata,"utf-8");
9 entity.setContentType("application/json");
10 httpPost.setEntity(entity);
11 httpPost.setHeader("Accept", "application/json");
12
13
14 CloseableHttpResponse response = httpClient.execute(httpPost);
15
16 try {
17 int statusCode = response.getStatusLine().getStatusCode();
18 if (statusCode == 200) {
19 System.out.println("success,return body = " + EntityUtils.toString (response.getEntity()));
20 } else if (statusCode == 204) {
21 System.out.println("success");
22 } else {
23 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
24 throw new IOException("request failed");
25 }
26 } finally {
27 response.close();
28 }
29}
重要入参说明:
条件参数merchant_sign_plan_no是在接口URL中传递的,请勿传到body中去。
3.2.9. 【服务端】创建用户的签约计划详情对应的服务订单
步骤说明: 在用户使用已签约计划内指定计划详情对应服务时,创建已签约计划待使用计划详情对应的服务订单。
1public void CreateServiceOrder() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/sign-plan/serviceorder");
4
5 String reqdata = "{"
6 + "\"service_id\":\"500001\","
7 + "\"sign_plan_id\":\"01020033210024539462000000008173\","
8 + "\"plan_detail_no\":2,"
9 + "\"appid\":\"wxd678efh567hg6787\","
10 + "\"openid\":\"XCU234_afgew355\","
11 + "\"notify_url\":\"https://www.weixin.qq.com/wxpay/pay.php\","
12 + "\"service_introduction\":\"羽毛球基础课程\","
13 + "\"time_range\": {"
14 + "\"start_time\":\"20091225091010\","
15 + "\"end_time\":\"20091225121010\""
16 + "},"
17 + "\"location\": {"
18 + "\"start_location\":\"xxx体育馆羽毛球场\","
19 + "\"end_location\":\"xxx体育馆羽毛球场\""
20 + "},"
21 + "\"out_order_no\":\"1234323JKHDFE1243252\","
22 + "}";
23 StringEntity entity = new StringEntity(reqdata,"utf-8");
24 entity.setContentType("application/json");
25 httpPost.setEntity(entity);
26 httpPost.setHeader("Accept", "application/json");
27
28
29 CloseableHttpResponse response = httpClient.execute(httpPost);
30
31 try {
32 int statusCode = response.getStatusLine().getStatusCode();
33 if (statusCode == 200) {
34 System.out.println("success,return body = " + EntityUtils.toString (response.getEntity()));
35 } else if (statusCode == 204) {
36 System.out.println("success");
37 } else {
38 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
39 throw new IOException("request failed");
40 }
41 } finally {
42 response.close();
43 }
44}
重要入参说明:
调用接口时请确保传入的服务ID{service_id}有效,对应服务已经开通了支付分按计划确认权限,商户已经完成服务入驻,请保障调用此接口的服务和对应创建用户的签约计划时的服务一致
创建已签约计划待使用计划详情对应的服务订单时,请确保待创建订单的计划详情前置计划详情已经取消或者完成使用且用户支付成功
创建已签约计划待使用计划详情对应的服务订单时,请确保待创建订单传入的商户侧单号out_trade_no和创建用户的签约计划时所传的计划详情对应的商户侧单号一致
3.2.10. 【服务端】完结支付分订单
步骤说明: 用户服务结束后,商户通过请求完结支付分订单接口,通过微信支付分进行用户扣款操作。
1public void CompleteServiceOrder() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/1234323JKHDFE1243252/complete");
4
5 String reqdata = "{"
6 + "\"appid\":\"wxd678efh567hg6787\","
7 + "\"service_id\":\"500001\","
8 + "\"post_payments\": ["
9 + "{"
10 + "\"name\":\"羽毛球第二节课\","
11 + "\"amount\":12000"
12 + "}"
13 + "],"
14 + "\"total_amount\":12000,"
15 + "\"time_range\": {"
16 + "\"start_time\":\"20091225091010\","
17 + "\"end_time\":\"20091225121010\""
18 + "},"
19 + "\"location\": {"
20 + "\"end_location\":\"xxx体育馆羽毛球场\""
21 + "},"
22 + "\"profit_sharing\":false"
23 + "}";
24 StringEntity entity = new StringEntity(reqdata,"utf-8");
25 entity.setContentType("application/json");
26 httpPost.setEntity(entity);
27 httpPost.setHeader("Accept", "application/json");
28
29
30 CloseableHttpResponse response = httpClient.execute(httpPost);
31
32 try {
33 int statusCode = response.getStatusLine().getStatusCode();
34 if (statusCode == 200) {
35 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
36 } else if (statusCode == 204) {
37 System.out.println("success");
38 } else {
39 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
40 throw new IOException("request failed");
41 }
42 } finally {
43 response.close();
44 }
45}
更多参数、响应详情及错误码请参见 完结支付分订单API接口文档。
3.2.11. 【服务端】取消支付分订单
步骤说明: 订单为以下状态时可以取消订单:CREATED(已创单)、DOING(进行中)(包括商户完结支付分订单后,且支付分订单收款状态为待支付USER_PAYING)。
1public void CancelServiceOrder() throws Exception{
2
3 HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/payscore/serviceorder/{out_order_no}/cancel");
4
5 String reqdata = "{"
6 + "\"appid\":\"wxd678efh567hg6787\","
7 + "\"service_id\":\"500001\","
8 + "\"reason\":\"用户投诉\""
9 + "}";
10 StringEntity entity = new StringEntity(reqdata,"utf-8");
11 entity.setContentType("application/json");
12 httpPost.setEntity(entity);
13 httpPost.setHeader("Accept", "application/json");
14
15
16 CloseableHttpResponse response = httpClient.execute(httpPost);
17
18 try {
19 int statusCode = response.getStatusLine().getStatusCode();
20 if (statusCode == 200) {
21 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
22 } else if (statusCode == 204) {
23 System.out.println("success");
24 } else {
25 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
26 throw new IOException("request failed");
27 }
28 } finally {
29 response.close();
30 }
31}
更多参数、响应详情及错误码请参见取消支付分订单API接口文档。
3.2.12. 【服务端】查询支付分订单
步骤说明: 一般在创建订单后、订单完结成功后等关键流程中,商户可能有知晓订单状态的需求,此时即可通过该接口查询订单状态。
1public void GetServiceOrder() throws Exception{
2
3 HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/payscore/serviceorder?service_id=500001&out_order_no=8416518464133&appid=wxd678efh567hg6787");
4 httpGet.setHeader("Accept", "application/json");
5
6
7 CloseableHttpResponse response = httpClient.execute(httpGet);
8
9 try {
10 int statusCode = response.getStatusLine().getStatusCode();
11 if (statusCode == 200) {
12 System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
13 } else if (statusCode == 204) {
14 System.out.println("success");
15 } else {
16 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
17 throw new IOException("request failed");
18 }
19 } finally {
20 response.close();
21 }
22}
更多参数、响应详情及错误码请参见 查询支付分订单API接口文档。
3.2.13. 【服务端】支付成功回调通知
步骤说明: 当用户完成支付,微信会把相关支付结果将通过异步回调的方式通知商户,商户需要接收处理,并按文档规范返回应答
| 注意 支付结果通知是以POST 方法访问商户设置的通知URL,通知的数据以JSON 格式通过请求主体(BODY)传输。通知的数据包括了加密的支付结果详情 加密不能保证通知请求来自微信。微信会对发送给商户的通知进行签名,并将签名值放在通知的HTTP头Wechatpay-Signature。商户应当验证签名,以确认请求来自微信,而不是其他的第三方。签名验证的算法请参考 《微信支付API v3签名验证》。 支付通知HTTP应答码为200或204才会当作正常接收,当回调处理异常时,应答的HTTP状态码应为500,或者4xx 商户成功接收到回调通知后应返回成功的HTTP应答码为200或204 同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当商户系统收到通知进行处理时,先检查对应业务数据的状态,并判断该通知是否已经处理。如果未处理,则再进行处理;如果已处理,则直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 如果在所有通知频率(4小时)后没有收到微信侧回调,商户应调用查询订单接口确认订单状态。
|
|
3.2.14. 【服务端】申请交易账单
步骤说明: 微信支付按天提供交易账单文件,商户可以通过该接口获取账单文件的下载地址。
1public void TradeBill() throws Exception {
2
3 URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/bill/tradebill");
4 uriBuilder.setParameter("bill_date", "2020-11-09");
5 uriBuilder.setParameter("bill_type", "ALL");
6
7
8 HttpGet httpGet = new HttpGet(uriBuilder.build());
9 httpGet.addHeader("Accept", "application/json");
10 CloseableHttpResponse response = httpClient.execute(httpGet);
11
12 try {
13 int statusCode = response.getStatusLine().getStatusCode();
14 if (statusCode == 200) {
15 System.out.println("success,return body = " + EntityUtils.toString (response.getEntity()));
16 } else if (statusCode == 204) {
17 System.out.println("success");
18 } else {
19 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
20 throw new IOException("request failed");
21 }
22 } finally {
23 response.close();
24 }
25}
更多参数、响应详情及错误码请参见 申请交易账单API接口文档。
3.2.15. 【服务端】下载交易账单
步骤说明: 通过申请交易账单接口获取到账单下载地址(download_url)后,再通过该接口获取到对应的账单文件,文件内包含交易相关的金额、时间、营销等信息,供商户核对订单、退款、银行到账等情况
1public void DownloadUrl(String download_url) throws Exception{
2 PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream (privateKey.getBytes("utf-8")));
3
4
5
6 httpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator((response) -> true).build();
7
8
9
10 URIBuilder uriBuilder = new URIBuilder(download_url);
11 HttpGet httpGet = new HttpGet(uriBuilder.build());
12 httpGet.addHeader("Accept", "application/json");
13
14
15 CloseableHttpResponse response = httpClient.execute(httpGet);
16 try {
17 int statusCode = response.getStatusLine().getStatusCode();
18 if (statusCode == 200) {
19 System.out.println("success,return body = " + EntityUtils.toString (response.getEntity()));
20 } else if (statusCode == 204) {
21 System.out.println("success");
22 } else {
23 System.out.println("failed,resp code = " + statusCode+ ",return body = " + EntityUtils.toString(response.getEntity()));
24 throw new IOException("request failed");
25 }
26 } finally {
27 response.close();
28 }
29}