业务示例代码
更新时间:2025.09.111. 目标
通过本教程的学习,你应该可以:
查询投诉单列表
查询某个投诉单的具体内容及协商历史(包括图片)
创建/更新/删除和查看投诉地址,并接入投诉通知回调接口,实时获取投诉信息
回复用户的投诉(包括图片),反馈处理完成,更新退款审批结果
本文档Demo中使用的WXPayUtility工具类可通过Java文档获得。
2. 前置流程
当有新的投诉事件发生、投诉状态发生变化时服务会主动推送回调到回调地址,因此需要提前创建回调地址,详情见 创建投诉通知回调地址
2.1 创建投诉回调通知地址
该接口的参数、响应详情及错误码请参见 创建投诉通知回调地址 接口文档。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012458679 4import com.java.demo.CreateComplaintNotifications; 5import com.java.demo.CreateComplaintNotifications.CreateComplaintNotificationsRequest; 6import com.java.demo.CreateComplaintNotifications.CreateComplaintNotificationsResponse; 7import com.java.utils.WXPayUtility; 8 9public class CreateComplaintNotificationsExample { 10 public static void main(String[] args) { 11 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 12 CreateComplaintNotifications client = new CreateComplaintNotifications( 13 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 14 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 15 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 16 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 17 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 18 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 19 ); 20 21 CreateComplaintNotificationsRequest request = new CreateComplaintNotificationsRequest(); 22 // 通知地址,仅支持HTTPS 23 request.url = "https://www.xxx.com/notify"; 24 try { 25 CreateComplaintNotificationsResponse response = client.run(request); 26 27 // 请求成功,可查看该商户是否注册成功,及成功注册的url 28 String mchid = response.mchid; 29 String url = response.url; 30 } catch (WXPayUtility.ApiException e) { 31 // 请求失败,根据状态码执行不同的逻辑 32 if (e.getErrorCode().equals("PARAM_ERROR")) { 33 // 错误:参数错误 34 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 35 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 36 // 错误:请求非法,请求参数正确,但是不符合业务规则 37 // 解决方式:请参阅 38 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 39 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 40 // 错误:签名错误 41 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 42 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 43 // 错误:系统错误 44 // 解决方式:稍后原单重试 45 } else if (e.getErrorCode().equals("NO_AUTH")) { 46 // 错误:商户信息不合法 47 // 解决方式:登录商户平台核对,传入正确信息 48 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 49 // 错误:频率超限 50 // 解决方式:请求量不要超过接口调用频率限制 51 } else { 52 // 其他错误,稍等一会重试 53 } 54 } 55 } 56} 57
2.2 查询投诉回调通知地址
该接口的参数、响应详情及错误码请参见 查询投诉通知回调地址 接口文档。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012459014 4import com.java.demo.QueryComplaintNotifications; 5import com.java.demo.QueryComplaintNotifications.QueryComplaintNotificationsResponse; 6import com.java.utils.WXPayUtility; 7 8public class QueryComplaintNotificationsExample { 9 10 public static void main(String[] args) { 11 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 12 QueryComplaintNotifications client = new QueryComplaintNotifications( 13 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 14 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 15 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 16 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 17 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 18 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 19 ); 20 21 try { 22 QueryComplaintNotificationsResponse response = client.run(); 23 24 // 请求成功,继续业务逻辑 25 } catch (WXPayUtility.ApiException e) { 26 // 请求失败,根据状态码执行不同的逻辑 27 if (e.getErrorCode().equals("PARAM_ERROR")) { 28 // 错误:参数错误 29 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 30 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 31 // 错误:请求非法,请求参数正确,但是不符合业务规则 32 // 解决方式:请参阅 33 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 34 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 35 // 错误:签名错误 36 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 37 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 38 // 错误:系统错误 39 // 解决方式:稍后原单重试 40 } else if (e.getErrorCode().equals("NO_AUTH")) { 41 // 错误:商户信息不合法 42 // 解决方式:登录商户平台核对,传入正确信息 43 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 44 // 错误:频率超限 45 // 解决方式:请求量不要超过接口调用频率限制 46 } else if (e.getErrorCode().equals("RESOURCE_NOT_EXISTS")) { 47 // 错误:查询的通知回调不存在 48 // 解决方式:请检查是否已创建通知回调 49 } else { 50 // 其他错误,稍等一会重试 51 } 52 } 53 } 54} 55
2.3 更新投诉通知回调地址
该接口的参数、响应详情及错误码请参见 更新投诉通知回调地址 接口文档。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012459282 4import com.java.demo.UpdateComplaintNotifications; 5import com.java.demo.UpdateComplaintNotifications.UpdateComplaintNotificationsRequest; 6import com.java.demo.UpdateComplaintNotifications.UpdateComplaintNotificationsResponse; 7import com.java.utils.WXPayUtility; 8 9public class UpdateComplaintNotificationsExample { 10 11 public static void main(String[] args) { 12 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 13 UpdateComplaintNotifications client = new UpdateComplaintNotifications( 14 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 15 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 16 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 17 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 18 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 19 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 20 ); 21 22 UpdateComplaintNotificationsRequest request = new UpdateComplaintNotificationsRequest(); 23 request.url = "https://www.xxx.com/notify"; 24 try { 25 UpdateComplaintNotificationsResponse response = client.run(request); 26 27 // 请求成功,继续业务逻辑 28 } catch (WXPayUtility.ApiException e) { 29 // 请求失败,根据状态码执行不同的逻辑 30 if (e.getErrorCode().equals("PARAM_ERROR")) { 31 // 错误:参数错误 32 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 33 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 34 // 错误:请求非法,请求参数正确,但是不符合业务规则 35 // 解决方式:请参阅 36 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 37 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 38 // 错误:签名错误 39 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 40 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 41 // 错误:系统错误 42 // 解决方式:稍后原单重试 43 } else if (e.getErrorCode().equals("NO_AUTH")) { 44 // 错误:商户信息不合法 45 // 解决方式:登录商户平台核对,传入正确信息 46 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 47 // 错误:频率超限 48 // 解决方式:请求量不要超过接口调用频率限制 49 } else if (e.getErrorCode().equals("RESOURCE_NOT_EXISTS")) { 50 // 错误:查询的通知回调不存在 51 // 解决方式:请检查是否已创建通知回调 52 } else { 53 // 其他错误,稍等一会重试 54 } 55 } 56 } 57} 58
2.4 删除投诉通知回调地址
该接口的参数、响应详情及错误码请参见 删除投诉通知回调地址 接口文档。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012460452 4import com.java.demo.DeleteComplaintNotifications; 5import com.java.utils.WXPayUtility; 6 7public class DeleteComplaintNotificationsExample { 8 9 public static void main(String[] args) { 10 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 11 DeleteComplaintNotifications client = new DeleteComplaintNotifications( 12 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 13 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 14 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 15 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 16 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 17 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 18 ); 19 20 try { 21 client.run(); 22 23 // 请求成功,继续业务逻辑 24 } catch (WXPayUtility.ApiException e) { 25 // 请求失败,根据状态码执行不同的逻辑 26 if (e.getErrorCode().equals("PARAM_ERROR")) { 27 // 错误:参数错误 28 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 29 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 30 // 错误:请求非法,请求参数正确,但是不符合业务规则 31 // 解决方式:请参阅 32 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 33 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 34 // 错误:签名错误 35 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 36 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 37 // 错误:系统错误 38 // 解决方式:稍后原单重试 39 } else if (e.getErrorCode().equals("NO_AUTH")) { 40 // 错误:商户信息不合法 41 // 解决方式:登录商户平台核对,传入正确信息 42 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 43 // 错误:频率超限 44 // 解决方式:请求量不要超过接口调用频率限制 45 } else if (e.getErrorCode().equals("RESOURCE_NOT_EXISTS")) { 46 // 错误:查询的通知回调不存在 47 // 解决方式:请检查是否已创建通知回调 48 } else { 49 // 其他错误,稍等一会重试 50 } 51 } 52 } 53} 54
3. 业务处理流程-处理投诉消息
在收到投诉通知回调后,可以根据回调报文的内容查看投诉单相关的信息,然后处理投诉:
咨询单初始状态为“待处理”,商家需及时回复用户,与用户充分沟通。
回复用户后咨询单状态转为“处理中”,只有处于“处理中“的状态才可点击处理完成。
商家确认投诉已妥善处理后可执行结单操作。用户咨询分为「退款申请」和「普通咨询」,这两类咨询对应的结单方式有所不同:
「普通咨询」时:与用户充分沟通后可选择是否对用户发起退款,确认解决问题后需操作“处理完成”来结单。
「退款申请」时:判断用户需求合理时,可发起“同意退款”来结单;充分沟通后判断用户需求不合理时可发起“驳回退款”来结单。
在商户待处理和处理中的状态下,用户也可以发起升级投诉。在商户处理完成状态下,在商家全额退款到账后不可发起升级投诉,否则仍可以发起升级投诉。
「普通咨询」的投诉单状态机:
「退款申请」的投诉单状态机:
3.1 商户投诉单获取
商户投诉单获取分为被动获取和主动获取:
被动获取:新的投诉事件发生、投诉状态发生变化时投诉通知回调主动通知商户投诉单号和动作类型,投诉单详情需按单号反查。
3.1.1 投诉通知回调
当用户产生新投诉且投诉状态已变更时,微信支付会通过投诉回调通知接口发送通知至商户创建的回调通知URL。服务商、渠道商,会收到所有子商户的投诉信息推送。
收到通知回调后,需要依次经过验签,解密的步骤,详情可参考 投诉通知回调 ,并进行应答:
验签通过:商户需告知微信支付接收回调成功,HTTP应答状态码需返回200或204,无需返回应答报文。
验签不通过:商户需告知微信支付接收回调失败,HTTP应答状态码需返回5XX或4XX,同时需返回应答报文。
为了保证业务信息的安全性,微信支付将业务信息进行了AES-256-GCM加密,并通过参数resource将加密信息回调给商户,商户需要进行解密后才能获取到业务信息。
1package com.java.complaint; 2 3import java.security.PublicKey; 4 5import com.google.gson.annotations.SerializedName; 6import com.java.utils.WXPayUtility; 7import com.java.utils.WXPayUtility.Notification; 8 9import okhttp3.Headers; 10 11public class Callback { 12 13 public static class CallbackData { 14 @SerializedName("complaint_id") 15 public String complaint_id; 16 17 @SerializedName("action_type") 18 public String action_type; 19 } 20 21 public static class CallbackResponse { 22 @SerializedName("http_code") 23 public int http_code; 24 25 @SerializedName("code") 26 public String code; 27 28 @SerializedName("message") 29 public String message; 30 } 31 32 public Callback() { 33 this.wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx"; // 微信支付公钥ID,如何获取请参考 34 // https://pay.weixin.qq.com/doc/v3/merchant/4013038816 35 String wechatPayPublicKeyPath = "/path/to/wxp_pub.pem"; // 微信支付公钥文件路径,本地文件路径 36 this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyPath); 37 38 this.apiv3AesKey = "CF3xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // APIv3密钥,如何获取请参考 39 // https://pay.weixin.qq.com/doc/v3/merchant/4013053267 40 } 41 42 public CallbackResponse callback(Headers headers, String reqBody) { 43 try { 44 // 解密 45 Notification notification = WXPayUtility.parseNotification(apiv3AesKey, wechatPayPublicKeyId, 46 wechatPayPublicKey, headers, reqBody); 47 String data = notification.getPlaintext(); 48 CallbackData callbackData = WXPayUtility.fromJson(data, CallbackData.class); 49 50 // 获取其中的字段并处理。 51 String complaint_id = callbackData.complaint_id; 52 String action_type = callbackData.action_type; 53 54 // 成功,响应:200,无响应体 55 CallbackResponse response = new CallbackResponse(); 56 response.http_code = 200; 57 return response; 58 } catch (Exception e) { 59 e.printStackTrace(); 60 // 失败,响应:5xx或4xx,响应: 61 // { 62 // "code": "FAIL", 63 // "message": "失败" 64 // } 65 CallbackResponse response = new CallbackResponse(); 66 response.http_code = 500; 67 response.code = "FAIL"; 68 response.message = "失败"; 69 return response; 70 } 71 } 72 73 private String wechatPayPublicKeyId; 74 private PublicKey wechatPayPublicKey; 75 private String apiv3AesKey; 76}
3.1.2 查询投诉单列表
商户可通过调用此接口,查询指定时间段的所有用户投诉信息,以分页输出查询结果,该接口的参数、响应详情及错误码请参见 查询投诉单列表 接口文档。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012533431 4import com.java.demo.ListComplaintsV2; 5import com.java.demo.ListComplaintsV2.ListComplaintsV2Request; 6import com.java.demo.ListComplaintsV2.ListComplaintsV2Response; 7import com.java.utils.WXPayUtility; 8 9public class QueryComplaintListExample { 10 public static void main(String[] args) { 11 12 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 13 ListComplaintsV2 client = new ListComplaintsV2( 14 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 15 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 16 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 17 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 18 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 19 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 20 ); 21 22 ListComplaintsV2Request request = new ListComplaintsV2Request(); 23 24 // 需要查询的被诉商户号,起止日期,分页大小和分页开始位置 25 // 注意:起止日期之间的范围不能超过30天,仅能查询该商户下的投诉单 26 // 分页大小最大为50 27 request.limit = 10L; 28 request.offset = 0L; 29 request.beginDate = "2025-07-01"; 30 request.endDate = "2025-07-01"; 31 request.complaintedMchid = "163xxxxxxx"; 32 try { 33 ListComplaintsV2Response response = client.run(request); 34 // 请求成功,继续业务逻辑 35 36 } catch (WXPayUtility.ApiException e) { 37 // 请求失败,根据状态码执行不同的逻辑 38 if (e.getErrorCode().equals("PARAM_ERROR")) { 39 // 错误:参数错误 40 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 41 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 42 // 错误:请求非法 43 // 解决方式:请参阅 44 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 45 // 如果request.limit超限制,会收到错误消息:当前查询结果数据量过大,请缩小limit后重试 46 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 47 // 错误:签名错误 48 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 49 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 50 // 错误:系统错误 51 // 解决方式:稍后原单重试 52 } else if (e.getErrorCode().equals("NO_AUTH")) { 53 // 错误:商户信息不合法,或无权限访问该投诉单 54 // 解决方式:登录商户平台核对,传入正确信息 55 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 56 // 错误:频率超限 57 // 解决方式:请求量不要超过接口调用频率限制 58 } else { 59 // 其他错误,稍等一会重试 60 } 61 } 62 } 63} 64
3.1.3 查询某个投诉单的具体内容和协商历史(包含图片)
在上一步中可以获取指定的投诉单complaint_id
,此时商户可以调用API主动查询相关信息:
商户可参照该Demo中的
equeryComplaint
函数查询指定投诉单的用户投诉详情,包含投诉关联订单信息、支付分服务单信息、投诉的问题类型、问题描述、投诉人联系方式等信息,方便商户处理投诉,更多用户投诉单详情字段可参考 查询投诉单详情 。也可参照该Demo中的
queryNegotiationHistory
函数查询指定投诉单的用户与商户的协商历史,以分页输出查询结果,方便商户根据处理历史来制定后续处理方案,更多用户协商历史字段可参考 查询投诉单协商历史 。
此外,商家也可以定期主动去查询投诉单的信息和协商历史,同样是调用这两个接口。
1package com.java.complaint; 2 3import java.util.List; 4 5// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012533436 6import com.java.demo.QueryComplaintV2; 7import com.java.demo.QueryComplaintV2.ComplaintInfo; 8import com.java.demo.QueryComplaintV2.ComplaintMedia; 9import com.java.demo.QueryComplaintV2.QueryComplaintV2Request; 10// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012533439 11import com.java.demo.QueryNegotiationHistoryV2; 12import com.java.demo.QueryNegotiationHistoryV2.ComplaintNegotiationHistoryWithLogId; 13import com.java.demo.QueryNegotiationHistoryV2.QueryNegotiationHistoryV2Request; 14import com.java.demo.QueryNegotiationHistoryV2.QueryNegotiationHistoryV2Response; 15import com.java.utils.WXPayUtility; 16 17public class QueryComplaintExample { 18 public static void main(String[] args) { 19 // 填写查询的投诉单号,分页大小和分页开始位置 20 String complaint_id = "2002018xxxxxxxxxxxxxxxxxxxx"; 21 Long limit = 10L; 22 Long offset = 0L; 23 24 QueryComplaintExample example = new QueryComplaintExample(); 25 // 查询具体内容,填写投诉单号 26 example.queryComplaint(complaint_id); 27 // 查询协商历史,填写投诉单号,分页大小和分页开始位置 28 // 分页大小最大为300 29 example.queryNegotiationHistory(complaint_id, limit, offset); 30 } 31 32 QueryComplaintExample() { 33 34 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 35 this.mchid = "19xxxxxxxx"; // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 36 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 37 this.certificateSerialNo = "1DDE55AD98Exxxxxxxxxx"; // 商户API证书序列号,如何获取请参考 38 // https://pay.weixin.qq.com/doc/v3/merchant/4013053053 39 this.privateKeyFilePath = "/path/to/apiclient_key.pem"; // 商户API证书私钥文件路径,本地文件路径 40 this.wechatPayPublicKeyId = "PUB_KEY_ID_xxxxxxxxxxxxx"; // 微信支付公钥ID,如何获取请参考 41 // https://pay.weixin.qq.com/doc/v3/merchant/4013038816 42 this.wechatPayPublicKeyFilePath = "/path/to/wxp_pub.pem"; // 微信支付公钥文件路径,本地文件路径 43 } 44 45 private void queryComplaint(String complaintId) { 46 QueryComplaintV2 client = new QueryComplaintV2( 47 this.mchid, 48 this.certificateSerialNo, 49 this.privateKeyFilePath, 50 this.wechatPayPublicKeyId, 51 this.wechatPayPublicKeyFilePath); 52 53 QueryComplaintV2Request request = new QueryComplaintV2Request(); 54 55 // 填写查询的投诉单号 56 request.complaintId = complaintId; 57 try { 58 ComplaintInfo response = client.run(request); 59 // 请求成功,继续业务逻辑 60 List<ComplaintMedia> complaintMediaList = response.complaintMediaList; 61 if (complaintMediaList != null && complaintMediaList.size() > 0) { 62 // 根据媒体列表请求图片数据,可根据业务考虑异步处理 63 for (ComplaintMedia complaintMedia : complaintMediaList) { 64 // 获取的media_url形如:https://api.mch.weixin.qq.com/v3/merchant-service/images/xxxxx 65 List<String> media_urls = complaintMedia.mediaUrl; 66 for (String media_url : media_urls) { 67 // 提取URL最后的media_id部分 68 if (media_url != null && !media_url.isEmpty()) { 69 String[] parts = media_url.split("/"); 70 String lastPart = parts[parts.length - 1]; 71 System.out.println("media_id: " + lastPart); 72 } 73 // 请求图片数据,请参考https://pay.weixin.qq.com/doc/v3/merchant/4012467251 74 } 75 } 76 } 77 78 } catch (WXPayUtility.ApiException e) { 79 // 请求失败,根据状态码执行不同的逻辑 80 if (e.getErrorCode().equals("PARAM_ERROR")) { 81 // 错误:参数错误 82 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 83 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 84 // 错误:请求非法 85 // 解决方式:请参阅 86 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 87 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 88 // 错误:签名错误 89 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 90 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 91 // 错误:系统错误 92 // 解决方式:稍后原单重试 93 } else if (e.getErrorCode().equals("NO_AUTH")) { 94 // 错误:商户信息不合法,或无权访问该投诉单 95 // 解决方式:登录商户平台核对,传入正确信息 96 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 97 // 错误:频率超限 98 // 解决方式:请求量不要超过接口调用频率限制 99 } else { 100 // 其他错误,稍等一会重试 101 } 102 } 103 } 104 105 private void queryNegotiationHistory(String complaintId, Long limit, Long offset) { 106 QueryNegotiationHistoryV2 client = new QueryNegotiationHistoryV2( 107 this.mchid, 108 this.certificateSerialNo, 109 this.privateKeyFilePath, 110 this.wechatPayPublicKeyId, 111 this.wechatPayPublicKeyFilePath); 112 113 QueryNegotiationHistoryV2Request request = new QueryNegotiationHistoryV2Request(); 114 // 需要查询的投诉单号,分页大小和分页开始位置 115 request.complaintId = complaintId; 116 request.limit = limit; 117 request.offset = offset; 118 try { 119 QueryNegotiationHistoryV2Response response = client.run(request); 120 121 // 请求成功,继续业务逻辑 122 for (ComplaintNegotiationHistoryWithLogId log : response.data) { 123 if (log.complaintMediaList != null && log.complaintMediaList.mediaUrl != null 124 && log.complaintMediaList.mediaUrl.size() > 0) { 125 for (String imageUrl : log.complaintMediaList.mediaUrl) { 126 // 提取URL最后的media_id部分 127 if (imageUrl != null && !imageUrl.isEmpty()) { 128 String[] parts = imageUrl.split("/"); 129 String lastPart = parts[parts.length - 1]; 130 System.out.println("image_id: " + lastPart); 131 } 132 // 请求图片数据,请参考https://pay.weixin.qq.com/doc/v3/merchant/4012467251 133 } 134 } 135 } 136 137 } catch (WXPayUtility.ApiException e) { 138 // 请求失败,根据状态码执行不同的逻辑 139 if (e.getErrorCode().equals("PARAM_ERROR")) { 140 // 错误:参数错误 141 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 142 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 143 // 错误:请求非法 144 // 解决方式:请参阅 145 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 146 // 如果request.limit超限制,会收到错误消息:当前查询结果数据量过大,请缩小limit后重试 147 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 148 // 错误:签名错误 149 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 150 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 151 // 错误:系统错误 152 // 解决方式:稍后原单重试 153 } else if (e.getErrorCode().equals("NO_AUTH")) { 154 // 错误:商户信息不合法,或无权访问该投诉单 155 // 解决方式:登录商户平台核对,传入正确信息 156 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 157 // 错误:频率超限 158 // 解决方式:请求量不要超过接口调用频率限制 159 } else { 160 // 其他错误,稍等一会重试 161 } 162 } 163 } 164 165 private final String mchid; 166 private final String certificateSerialNo; 167 private final String privateKeyFilePath; 168 private final String wechatPayPublicKeyId; 169 private final String wechatPayPublicKeyFilePath; 170} 171
3.2 回复用户(包含图片)
在回复用户之前,可以先调用 图片上传接口 接口上传图片,获得media_id。
注意:上传图片获取到的media_id 不能直接
用于下载图片,需要将图片通过回复用户后, 查询投诉单协商历史 获取到图片URL 后,用获取的URL下载图片,并且URL不能做任何编码动作需要原样使用。
接下来,可以带着media_id回复用户:
1package com.java.complaint; 2 3import java.util.ArrayList; 4 5// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012467254 6import com.java.demo.ResponseComplaintV2; 7import com.java.demo.ResponseComplaintV2.MiniProgramJumpInfo; 8import com.java.demo.ResponseComplaintV2.ResponseComplaintV2Request; 9import com.java.utils.WXPayUtility; 10 11public class ResponseComplaintExample { 12 13 public static void main(String[] args) { 14 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 15 ResponseComplaintV2 client = new ResponseComplaintV2( 16 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 17 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 18 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 19 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 20 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 21 ); 22 23 ResponseComplaintV2Request request = new ResponseComplaintV2Request(); 24 // 投诉单号,商户号,回复内容,回复图片(已上传好的media_id,最多四张) 25 request.complaintId = "200201xxxxxxxxxxxxxxxxxxxx"; 26 request.complaintedMchid = "1900xxxxx1"; 27 request.responseContent = "已与用户沟通解决"; 28 request.responseImages = new ArrayList<>(); 29 { 30 request.responseImages.add("aabbccdd"); // 在传入图片前应当先上传图片,请参考:https://pay.weixin.qq.com/doc/v3/merchant/4012467250 31 } 32 33 // 注意,这里仅对部分商户开放,如有需要请联系对接的行业运营进行咨询 34 // 商户可以在下述内容中二选一填入请求: 35 // 1. 跳转链接 + 文案(缺一不可) 36 request.jumpUrl = "https://www.xxx.com/notify"; 37 request.jumpUrlText = "查看订单详情"; 38 // 2. 小程序信息 39 request.miniProgramJumpInfo = new MiniProgramJumpInfo(); 40 request.miniProgramJumpInfo.appid = "example_appid"; 41 request.miniProgramJumpInfo.path = "example_path"; 42 request.miniProgramJumpInfo.text = "example_text"; 43 44 try { 45 client.run(request); 46 47 // 请求成功,继续业务逻辑 48 } catch (WXPayUtility.ApiException e) { 49 // 请求失败,根据状态码执行不同的逻辑 50 if (e.getErrorCode() == "PARAM_ERROR") { 51 // 错误:参数错误 52 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 53 } else if (e.getErrorCode() == "INVALID_REQUEST") { 54 // 错误:请求非法,请求参数正确,但是不符合业务规则 55 // 解决方式:请参阅 https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 56 } else if (e.getErrorCode() == "SIGN_ERROR") { 57 // 错误:签名错误 58 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 59 } else if (e.getErrorCode() == "SYSTEM_ERROR") { 60 // 错误:系统错误 61 // 解决方式:稍后原单重试 62 } else if (e.getErrorCode() == "NO_AUTH") { 63 // 错误:商户信息不合法,或无权访问投诉单 64 // 解决方式:登录商户平台核对,传入正确信息 65 } else if (e.getErrorCode() == "FREQUENCY_LIMITED") { 66 // 错误:频率超限 67 //解决方式:请求量不要超过接口调用频率限制 68 }else { 69 //其他错误,稍等一会重试 70 } 71 } 72 } 73}
3.3 反馈处理完成
在确保和用户反馈已处理完成的情况下,可以调用此接口,标识反馈投诉单已处理完成,投诉单进入已完成状态,反馈处理完成接口的字段详情可参考 反馈处理完成 。
注意:反馈处理完成接口与回复用户接口之间,建议间隔一段时间调用(5s 或者10s),避免并发对同一个投诉单做操作,导致接口调用失败。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012467255 4import com.java.demo.CompleteComplaintV2; 5import com.java.demo.CompleteComplaintV2.CompleteComplaintV2Request; 6import com.java.utils.WXPayUtility; 7 8public class CompleteComplaintExample { 9 10 public static void main(String[] args) { 11 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 12 CompleteComplaintV2 client = new CompleteComplaintV2( 13 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 14 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 15 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 16 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 17 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 18 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 19 ); 20 21 CompleteComplaintV2Request request = new CompleteComplaintV2Request(); 22 request.complaintId = "2002018xxxxxxxxxxxxxxxxxxxx"; 23 request.complaintedMchid = "190xxxxxxx"; 24 try { 25 client.run(request); 26 27 // 请求成功,继续业务逻辑 28 } catch (WXPayUtility.ApiException e) { 29 // 请求失败,根据状态码执行不同的逻辑 30 if (e.getErrorCode().equals("PARAM_ERROR")) { 31 // 错误:参数错误 32 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 33 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 34 // 错误:请求非法,请求参数正确,但是不符合业务规则 35 // 解决方式:请参阅 36 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 37 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 38 // 错误:签名错误 39 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 40 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 41 // 错误:系统错误 42 // 解决方式:稍后原单重试 43 } else if (e.getErrorCode().equals("NO_AUTH")) { 44 // 错误:商户信息不合法,或无权访问投诉单 45 // 解决方式:登录商户平台核对,传入正确信息 46 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 47 // 错误:频率超限 48 // 解决方式:请求量不要超过接口调用频率限制 49 } else { 50 // 其他错误,稍等一会重试 51 } 52 } 53 } 54} 55
3.4 更新退款审批结果
针对“申请退款单”,需要商户明确返回是否可退款的审批结果。全额退款后,用户仅可继续留言,不可以继续投诉,更多关于更新退款审批结果接口的字段详情可参考 更新退款审批结果 。
1package com.java.complaint; 2 3// 使用商户平台通用投诉接口:https://pay.weixin.qq.com/doc/v3/merchant/4012467256 4import com.java.demo.UpdateRefundProgress; 5import com.java.demo.UpdateRefundProgress.Action; 6import com.java.demo.UpdateRefundProgress.UpdateRefundProgressRequest; 7import com.java.utils.WXPayUtility; 8 9public class UpdateRefundProgressExample { 10 11 public static void main(String[] args) { 12 // 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 13 UpdateRefundProgress client = new UpdateRefundProgress( 14 "19xxxxxxxx", // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 15 // https://pay.weixin.qq.com/doc/v3/merchant/4013070756 16 "1DDE55AD98Exxxxxxxxxx", // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 17 "/path/to/apiclient_key.pem", // 商户API证书私钥文件路径,本地文件路径 18 "PUB_KEY_ID_xxxxxxxxxxxxx", // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 19 "/path/to/wxp_pub.pem" // 微信支付公钥文件路径,本地文件路径 20 ); 21 22 UpdateRefundProgressRequest request = new UpdateRefundProgressRequest(); 23 // 若根据用户描述,核实可以退款,审批动作传入“APPROVE”,同意退款,并给出一个预计退款时间。传入“同意退款”后,需要额外调退款接口发起原路退款。退款到账后,投诉单的状态将自动扭转为“处理完成”。 24 request.action = Action.APPROVE; 25 request.complaintId = "200201xxxxxxxxxxxxxxxxxxx"; 26 request.launchRefundDay = 3L; // 同意退款传入:预计发起退款时间 27 request.remark = "已处理完成"; // 任何需要向微信支付客服反馈的信息,用户不可见 28 29 // 若根据用户描述,核实不能退款,审批动作传入“REJECT”,拒绝退款,并给出拒绝退款原因和举证。驳回退款后,投诉单的状态将自动扭转为“处理完成”。 30 // request.action = Action.REJECT; 31 // request.complaintId = "200201xxxxxxxxxxxxxxxxxxx"; 32 // request.rejectReason = "拒绝退款"; // 拒绝退款传入:拒绝原因 33 // request.rejectMediaList = new ArrayList<>(); // 拒绝退款传入:拒绝举证 34 // { 35 // // 可以提前上传图片,这里填写media_id,最多上传4张图片凭证 36 // request.rejectMediaList.add("aabbccdd"); 37 // } 38 // request.remark = "已处理完成"; // 任何需要向微信支付客服反馈的信息,用户不可见 39 try { 40 client.run(request); 41 // 请求成功,继续业务逻辑 42 } catch (WXPayUtility.ApiException e) { 43 // 请求失败,根据状态码执行不同的逻辑 44 if (e.getErrorCode().equals("PARAM_ERROR")) { 45 // 错误:参数错误 46 // 解决方式:按照报错返回的message,需要根据接口文档对参数进行校对和修正,然后重新发起请求 47 } else if (e.getErrorCode().equals("INVALID_REQUEST")) { 48 // 错误:请求非法,请求参数正确,但是不符合业务规则 49 // 解决方式:请参阅 50 // https://pay.weixin.qq.com/doc/v3/merchant/4012081709,如果可以修改参数达到符合业务规则的修改请求符合业务规则之后再原单重试 51 } else if (e.getErrorCode().equals("SIGN_ERROR")) { 52 // 错误:签名错误 53 // 解决方式:检查平台商户公钥ID,公钥文件,同时确认下签名过程是不是按照微信支付的签名方式 54 } else if (e.getErrorCode().equals("SYSTEM_ERROR")) { 55 // 错误:系统错误 56 // 解决方式:稍后原单重试 57 } else if (e.getErrorCode().equals("NO_AUTH")) { 58 // 错误:商户信息不合法,或无权访问投诉单 59 // 解决方式:登录商户平台核对,传入正确信息 60 } else if (e.getErrorCode().equals("FREQUENCY_LIMITED")) { 61 // 错误:频率超限 62 // 解决方式:请求量不要超过接口调用频率限制 63 } else { 64 // 其他错误,稍等一会重试 65 } 66 } 67 } 68} 69