100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 微信支付V3之投诉回调API封装

微信支付V3之投诉回调API封装

时间:2019-05-29 20:24:35

相关推荐

微信支付V3之投诉回调API封装

大家好,我是梦辛工作室的灵,近期在制作相关软件的过程中,有需要用到对微信商户号的投诉风险合规,第一次搞这个还是踩了不少坑,然后为了方便使用做了以下封装便于大家使用,其实也不难,主要怪自己没有去仔细看微信提供的指导(在 官方提供的 wechatpay-apache-httpclient-0.2.2.jar 的基础上继续简化的),下来就讲下怎么写吧

先准备好以下相关资料:

1.商户号 编号

2.商户号 ApiV3 秘钥,需要在 微信商户号后台设置

3.商户号证书私钥,下载商户号 证书时会有那个 apiclient_key.pem 文件就是它

4.商户号证书序列号,这个可以在 微信商户号证书管理后台查看

准备好以上资料后就可以开始写了

第一步:加载证书,获取到httpCilent对象:

public void setup() {if (!doCheckParam()) {isStep = false;return;}try {privateKey = new String(Files.readAllBytes(Paths.get(privateKeyFilePath)), "utf-8");// 加载商户私钥(privateKey:私钥字符串)merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));// 初始化httpClienthttpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();isStep = true;} catch (Exception e) {// TODO: handle exceptionerrorHint = errorHint.toString();isStep = false;}}

第二步:发起创建投诉回调通知 ,其他操作类同

/*** 创建投诉回调通知* * @param url* 回调地址* @return* @throws Exception*/public String CreateComplaintsNotify(String url) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpPost httpPost = new HttpPost("https://api.mch./v3/merchant-service/complaint-notifications");JSONObject dataJSON = new JSONObject();dataJSON.put("url", url);StringEntity entity = new StringEntity(dataJSON.toString());entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpPost);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}

这里需要注意的是:微信提供的这个 API封装(wechatpay-apache-httpclient-0.2.2.jar)在发起请求时已经自带了 Authorization Header,所以不用再去添加这个Header,不然会报错,楼主在这里被坑了好久,翻了不好文档才找到

还有就是如果有出现 java.security.InvalidKeyException: Illegal key size WechatPayHttpClientBuild 错误的话,请注意 java版本,如果在 java 8u 162 以下的话,请更换 java 版本 ,必须要在 java 8u 162 以上才可以 , 早期的java 运行 限制了 JCE支持的秘钥长度,即默认不支持256位的AES,这里楼主也被坑了好早,试过好多方法才找到,换来是java版本低了

下面是所有代码示例:

