微信支付版本V3的Demo,在官方上下载下来,压根就是不能直接用的东西,你要想学会用,你就得一层一层的看源码,看文档,要求你事无巨细的做一个接入者。
如果接入API需要让人看源码来理解,我觉得是一件让人很费劲的事情。
就像你一个买了一个手机,你除了开机,关机,你啥都不知道怎么操作,截个屏,你分分钟都要在说明说书上找半天。
就像你买了个苹果手机,想长截屏,你看了好久的系统,搜了好久的百度,发现,最后是要另外下第三方的App才能支持。
然而安卓自带的长截屏,早就有了。
就像我写微信支付,我要写一个V3批量到零钱的接口,我看了好久的文档,下了demo,最后你只给了我些加密方式和一些其他无关痛痒的接口事例。
然而阿里的Demo早就Run一下就通了。
批量到零钱的接口的传参你写在了文档,Demo压根就没有这个批量到零钱的接口,我要看你Demo的其他事例,搞懂你的调用的原理,再来看具体的文档,自己再来写一遍,然后不断的Run看返回的信息,如果失败,再来问客服(回复速度,令人心感疲惫),然后自己也开始面向百度编程,面向CSDN编程。
学习成本太高,增加了入手难度,对于接入的使用者来说,是一件灾难的事情。
在我心目中的接入API就是一件,看着文档,把自己的参数设置进去Demo里面,跑一个main方法,叮咚,完成了。
很多阿里的API就是如此,方便快捷,你在对照着文档来设置参数就行,你压根不需要怎么看源码。
我自己本身的业务逻辑已经很多了,接入你的产品,你要我看源码来理解,看了又不懂,不懂我问了你又回的慢,回的慢就算了,你甚至会不回,我的内心接近崩溃的,我又不是你们公司的,为什么我需要懂你们的代码,懂了对我而言,有什么好处呢?我能去你公司上班,你能给我发工资吗?
以上吐槽只是个人对接过程遇到问题,不是捧谁踩谁。
真心希望科技是以人为本,服务所有的用户,做到傻瓜式接入。
科技的本质就是让人偷懒的,科技的进步也是从偷懒开始的。
微信在V3版本的Demo真的存在很多不完善,希望以后越做越好吧。
废话不多说,下面上下我自己理解的代码,你只需要跑一个main方法就行了。
(具体进件的注册商户的东西,我这里就略过了,自己看着官网流程即可)
下面的例子是,批量转账到零钱(服务商)的接入示例:
(微信文档地址:开发文档-微信支付批量转账到零钱)
微信支付V3版本,微信SDK,内部封装了部分方法,参照着官方Demo,下载地址:GitHub - wechatpay-apiv3/wechatpay-apache-httpclient: 微信支付 APIv3 Apache HttpClient装饰器(decorator)
步骤1:引入微信SDK依赖
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
0.3.0
cn.hutool
hutool-all
5.6.6
步骤2:构造微信接口通讯Client,构造隐私数据加密公钥,每次调用微信V3的接口都需要用Client来发起,敏感数据需通过平台公钥加密。
微信的的商户私钥和平台公钥有效期是一年的,一年到期需要手动去后台更新重新配置,微信说有延迟过期时间的重新获取平台公钥的方法,这里跳过了。
因为就算延迟了,到时候还是要手动后台在重新设置,怎样都无可避免重新设置,感觉意义不大,就跳过了。
但是第一次获取平台公钥的方法还是很麻烦的,最好的方法是下载他们的jar包去获取,这样会减少很多碰壁。
下载地址在这里:
GitHub - wechatpay-apiv3/CertificateDownloader: Java 微信支付 APIv3 平台证书的命令行下载工具
具体的配置方法,在文档地址里面都有说明,需要详细阅读,按照步骤操作即可,
商户号,商户API密钥、商户API密钥序列号、商户API V3密钥、都需要从微信后台去设置,具体可以登录商户后台设置,设置完之后保存本地,即可获得。(参数很多,需要自己看文档流程理解)
我执行jar包的命令:
java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
获取后,保存下来平台公钥、公钥序列号,可多次使用。
平台公钥私钥如:
下面是构造微信Client的代码:
package Util;
import cn.hutool.core.io.FileUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.File;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Arrays;
/**
* @Author zkinghao
* @Date 2022/1/11 15:05
*/
public class WeChatClient {
/**
* 微信通讯client
* @return CloseableHttpClient
*/
public static CloseableHttpClient getClient() {
/**商户私钥文件*/
File privateKeyFile = new File("E:\\wechat\\apiclient_key.pem");
InputStream privateKeyInputStream = FileUtil.getInputStream(privateKeyFile);
/**微信平台公钥文件*/
File platFormKeyFile= new File("E:\\wechat\\wechatpay.pem");
InputStream platformCertInputStream = FileUtil.getInputStream(platFormKeyFile);
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKeyInputStream);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant("商户号", "商户证书序列号", merchantPrivateKey)
.withWechatPay(Arrays.asList(PemUtil.loadCertificate(platformCertInputStream)));
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
/**
* 微信敏感数据加密公钥
* @return
*/
public static X509Certificate getSaveCertificates() {
File file2 = new File("微信平台私钥文件");
InputStream platformCertInputStream = FileUtil.getInputStream(file2);
return PemUtil.loadCertificate(platformCertInputStream);
}
}
步骤3:调用-发起批量转账API
前置:
1、需用户与小程序绑定,获取用户的openId;
2、也需在特约商户后台,服务商后台绑定对应的小程序的appId。
具体步骤可以参考:
商家商户号与AppID账号关联管理
绑定成功后,即可用调用接口,
微信接口说明地址:开发文档-微信支付批量转账到零钱
代码如下:
import Util.WeChatClient;
import cn.hutool.json.JSONUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import javax.crypto.IllegalBlockSizeException;
import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* @Author zkinghao
* @Date 2022/1/11 14:19
*/
public class TestBatch {
/**
* 发起批量转账API
*
* @throws IllegalBlockSizeException
* @throws IOException
*/
public static void batchPay() throws IllegalBlockSizeException, IOException {
CloseableHttpClient httpClient = WeChatClient.getClient();
X509Certificate x509Certificate = WeChatClient.getSaveCertificates();
Map map = new HashMap<>();
map.put("sub_mchid", "特约商户号");
map.put("authorization_type", "FUND_AUTHORIZATION_TYPE");
map.put("out_batch_no", "batch018");
map.put("batch_name", "测试3");
map.put("batch_remark", "批次备注出款");
map.put("total_amount", 50);
map.put("total_num", 1);
List
步骤4:
商家明细单号查询明细单API
微信接口说明地址:开发文档-微信支付批量转账到零钱
代码如下:
import Util.WeChatClient;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URISyntaxException;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* @Author zkinghao
* @Date 2022/1/11 15:08
*/
public class TestQueryBatch {
public static final Integer SUCCESS_CODE = 200;
/**
* 商家明细单号查询明细单API
* @throws URISyntaxException
* @throws IOException
*/
public static void queryBatch() throws URISyntaxException, IOException {
CloseableHttpClient httpClient = WeChatClient.getClient();
//批次号
String batchCode = "batch018";
//明细号
String detailCode = "detail018";
StringBuilder url = new StringBuilder("https://api.mch.weixin.qq.com/v3/partner-transfer/batches/out-batch-no/");
url.append(batchCode).append("/details/out-detail-no/").append(detailCode);
URIBuilder uriBuilder = new URIBuilder(url.toString());
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println("微信支付查询返回:" + bodyAsString);
JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
if (SUCCESS_CODE.equals(response.getStatusLine().getStatusCode())) {
//成功 或未知,仍需要判断具体状态
/**
* 枚举值:
* PROCESSING:转账中。正在处理中,转账结果尚未明确
* SUCCESS:转账成功
* FAIL:转账失败。需要确认失败原因后,再决定是否重新发起对该笔明细单的转账(并非整个转账批次单)
* 示例值:SUCCESS
*/
String status = jsonObject.getStr("detail_status");
String failReason = jsonObject.getStr("fail_reason");
System.out.println("交易状态:" + status);
System.out.println("失败原因:" + failReason);
} else {
//失败
}
} finally {
response.close();
}
}
public static void main(String[] args) throws Exception {
/**商家明细单号查询明细单API*/
queryBatch();
}
}
上面两个事例事例是对应post和get请求的区别,其他接口也都类似这样实现即可,看着文档实现就行,下面就不一一示范了。
但是有一个最坑的接口的接口,写了希望大家可以避坑,这也是我写这篇博客的最大意愿,希望大家都可以少走弯路。
那就是:下载电子回单API。
就是这个api:开发文档-微信支付批量转账到零钱
官方文档写的根本就没有代码,只有一个postman的脚本,还是我自己看脚本,看规则,然后自己抓包来看那个Authorization,再自己写一个类似postman脚本的代码调试出来的,问技术基本也是没有回过声的。
真的,没有文档的东西,开发起来,真的是让人疲惫,耗时间,希望后面的人可以不要踩坑了。也希望微信能好好更新文档,弄的更通俗易懂,V3版本的demo真的是少的可怜,开发起来都要从头到尾看一遍。看的是都以为自己是腾讯人员的开发人员一样,要从头到尾的来读你的源码。
我还去他们的问答区写下了自己源码,希望对需要的你们有用。
下面是下载电子回单API的步骤:
步骤1:
调用:转账明细电子回单受理API
开发文档-微信支付批量转账到零钱
代码如下:
import Util.WeChatClient;
import cn.hutool.json.JSONUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* @Author zkinghao
* @Date 2022/1/11 17:57
*/
public class TestApplyReceipts {
/**
* 申请电子回单
* @throws URISyntaxException
* @throws IOException
*/
public static void apply() throws URISyntaxException, IOException {
CloseableHttpClient httpClient = WeChatClient.getClient();
Map map = new HashMap<>(1);
map.put("out_batch_no", "batch016");
String body = JSONUtil.toJsonStr(map);
System.out.println("请求参数:" + body);
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/transfer/bill-receipt");
httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString());
httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
httpPost.setEntity(new StringEntity(body, "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println("接口返回:" + bodyAsString);
} finally {
response.close();
}
}
public static void main(String[] args) throws Exception{
/**申请电子回单*/
apply();
}
}
步骤2:
查询转账明细电子回单受理结果API
开发文档-微信支付批量转账到零钱
代码如下:
import Util.WeChatClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.net.URISyntaxException;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* @Author zkinghao
* @Date 2022/1/11 17:51
*/
public class TestGetDownLoadUrl {
/**
* 查询转账明细电子回单受理结果API
*
* @throws URISyntaxException
* @throws IOException
*/
public static void getDownLoadUrl() throws URISyntaxException, IOException {
CloseableHttpClient httpClient = WeChatClient.getClient();
String url = "https://api.mch.weixin.qq.com/v3/transfer/bill-receipt/batch016";
URIBuilder uriBuilder = new URIBuilder(url);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
System.out.println("返回结果:" + bodyAsString);
} finally {
response.close();
}
}
public static void main(String[] args) throws Exception {
/**查询转账明细电子回单受理结果API*/
getDownLoadUrl();
}
}
步骤3:
下载电子回单API
开发文档-微信支付批量转账到零钱
代码如下:
import Util.DownLoadBillUtil;
import cn.hutool.core.io.FileUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import java.io.File;
import java.io.InputStream;
import java.security.PrivateKey;
/**
* @Author zkinghao
* @Date 2022/1/11 19:17
*/
public class TestDownLoadReceipts {
public static void billDownLoad3() throws Exception {
File file = new File("商户私钥文件");
InputStream privateKeyInput = FileUtil.getInputStream(file);
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKeyInput);
DownLoadBillUtil downLoadBillUtil = new DownLoadBillUtil("商户号",
"平台证书序列号", merchantPrivateKey);
//下载地址
String url = "https://api.mch.weixin.qq.com/v3/transferdownload/signfile?token=A9TMeNMBAAABAA1AAACq6YGHyqm650A_n2vdYSAAAAArJppkbNTL7ba83Qu52eBlrYb12LXyYf_BuqLxJtMcKrjwb-KVl_NqfzeKUa_OP_b1QRkq2IRuOkoX8N-uUT6nCZ0DKF_86JLOuYFFpQ6uxFgZQsItXJwAN-pwO0MUOc1yHYAVkHmqTK2DXEIu10GHprm3YZcuhlS59niwuLYx7Vp5cKZsm-MIuuTyb4VEgq9sGQ";
DownLoadBillUtil.writeToLocal("E://80.pdf", downLoadBillUtil.downloadBill(url));
System.out.printf("返回成功");
}
public static void main(String[] args) throws Exception{
billDownLoad3();
}
}
下载工具类:
package Util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.UUID;
/**
* @Author zkinghao
* @Date 2022/1/11 17:41
*/
public class DownLoadBillUtil {
/**
* 商户号
*/
private String merchantId;
/**
* 商户序列号
*/
private String certificateSerialNo;
/**
* 商户私钥
*/
private PrivateKey privateKey;
public DownLoadBillUtil(String merchantId, String certificateSerialNo, PrivateKey privateKey) {
this.merchantId = merchantId;
this.certificateSerialNo = certificateSerialNo;
this.privateKey = privateKey;
}
public InputStream downloadBill(String downloadUrl) throws Exception {
String timestamp = String.valueOf(System.currentTimeMillis());
String nonceStr = UUID.randomUUID().toString().replace("-", "");
;
HttpGet httpGet = new HttpGet(downloadUrl);
String path = httpGet.getURI().getPath();
String canonicalUrl = httpGet.getURI().getQuery();
if (canonicalUrl != null) {
path += "?" + canonicalUrl;
}
String billSign = this.createBillSign(nonceStr, timestamp, path);
StringBuilder sb = new StringBuilder("WECHATPAY2-SHA256-RSA2048 mchid=").append("\"").append(this.merchantId).append("\",");
sb.append("serial_no=").append("\"").append(this.certificateSerialNo).append("\",");
sb.append("nonce_str=").append("\"").append(nonceStr).append("\",");
sb.append("timestamp=").append("\"").append(timestamp).append("\",");
sb.append("signature=").append("\"").append(billSign).append("\"");
String auth = sb.toString();
HttpResponse execute = HttpRequest.get(downloadUrl).auth(auth).execute();
return execute.bodyStream();
}
public String createBillSign(String nonceStr, String timestamp, String download) throws Exception {
String plain_text = "GET" + "\n"
+ download + "\n"
+ timestamp + "\n"
+ nonceStr + "\n";
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(this.privateKey);
sign.update(plain_text.getBytes("utf-8"));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 将InputStream写入本地文件
*
* @param destination 写入本地目录
* @param input 输入流
* @throws IOException
*/
public static void writeToLocal(String destination, InputStream input)
throws IOException {
int index;
byte[] bytes = new byte[1024];
FileOutputStream downloadFile = new FileOutputStream(destination);
while ((index = input.read(bytes)) != -1) {
downloadFile.write(bytes, 0, index);
downloadFile.flush();
}
downloadFile.close();
input.close();
}
}
以上就是我个人写的微信支付V3版本的一些Demo,希望能帮助到未来要对接的人,让你们少走一些弯路,少踩一些坑。
让写代码的过程可以简单些。
不喜勿喷。