业务示例代码

更新时间:2025.09.03

1、目标

通过本教程的学习,你应该可以:

  • 设置商户违规处置通知的回调地址

  • 处理商户平台处置记录回调通知

2、业务处理流程

2.1、管理回调地址

从业机构/服务商/渠道商需要先设置商户平台处置通知回调URL后,才可以接收对应的违规、拦截、申诉事件通知。

1package com.java.violation_notification;
2
3// 使用合作伙伴平台创建商户违规通知回调地址接口https://pay.weixin.qq.com/doc/v3/partner/4012471333
4import com.java.demo.CreateViolationNotification;
5import static com.java.demo.CreateViolationNotification.*;
6// 使用合作伙伴平台修改商户违规通知回调地址接口https://pay.weixin.qq.com/doc/v3/partner/4012471330
7import com.java.demo.UpdateViolationNotification;
8import static com.java.demo.UpdateViolationNotification.*;
9// 使用合作伙伴平台删除商户违规通知回调地址接口https://pay.weixin.qq.com/doc/v3/partner/4012471334
10import com.java.demo.DeleteViolationNotification;
11import static com.java.demo.DeleteViolationNotification.*;
12// 使用合作伙伴平台查询商户违规通知回调地址接口https://pay.weixin.qq.com/doc/v3/partner/4012471327
13import com.java.demo.GetViolationNotification;
14import static com.java.demo.GetViolationNotification.*;
15
16public class ViolationNotificationUrlDemo {
17    // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/partner/4013080340
18    // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考
19    // https://pay.weixin.qq.com/doc/v3/partner/4013080340
20    private static String mchid = "19xxxxxxxx";
21    // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013058924
22    private static String certificateSerialNo = "1DDE55AD98Exxxxxxxxxx";
23    // 商户API证书私钥文件路径,本地文件路径
24    private static String privateKeyFilePath = "/path/to/apiclient_key.pem";
25    // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
26    private static String wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx";
27    // 微信支付公钥文件路径,本地文件路径
28    private static String wechatPayPublicKeyFilePath = "/path/to/wxp_pub.pem";
29
30    public static void main(String[] args) {
31        String notifyUrl = "https://www.example.com/notify";
32        ViolationNotificationUrlDemo demo = new ViolationNotificationUrlDemo();
33
34        // 1. 创建违约通知URL
35        String createNotifyUrl = demo.createViolationNotificationUrl(notifyUrl);
36        System.out.println("createNotifyUrl: " + createNotifyUrl);
37
38        // 2. 更新违约通知URL
39        // String updateNotifyUrl = demo.updateViolationNotificationUrl(notifyUrl);
40        // System.out.println("updateNotifyUrl: " + updateNotifyUrl);
41
42        // 3. 删除违约通知URL
43        // demo.deleteViolationNotificationUrl();
44
45        // 4. 查询违约通知URL
46        String getNotifyUrl = demo.getViolationNotificationUrl();
47        System.out.println("getNotifyUrl: " + getNotifyUrl);
48    }
49
50    private String createViolationNotificationUrl(String notifyUrl) {
51        CreateViolationNotification client = new CreateViolationNotification(
52                mchid,
53                certificateSerialNo,
54                privateKeyFilePath,
55                wechatPayPublicKeyId,
56                wechatPayPublicKeyFilePath);
57
58        CreateViolationNotificationRequest request = new CreateViolationNotificationRequest();
59        request.notifyUrl = notifyUrl;
60        try {
61            CreateViolationNotificationResponse response = client.run(request);
62            System.out.println(response);
63            return response.notifyUrl;
64        } catch (WXPayUtility.ApiException e) {
65            // 创建违约通知URL处理异常处理逻辑
66            if (e.getErrorCode().equals("SYSTEM_ERROR")) {
67                // 错误:系统错误
68                // 解决方式:稍后重试
69            } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
70                // 错误:限频报错
71                // 解决方式:稍后重试
72            } else if (e.getErrorCode().equals("ALREADY_EXISTS")) {
73                // 错误:用户账户异常
74                // 解决方式:说明已经创建成功,可以通过查询接口确认是否符合预期
75            } else if (e.getErrorCode().equals("SIGN_ERROR")) {
76                // 错误:签名错误
77                // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
78                // 描述:签名报错,需要确认签名材料和签名流程是否正确
79            } else if (e.getErrorCode().equals("PARAM_ERROR")) {
80                // 错误:参数错误
81                // 解决方式:按照报错返回的message,重新输入请求参数
82                // 描述:参数的类型,长度,或者必填选项没有填写等
83            } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
84                // 错误:请求非法,请求参数正确,但是不符合违约通知URL业务规则
85                // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
86            } else {
87                // 其他类型错误:稍等一会后重试
88            }
89        }
90        return null; // 如果发生异常,返回null
91    }
92
93    private String updateViolationNotificationUrl(String notifyUrl) {
94        UpdateViolationNotification client = new UpdateViolationNotification(
95                mchid,
96                certificateSerialNo,
97                privateKeyFilePath,
98                wechatPayPublicKeyId,
99                wechatPayPublicKeyFilePath);
100
101        UpdateViolationNotificationRequest request = new UpdateViolationNotificationRequest();
102        request.notifyUrl = notifyUrl;
103        try {
104            UpdateViolationNotificationResponse response = client.run(request);
105            System.out.println(response);
106            return response.notifyUrl;
107        } catch (WXPayUtility.ApiException e) {
108            // 更新违约通知URL处理异常处理逻辑
109            if (e.getErrorCode().equals("SYSTEM_ERROR")) {
110                // 错误:系统错误
111                // 解决方式:稍后重试
112            } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
113                // 错误:限频报错
114                // 解决方式:稍后重试
115            } else if (e.getErrorCode().equals("NOT_FOUND")) {
116                // 错误:回调地址不存在
117                // 解决方式:请检查是否已创建通知回调URL
118            } else if (e.getErrorCode().equals("SIGN_ERROR")) {
119                // 错误:签名错误
120                // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
121                // 描述:签名报错,需要确认签名材料和签名流程是否正确
122            } else if (e.getErrorCode().equals("PARAM_ERROR")) {
123                // 错误:参数错误
124                // 解决方式:按照报错返回的message,重新输入请求参数
125                // 描述:参数的类型,长度,或者必填选项没有填写等
126            } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
127                // 错误:请求非法,请求参数正确,但是不符合违约通知URL业务规则
128                // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
129            } else {
130                // 其他类型错误:稍等一会后重试
131            }
132        }
133        return null; // 如果发生异常,返回null
134    }
135
136    private void deleteViolationNotificationUrl() {
137        DeleteViolationNotification client = new DeleteViolationNotification(
138                mchid,
139                certificateSerialNo,
140                privateKeyFilePath,
141                wechatPayPublicKeyId,
142                wechatPayPublicKeyFilePath);
143
144        try {
145            client.run();
146            System.out.println("删除成功");
147        } catch (WXPayUtility.ApiException e) {
148            // 删除违约通知URL处理异常处理逻辑
149            if (e.getErrorCode().equals("SYSTEM_ERROR")) {
150                // 错误:系统错误
151                // 解决方式:稍后重试
152            } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
153                // 错误:限频报错
154                // 解决方式:稍后重试
155            } else if (e.getErrorCode().equals("NOT_FOUND")) {
156                // 错误:回调地址不存在
157                // 解决方式:请检查是否已创建通知回调URL
158            } else if (e.getErrorCode().equals("SIGN_ERROR")) {
159                // 错误:签名错误
160                // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
161                // 描述:签名报错,需要确认签名材料和签名流程是否正确
162            } else if (e.getErrorCode().equals("PARAM_ERROR")) {
163                // 错误:参数错误
164                // 解决方式:按照报错返回的message,重新输入请求参数
165                // 描述:参数的类型,长度,或者必填选项没有填写等
166            } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
167                // 错误:请求非法,请求参数正确,但是不符合违约通知URL业务规则
168                // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
169            } else {
170                // 其他类型错误:稍等一会后重试
171            }
172        }
173    }
174
175    private String getViolationNotificationUrl() {
176        GetViolationNotification client = new GetViolationNotification(
177                mchid,
178                certificateSerialNo,
179                privateKeyFilePath,
180                wechatPayPublicKeyId,
181                wechatPayPublicKeyFilePath);
182
183        try {
184            GetViolationNotificationResponse response = client.run();
185            System.out.println(response);
186            return response.notifyUrl;
187        } catch (WXPayUtility.ApiException e) {
188            // 查询违约通知URL处理异常处理逻辑
189            if (e.getErrorCode().equals("SYSTEM_ERROR")) {
190                // 错误:系统错误
191                // 解决方式:稍后重试
192            } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) {
193                // 错误:限频报错
194                // 解决方式:稍后重试
195            } else if (e.getErrorCode().equals("NOT_FOUND")) {
196                // 错误:回调地址不存在
197                // 解决方式:请检查是否已创建通知回调URL
198            } else if (e.getErrorCode().equals("SIGN_ERROR")) {
199                // 错误:签名错误
200                // 解决方式:检查平台商户证书序列号,证书私钥文件,公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式
201                // 描述:签名报错,需要确认签名材料和签名流程是否正确
202            } else if (e.getErrorCode().equals("PARAM_ERROR")) {
203                // 错误:参数错误
204                // 解决方式:按照报错返回的message,重新输入请求参数
205                // 描述:参数的类型,长度,或者必填选项没有填写等
206            } else if (e.getErrorCode().equals("INVALID_REQUEST")) {
207                // 错误:请求非法,请求参数正确,但是不符合违约通知URL业务规则
208                // 解决方式:根据具体message查看具体哪里不符合业务规则,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再重试
209            } else {
210                // 其他类型错误:稍等一会后重试
211            }
212        }
213        return null; // 如果发生异常,返回null
214    }   
215}

