mirror of
https://gitee.com/binary/weixin-java-tools.git
synced 2026-03-10 00:13:40 +08:00
🆕 #2562 【微信支付】增加微信消费者投诉2.0接口
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package com.github.binarywang.wxpay.service;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.complaint.*;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 微信支付 消费者投诉2.0 API.
|
||||
* Created by jmdhappy on 2022/3/19.
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="https://gitee.com/jeequan/jeepay">jmdhappy</a>
|
||||
*/
|
||||
public interface ComplaintService {
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 查询投诉单列表API
|
||||
* 商户可通过调用此接口,查询指定时间段的所有用户投诉信息,以分页输出查询结果。
|
||||
* 对于服务商、渠道商,可通过调用此接口,查询指定子商户号对应子商户的投诉信息,若不指定则查询所有子商户投诉信息。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_11.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link ComplaintRequest} 查询投诉单列表请求数据
|
||||
* @return {@link ComplaintResult} 微信返回的投诉单列表
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintResult queryComplaints(ComplaintRequest request) throws WxPayException, BadPaddingException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 查询投诉单详情API
|
||||
* 商户可通过调用此接口,查询指定投诉单的用户投诉详情,包含投诉内容、投诉关联订单、投诉人联系方式等信息,方便商户处理投诉。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_13.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link ComplaintDetailRequest} 投诉单详情请求数据
|
||||
* @return {@link ComplaintDetailResult} 微信返回的投诉单详情
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintDetailResult getComplaint(ComplaintDetailRequest request) throws WxPayException, BadPaddingException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 查询投诉协商历史API
|
||||
* 商户可通过调用此接口,查询指定投诉的用户商户协商历史,以分页输出查询结果,方便商户根据处理历史来制定后续处理方案。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_12.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link NegotiationHistoryRequest} 请求数据
|
||||
* @return {@link NegotiationHistoryResult} 微信返回结果
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
NegotiationHistoryResult queryNegotiationHistorys(NegotiationHistoryRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 创建投诉通知回调地址API
|
||||
* 商户通过调用此接口创建投诉通知回调URL,当用户产生新投诉且投诉状态已变更时,微信支付会通过回 调URL通知商户。对于服务商、渠道商,会收到所有子商户的投诉信息推送。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_2.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link ComplaintDetailRequest} 请求数据
|
||||
* @return {@link ComplaintNotifyUrlResult} 微信返回结果
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintNotifyUrlResult addComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 查询投诉通知回调地址API
|
||||
* 商户通过调用此接口查询投诉通知的回调URL。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_3.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @return {@link ComplaintNotifyUrlResult} 微信返回结果
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintNotifyUrlResult getComplaintNotifyUrl() throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 更新投诉通知回调地址API
|
||||
* 商户通过调用此接口更新投诉通知的回调URL。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_4.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link ComplaintDetailRequest} 请求数据
|
||||
* @return {@link ComplaintNotifyUrlResult} 微信返回结果
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintNotifyUrlResult updateComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 删除投诉通知回调地址API
|
||||
* 当商户不再需要推送通知时,可通过调用此接口删除投诉通知的回调URL,取消通知回调。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_5.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
void deleteComplaintNotifyUrl() throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 提交回复API
|
||||
* 商户可通过调用此接口,提交回复内容。其中上传图片凭证需首先调用商户上传反馈图片接口,得到图片id,再将id填入请求。
|
||||
* 回复可配置文字链,传入跳转链接文案和跳转链接字段,用户点击即可跳转对应页面
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_14.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link ResponseRequest} 请求数据
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
void submitResponse(ResponseRequest request) throws WxPayException;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 反馈处理完成API
|
||||
* 商户可通过调用此接口,反馈投诉单已处理完成。
|
||||
* 文档详见: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_15.shtml
|
||||
* </pre>
|
||||
*
|
||||
* @param request {@link CompleteRequest} 请求数据
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
void complete(CompleteRequest request) throws WxPayException;
|
||||
|
||||
}
|
||||
@@ -173,6 +173,25 @@ public interface WxPayService {
|
||||
*/
|
||||
InputStream downloadV3(String url) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 发送put V3请求,得到响应字符串.
|
||||
*
|
||||
* @param url 请求地址
|
||||
* @param url 请求数据
|
||||
* @return 返回请求结果字符串 string
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
String putV3(String url, String requestStr) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 发送delete V3请求,得到响应字符串.
|
||||
*
|
||||
* @param url 请求地址
|
||||
* @return 返回请求结果字符串 string
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
String deleteV3(String url) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 获取微信签约代扣服务类
|
||||
* @return entrust service
|
||||
@@ -1296,4 +1315,22 @@ public interface WxPayService {
|
||||
* @throws WxPayException .
|
||||
*/
|
||||
WxPayQueryExchangeRateResult queryExchangeRate(String feeType, String date) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 解析投诉通知
|
||||
* 详见https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter10_2_16.shtml
|
||||
*
|
||||
* @param notifyData 通知数据
|
||||
* @param header 通知头部数据,不传则表示不校验头
|
||||
* @return the wx pay refund notify result
|
||||
* @throws WxPayException the wx pay exception
|
||||
*/
|
||||
ComplaintNotifyResult parseComplaintNotifyResult(String notifyData, SignatureHeader header) throws WxPayException;
|
||||
|
||||
/**
|
||||
* 获取消费者投诉服务类.
|
||||
*
|
||||
* @return the complaints service
|
||||
*/
|
||||
ComplaintService getComplaintsService();
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
private final WxEntrustPapService wxEntrustPapService = new WxEntrustPapServiceImpl(this);
|
||||
private final PartnerTransferService partnerTransferService = new PartnerTransferServiceImpl(this);
|
||||
private final PayrollService payrollService = new PayrollServiceImpl(this);
|
||||
private final ComplaintService complaintsService = new ComplaintServiceImpl(this);
|
||||
|
||||
protected Map<String, WxPayConfig> configMap;
|
||||
|
||||
@@ -1222,4 +1223,33 @@ public abstract class BaseWxPayServiceImpl implements WxPayService {
|
||||
result.checkResult(this, request.getSignType(), true);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintNotifyResult parseComplaintNotifyResult(String notifyData, SignatureHeader header) throws WxPayException {
|
||||
if (Objects.nonNull(header) && !this.verifyNotifySign(header, notifyData)) {
|
||||
throw new WxPayException("非法请求,头部信息验证失败");
|
||||
}
|
||||
OriginNotifyResponse response = GSON.fromJson(notifyData, OriginNotifyResponse.class);
|
||||
OriginNotifyResponse.Resource resource = response.getResource();
|
||||
String cipherText = resource.getCiphertext();
|
||||
String associatedData = resource.getAssociatedData();
|
||||
String nonce = resource.getNonce();
|
||||
String apiV3Key = this.getConfig().getApiV3Key();
|
||||
try {
|
||||
String result = AesUtils.decryptToString(associatedData, nonce, cipherText, apiV3Key);
|
||||
ComplaintNotifyResult.DecryptNotifyResult decryptNotifyResult = GSON.fromJson(result, ComplaintNotifyResult.DecryptNotifyResult.class);
|
||||
ComplaintNotifyResult notifyResult = new ComplaintNotifyResult();
|
||||
notifyResult.setRawData(response);
|
||||
notifyResult.setResult(decryptNotifyResult);
|
||||
return notifyResult;
|
||||
} catch (GeneralSecurityException | IOException e) {
|
||||
throw new WxPayException("解析报文异常!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintService getComplaintsService() {
|
||||
return complaintsService;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.github.binarywang.wxpay.service.impl;
|
||||
|
||||
import com.github.binarywang.wxpay.bean.complaint.*;
|
||||
import com.github.binarywang.wxpay.exception.WxPayException;
|
||||
import com.github.binarywang.wxpay.service.ComplaintService;
|
||||
import com.github.binarywang.wxpay.service.WxPayService;
|
||||
import com.github.binarywang.wxpay.v3.util.RsaCryptoUtil;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* 消费者投诉2.0 实现.
|
||||
* Created by jmdhappy on 2022/3/19.
|
||||
* </pre>
|
||||
*
|
||||
* @author <a href="https://gitee.com/jeequan/jeepay">jmdhappy</a>
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class ComplaintServiceImpl implements ComplaintService {
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
private final WxPayService payService;
|
||||
|
||||
@Override
|
||||
public ComplaintResult queryComplaints(ComplaintRequest request) throws WxPayException, BadPaddingException {
|
||||
String url = String.format("%s/v3/merchant-service/complaints-v2?limit=%d&offset=%d&begin_date=%s&end_date=%s&complainted_mchid=%s",
|
||||
this.payService.getPayBaseUrl(), request.getLimit(), request.getOffset(), request.getBeginDate(), request.getEndDate(), request.getComplaintedMchid());
|
||||
String response = this.payService.getV3(url);
|
||||
ComplaintResult complaintResult = GSON.fromJson(response, ComplaintResult.class);
|
||||
List<ComplaintDetailResult> data = complaintResult.getData();
|
||||
for (ComplaintDetailResult complaintDetailResult : data) {
|
||||
// 对手机号进行解密操作
|
||||
String payerPhone = RsaCryptoUtil.decryptOAEP(complaintDetailResult.getPayerPhone(), this.payService.getConfig().getPrivateKey());
|
||||
complaintDetailResult.setPayerPhone(payerPhone);
|
||||
}
|
||||
return complaintResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintDetailResult getComplaint(ComplaintDetailRequest request) throws WxPayException, BadPaddingException {
|
||||
String url = String.format("%s/v3/merchant-service/complaints-v2/%s",
|
||||
this.payService.getPayBaseUrl(), request.getComplaintId());
|
||||
String response = this.payService.getV3(url);
|
||||
ComplaintDetailResult result = GSON.fromJson(response, ComplaintDetailResult.class);
|
||||
// 对手机号进行解密操作
|
||||
String payerPhone = RsaCryptoUtil.decryptOAEP(result.getPayerPhone(), this.payService.getConfig().getPrivateKey());
|
||||
result.setPayerPhone(payerPhone);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NegotiationHistoryResult queryNegotiationHistorys(NegotiationHistoryRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaints-v2/%s/negotiation-historys?limit=%d&offset=%d",
|
||||
this.payService.getPayBaseUrl(), request.getComplaintId(), request.getLimit(), request.getOffset());
|
||||
String response = this.payService.getV3(url);
|
||||
return GSON.fromJson(response, NegotiationHistoryResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintNotifyUrlResult addComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.postV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, ComplaintNotifyUrlResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintNotifyUrlResult getComplaintNotifyUrl() throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.getV3(url);
|
||||
return GSON.fromJson(response, ComplaintNotifyUrlResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplaintNotifyUrlResult updateComplaintNotifyUrl(ComplaintNotifyUrlRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl());
|
||||
String response = this.payService.putV3(url, GSON.toJson(request));
|
||||
return GSON.fromJson(response, ComplaintNotifyUrlResult.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteComplaintNotifyUrl() throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaint-notifications", this.payService.getPayBaseUrl());
|
||||
this.payService.deleteV3(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitResponse(ResponseRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaints-v2/%s/response", this.payService.getPayBaseUrl(), request.getComplaintId());
|
||||
// 上面url已经含有complaintId,这里设置为空,避免在body中再次传递,否则微信会报错
|
||||
request.setComplaintId(null);
|
||||
this.payService.postV3(url, GSON.toJson(request));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void complete(CompleteRequest request) throws WxPayException {
|
||||
String url = String.format("%s/v3/merchant-service/complaints-v2/%s/complete", this.payService.getPayBaseUrl(), request.getComplaintId());
|
||||
// 上面url已经含有complaintId,这里设置为空,避免在body中再次传递,否则微信会报错
|
||||
request.setComplaintId(null);
|
||||
this.payService.postV3(url, GSON.toJson(request));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -269,6 +269,24 @@ public class WxPayServiceApacheHttpImpl extends BaseWxPayServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String putV3(String url, String requestStr) throws WxPayException {
|
||||
HttpPut httpPut = new HttpPut(url);
|
||||
StringEntity entity = this.createEntry(requestStr);
|
||||
httpPut.setEntity(entity);
|
||||
httpPut.addHeader("Accept", "application/json");
|
||||
httpPut.addHeader("Content-Type", "application/json");
|
||||
return requestV3(url, httpPut);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteV3(String url) throws WxPayException {
|
||||
HttpDelete httpDelete = new HttpDelete(url);
|
||||
httpDelete.addHeader("Accept", "application/json");
|
||||
httpDelete.addHeader("Content-Type", "application/json");
|
||||
return requestV3(url, httpDelete);
|
||||
}
|
||||
|
||||
private CloseableHttpClient createApiV3HttpClient() throws WxPayException {
|
||||
CloseableHttpClient apiV3HttpClient = this.getConfig().getApiV3HttpClient();
|
||||
if (null == apiV3HttpClient) {
|
||||
|
||||
@@ -96,6 +96,16 @@ public class WxPayServiceJoddHttpImpl extends BaseWxPayServiceImpl {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String putV3(String url, String requestStr) throws WxPayException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String deleteV3(String url) throws WxPayException {
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpRequest buildHttpRequest(String url, String requestStr, boolean useKey) throws WxPayException {
|
||||
HttpRequest request = HttpRequest
|
||||
.post(url)
|
||||
|
||||
Reference in New Issue
Block a user