package com.mx.util;import java.io.ByteArrayInputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import .URI;import java.nio.file.Files;import java.nio.file.Paths;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.util.Base64;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import mons.codec.digest.DigestUtils;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpDelete;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpPut;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.util.EntityUtils;import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;import com.wechat.pay.contrib.apache.httpclient.WechatPayUploadHttpPost;import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;import net.sf.json.JSONArray;import net.sf.json.JSONObject;public class WeChatAPIV3 {CloseableHttpClient httpClient;String mchId = "商户号";String mchSerialNo = "证书序列号";String apiV3Key = "APIV3秘钥";String privateKeyFilePath = "证书私钥路径";String privateKey = "";PrivateKey merchantPrivateKey;String errorHint = "";public String getMchId() {return mchId;}public void setMchId(String mchId) {this.mchId = mchId;}public String getMchSerialNo() {return mchSerialNo;}public void setMchSerialNo(String mchSerialNo) {this.mchSerialNo = mchSerialNo;}public String getApiV3Key() {return apiV3Key;}public void setApiV3Key(String apiV3Key) {this.apiV3Key = apiV3Key;}public String getPrivateKeyFilePath() {return privateKeyFilePath;}public void setPrivateKeyFilePath(String privateKeyFilePath) {this.privateKeyFilePath = privateKeyFilePath;}private boolean isStep = false;public WeChatAPIV3() {}public WeChatAPIV3(String mchId, String mchSerialNo, String apiV3Key, String privateKeyFilePath) {this.mchId = mchId;this.mchSerialNo = mchSerialNo;this.apiV3Key = apiV3Key;this.privateKeyFilePath = privateKeyFilePath;}public String rsaDecryptOAEP(String ciphertext) throws BadPaddingException, IOException {if (!isStep) {errorHint = "未成功启用Step";return null;}if (merchantPrivateKey == null) {errorHint = "未成功加载私钥";return null;}try {Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");cipher.init(Cipher.DECRYPT_MODE, merchantPrivateKey);byte[] data = Base64.getDecoder().decode(ciphertext);return new String(cipher.doFinal(data), "utf-8");} catch (NoSuchPaddingException | NoSuchAlgorithmException e) {throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);} catch (InvalidKeyException e) {throw new IllegalArgumentException("无效的私钥", e);} catch (BadPaddingException | IllegalBlockSizeException e) {throw new BadPaddingException("解密失败");}}public boolean doCheckParam() {return doCheckValue(mchId, mchSerialNo, apiV3Key, privateKeyFilePath);}public boolean doCheckValue(String... item) {for (String each : item) {if (each == null || each.length() == 0) {errorHint = "缺少必要参数";return false;}}return true;}public void setup() {if (!doCheckParam()) {isStep = false;return;}try {privateKey = new String(Files.readAllBytes(Paths.get(privateKeyFilePath)), "utf-8");// 加载商户私钥(privateKey:私钥字符串)merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes("utf-8")));// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),apiV3Key.getBytes("utf-8"));// 初始化httpClienthttpClient = WechatPayHttpClientBuilder.create().withMerchant(mchId, mchSerialNo, merchantPrivateKey).withValidator(new WechatPay2Validator(verifier)).build();isStep = true;} catch (Exception e) {// TODO: handle exceptionerrorHint = errorHint.toString();isStep = false;}}/*** 查询投诉详情* * @param complaint_id* 投诉单号* @return* @throws Exception*/public String GetComplaintsInfo(String complaint_id) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpGet httpGet = new HttpGet("https://api.mch./v3/merchant-service/complaints-v2/" + complaint_id);httpGet.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpGet);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 查询协商历史* * @param complaint_id* 投诉单号* @param offset* 开始位置* @param limit* 返回数据条数* @return* @throws Exception*/public String GetComplaintsHis(String complaint_id, int offset, int limit) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpGet httpGet = new HttpGet("https://api.mch./v3/merchant-service/complaints-v2/" + complaint_id+ "/negotiation-historys?limit=" + limit + "&offset=" + offset);httpGet.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpGet);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 查询投诉单列表* * @param offset* 开始位置* @param limit* 返回数据条数* @param begin_date* 开始日期 yyyy-MM-dd* @param end_date* 结束日期 yyyy-MM-dd 时间差最大一个月* @return 查询结果* @throws Exception*/public String GetComplaintsList(int offset, int limit, String begin_date, String end_date) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpGet httpGet = new HttpGet("https://api.mch./v3/merchant-service/complaints-v2?limit=" + limit + "&offset=" + offset+ "&begin_date=" + begin_date + "&end_date=" + end_date + "&complainted_mchid=" + mchId);httpGet.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpGet);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}public void after() throws IOException {httpClient.close();}/*** 创建投诉回调通知* * @param url* 回调地址* @return* @throws Exception*/public String CreateComplaintsNotify(String url) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpPost httpPost = new HttpPost("https://api.mch./v3/merchant-service/complaint-notifications");JSONObject dataJSON = new JSONObject();dataJSON.put("url", url);StringEntity entity = new StringEntity(dataJSON.toString());entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpPost);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 更新投诉回调通知* * @param url* 回调通知* @return* @throws Exception*/public String UpdateComplaintsNotify(String url) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpPut httpPut = new HttpPut("https://api.mch./v3/merchant-service/complaint-notifications");JSONObject dataJSON = new JSONObject();dataJSON.put("url", url);StringEntity entity = new StringEntity(dataJSON.toString());entity.setContentType("application/json");httpPut.setEntity(entity);httpPut.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpPut);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 删除投诉回调通知地址* * @return* @throws Exception*/public String DelComplaintsNotify() throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpDelete httpDel = new HttpDelete("https://api.mch./v3/merchant-service/complaint-notifications");httpDel.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpDel);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 提交回复* * @param complaint_id* 被投诉单号* @param response_content* 回复内容* @param response_images* 回复图片* @return* @throws Exception*/public String ReplyInfo(String complaint_id, String response_content, String response_images) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpPost httpPost = new HttpPost("https://api.mch./v3/merchant-service/complaints-v2/" + complaint_id + "/response");// 请求body参数JSONObject dataJSON = new JSONObject();dataJSON.put("complainted_mchid", mchId);dataJSON.put("response_content", response_content);String[] imgs = response_images.split(",");JSONArray array = new JSONArray();for (String img : imgs) {array.add(img);}StringEntity entity = new StringEntity(dataJSON.toString());entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpPost);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 反馈处理完成* * @param complaint_id* 投诉单号* @return* @throws Exception*/public String CompleteComplaints(String complaint_id) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;// 请求URLHttpPost httpPost = new HttpPost("https://api.mch./v3/merchant-service/complaints-v2/" + complaint_id + "/complete");JSONObject dataJSON = new JSONObject();dataJSON.put("complainted_mchid", mchId);StringEntity entity = new StringEntity(dataJSON.toString());entity.setContentType("application/json");httpPost.setEntity(entity);httpPost.setHeader("Accept", "application/json");// 完成签名并执行请求CloseableHttpResponse response = httpClient.execute(httpPost);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}return result;}/*** 上传图片* * @param filePath* 图片路径* @return* @throws Exception*/public String uploadImg(String filePath) throws Exception {if (!isStep) {errorHint = "未成功启用Step";return null;}String result = null;URI uri = new URI("https://api.mch./v3/merchant-service/images/upload");File file = new File(filePath);try (FileInputStream ins1 = new FileInputStream(file)) {String sha256 = DigestUtils.sha256Hex(ins1);try (InputStream ins2 = new FileInputStream(file)) {HttpPost request = new WechatPayUploadHttpPost.Builder(uri).withImage(file.getName(), sha256, ins2).build();CloseableHttpResponse response = httpClient.execute(request);try {int statusCode = response.getStatusLine().getStatusCode();if (statusCode == 200) {// 处理成功result = EntityUtils.toString(response.getEntity());} else if (statusCode == 204) {// 处理成功,无返回Bodyresult = "{'code':204}";} else {result = EntityUtils.toString(response.getEntity());}} finally {response.close();}}}return result;}}

还有一个解密的封装类:

package com.mx.util;import java.io.IOException;import java.security.GeneralSecurityException;import java.security.InvalidAlgorithmParameterException;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.util.Base64;import javax.crypto.Cipher;import javax.crypto.NoSuchPaddingException;import javax.crypto.spec.GCMParameterSpec;import javax.crypto.spec.SecretKeySpec;public class AesUtil {static final int KEY_LENGTH_BYTE = 32;static final int TAG_LENGTH_BIT = 128;private final byte[] aesKey;/*** 创建解密类* * @param key*/public AesUtil(byte[] key) {if (key.length != KEY_LENGTH_BYTE) {throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");}this.aesKey = key;}/*** 解密数据* * @param associatedData* 附加数据包* @param nonce* 加密使用的随机串初始化向量* @param ciphertext* Base64编码后的密文* @return* @throws GeneralSecurityException* @throws IOException*/public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)throws GeneralSecurityException, IOException {try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(aesKey, "AES");GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);cipher.init(Cipher.DECRYPT_MODE, key, spec);cipher.updateAAD(associatedData);return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new IllegalStateException(e);} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {throw new IllegalArgumentException(e);}}}

上面这个就是楼主封装好的API,简单易用,下面是怎么调用:

public static void main(String[] args) {WeChatAPIV3 apiv3 = new WeChatAPIV3();try {apiv3.setMchId("商户号");apiv3.setMchSerialNo("证书序列号");apiv3.setApiV3Key("APIV3秘钥");apiv3.setPrivateKeyFilePath("证书私钥路径");apiv3.setup();// 查询投诉列表// System.out.print(apiv3.GetComplaintsList(从第几个开始, 返回多少条数据,// "开始日期格式:yyyy-MM-dd","结束日期格式:yyyy-MM-dd"));// 查询投诉详情// System.out.print(apiv3.GetComplaintsInfo("投诉单号"));// 查询投诉历史// System.out.print(apiv3.GetComplaintsHis("投诉单号", 从第几个开始,// 返回多少条数据));// 创建投诉回调通知// System.out.print(apiv3.CreateComplaintsNotify("通知地址"));// 更新投诉回调通知// System.out.print(apiv3.UpdateComplaintsNotify("通知地址"));// 解密电话// System.out.print(apiv3.rsaDecryptOAEP(// "密文"));} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}

你就只用填写对应的参数后,调用上面的方法即可,是不是一下就简单了很多,哇,你们不知道,我当时憋了好久才把这个给憋通,-=- ,主要怪楼主自己没有认真看API文档

github 传送门:/wintton/MxWxPayUtil.git

路径:src/com/mx/util/WeChatAPIV3.java

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。