2.2、处理商户平台处置记录回调通知

回调地址设置成功后,确保回调地址可以正常访问的情况下,子商户违规处理记录和交易拦截记录时,可以接收到对应的推送

1import okhttp3.Headers;
2
3// 商户APIv3密钥 https://pay.weixin.qq.com/doc/v3/partner/4013059095
4String apiv3Key = "xxxxxxx";
5// 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/partner/4013038589
6String wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx";
7// 微信支付公钥文件路径,本地文件路径
8String wechatPayPublicKeyFilePath = "/path/to/wxp_pub.pem";
9// 此处为代码示例,获取微信支付通知 Header 列表
10Headers headers = getHeaders();
11// 此处为代码示例,获取微信支付通知 Body
12String body = getBody();
13
14PublicKey wechatPayPublicKey WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath);
15
16Notification notification;
17try {
18  // 1. 解析微信支付回调包体,body为商户接收到微信回调的原始信息
19  notification  = WXPayUtility.parseNotification(apiv3Key, wechatPayPublicKeyId, wechatPayPublicKey, headers, body);
20  // 2. 解析完回调包体之后,可以先返回给微信支付200或者204,表示回调成功
21  //    商户再异步处理包体逻辑,这样子能保证微信回调不会超时,微信支付超时多次之后不会再回调
22  send2MQ(notification);
23} catch (Exception e) {
24  // 处理回调异常
25}
26
27