常规操作和接口说明看微信文档就好,这里记录一下构造httpClient的参数说明,由于我是半路接手的,有些参数的来源是推测来的,实际用的时候需要甄别,还有微信文档中对于同一个变量的称呼不是完全统一,这都给对接的时候挖了坑,需要注意
参数 | 说明 | 获取方式 |
mchId | 商户号 | 应该是开通微信支付以后,在管理端的商户信息 / 账户信息中可以查询微信支付商户号 |
mchSerialNo | 商户证书序列号 | 在管理端的API安全中有申请API证书,一顿操作之后可以得到证书序列号 |
privateKey | 商户私钥 / 商户API私钥 | 这部分我拿到的是前辈留下的证书文件,直接拿来用了,推测数据应该是上一个参数申请API证书的时候得到的,具体的操作就查看微信文档吧 |
certificate | 微信支付平台证书 | 这个参数有两种获取方式,一种是接口,一种是通过官方提供的一个jar包,首次下载证书,可以使用微信支付提供的证书下载工具。同时这个参数是需要更新的,但目前我还没做更新逻辑,后续补充 |
有了这几个参数,就可以构造httpClient了
参考这个项目:https://github.com/wechatpay-apiv3/CertificateDownloader
package com.genesysinfo.umss.core.modules.wxpay.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.genesysinfo.umss.core.common.constant.CommonConstant;
import com.genesysinfo.umss.core.modules.wxpay.dto.NativeOrderDTO;
import com.genesysinfo.umss.core.modules.wxpay.dto.SUCVDTO;
import com.genesysinfo.umss.core.modules.wxpay.dto.WXPlatCertificate;
import com.genesysinfo.umss.core.modules.wxpay.service.WxPayService;
import com.genesysinfo.umss.core.modules.wxpay.utils.AesUtil;
import com.genesysinfo.umss.core.modules.wxpay.utils.QRCodeUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.ScheduledUpdateCertificatesVerifier;
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 io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.stream.Collectors;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* 微信支付平台证书更新指引 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/zsgxguide.shtml
* @author coco
* @date 2021/12/29
*/
@Api(tags = "微信支付")
@RestController
@RequestMapping("/wxpay")
public class WxPayController {
@Autowired
private WxPayService wxPayService;
// 1.创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
@GetMapping("/setup")
public ResponseEntity setup() throws IOException, URISyntaxException {
// 你的商户私钥
String privateKey = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("static/apiclient_key.pem").toURI())), "utf-8");
// 商户API证书
//String certificate = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("static/apiclient_cert.pem").toURI())), "utf-8");
// 微信支付平台证书
String certificate = new String(Files.readAllBytes(Paths.get(ClassLoader.getSystemResource("static/wechatpay_66666666666666.pem").toURI())), "utf-8");
Map map = new HashMap<>();
// 商户号
map.put("mchId","000000000000000000000");
// 商户证书序列号
map.put("mchSerialNo","1111111111111111111111111111111111");
map.put("privateKey",privateKey);
map.put("certificate",certificate);
return new ResponseEntity<>(map, HttpStatus.OK);
}
/**
* 生成二维码
* @param content
* @param response
*/
@ApiOperation(value = "生成二维码")
@GetMapping("/genQrcode")
public void genQrcode(@RequestParam(name = "content") String content, HttpServletResponse response){
try {
QRCodeUtil.createCodeToOutputStream(content, response.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取平台证书列表
* @param param
* @return
* @throws IOException
* @throws URISyntaxException
*/
@ApiOperation(value = "获取平台证书列表", notes = "https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay5_1.shtml")
@PostMapping("/getPlatCertList")
public ResponseEntity> getPlatCertList(@Validated @RequestBody SUCVDTO param) throws IOException, URISyntaxException {
CloseableHttpClient httpClient;
ScheduledUpdateCertificatesVerifier verifier;
// 商户号
String mchId = param.getMchId();
// 商户证书序列号
String mchSerialNo = param.getMchSerialNo();
// API V3密钥
String apiV3Key = param.getApiV3Key();
// 你的商户私钥
String privateKey = param.getPrivateKey();
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
// 使用定时更新的签名验证器,不需要传入证书
verifier = new ScheduledUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
URIBuilder uriBuilder = new URIBuilder(CommonConstant.URL_WX_CERTIFICATES);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
return new ResponseEntity<>(JSONObject.parseObject(EntityUtils.toString(entity)), HttpStatus.OK);
} finally {
response.close();
httpClient.close();
}
}
/**
* 解密平台证书
* @param wxPlatCertificate
* @return
* @throws IOException
* @throws URISyntaxException
*/
@ApiOperation(value = "解密平台证书", notes = "https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_2.shtml")
@PostMapping("/decryptPlatCert")
public ResponseEntity> getCertList(@Validated @RequestBody WXPlatCertificate wxPlatCertificate) throws IOException {
// API V3密钥
String apiV3Key = wxPlatCertificate.getApiV3Key();
try {
// 解密返回数据,得到证书
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
String publicKey = aesUtil.decryptToString(wxPlatCertificate.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
wxPlatCertificate.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8),
wxPlatCertificate.getEncrypt_certificate().getCiphertext());
return new ResponseEntity<>(publicKey, HttpStatus.OK);
} catch (GeneralSecurityException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_GATEWAY);
}
}
/**
* 获取解密的启用时间最晚的平台证书
* @param param
* @return
* @throws IOException
* @throws URISyntaxException
*/
@ApiOperation(value = "获取解密的启用时间最晚的平台证书")
@PostMapping("/getDecryptedPlatCert")
public ResponseEntity> getDecryptedPlatCert(@Validated @RequestBody SUCVDTO param) throws IOException, URISyntaxException {
CloseableHttpClient httpClient;
ScheduledUpdateCertificatesVerifier verifier;
// 商户号
String mchId = param.getMchId();
// 商户证书序列号
String mchSerialNo = param.getMchSerialNo();
// API V3密钥
String apiV3Key = param.getApiV3Key();
// 你的商户私钥
String privateKey = param.getPrivateKey();
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
// 使用定时更新的签名验证器,不需要传入证书
verifier = new ScheduledUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
URIBuilder uriBuilder = new URIBuilder(CommonConstant.URL_WX_CERTIFICATES);
HttpGet httpGet = new HttpGet(uriBuilder.build());
httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
JSONObject js = JSONObject.parseObject(EntityUtils.toString(entity));
List respCertList = JSONArray.parseArray(js.get("data").toString(),WXPlatCertificate.class);
// 选用证书启用时间最晚的证书
List wx = respCertList.stream()
.sorted(Comparator.comparing((WXPlatCertificate w) -> w.getEffective_time()))
.collect(Collectors.toList());
WXPlatCertificate wxPlatCertificate = wx.get(0);
// 解密返回数据,得到证书
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
String publicKey = aesUtil.decryptToString(wxPlatCertificate.getEncrypt_certificate().getAssociated_data().getBytes(StandardCharsets.UTF_8),
wxPlatCertificate.getEncrypt_certificate().getNonce().getBytes(StandardCharsets.UTF_8),
wxPlatCertificate.getEncrypt_certificate().getCiphertext());
return new ResponseEntity<>(publicKey, HttpStatus.OK);
} catch (GeneralSecurityException e) {
int statusCode = response.getStatusLine().getStatusCode();
MultiValueMap params = new LinkedMultiValueMap<>();
for (Header header : response.getAllHeaders()) {
params.put(header.getName(), Collections.singletonList(header.getValue()));
}
return new ResponseEntity<>(e.getMessage(), params, statusCode);
} finally {
response.close();
httpClient.close();
}
}
/**
* 发起native下单
* @param dto
* @return
* @throws Exception
*/
@ApiOperation(value = "发起native下单", notes = "https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml")
@PostMapping("/createNativeOrder")
public ResponseEntity> createNativeOrder(@Validated @RequestBody NativeOrderDTO dto) throws Exception{
CloseableHttpClient httpClient;
if (StringUtils.isNotEmpty(dto.getWxPayBase().getApiV3Key())){
httpClient = createHttpClientWithVerifier(dto.getWxPayBase().getMchId(),
dto.getWxPayBase().getMchSerialNo(),
dto.getWxPayBase().getPrivateKey(),
dto.getWxPayBase().getApiV3Key());
}else{
httpClient = createHttpClientWithCertificate(dto.getWxPayBase().getMchId(),
dto.getWxPayBase().getMchSerialNo(),
dto.getWxPayBase().getPrivateKey(),
dto.getWxPayBase().getCertificate());
}
String bodyStr = filterJavaBean(dto);
HttpPost httpPost = new HttpPost(CommonConstant.URL_WX_PAY_NATIVE);
//转两次过滤掉一些为null的参数
StringEntity entity = new StringEntity(JSONObject.toJSONString(JSONObject.parseObject(bodyStr)),"utf-8");
entity.setContentType(CommonConstant.JSON);
httpPost.setEntity(entity);
httpPost.setHeader("Accept", CommonConstant.JSON);
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
//处理成功
if (statusCode == 200) {
return new ResponseEntity<>(JSONObject.parseObject(EntityUtils.toString(response.getEntity())), HttpStatus.OK);
//处理成功,无返回Body
} else if (statusCode == 204) {
return new ResponseEntity<>(new JSONObject(), HttpStatus.NO_CONTENT);
} else {
MultiValueMap params = new LinkedMultiValueMap<>();
for (Header header : response.getAllHeaders()) {
params.put(header.getName(), Collections.singletonList(header.getValue()));
}
return new ResponseEntity<>(JSONObject.parseObject(EntityUtils.toString(response.getEntity())), params, statusCode);
}
} finally {
response.close();
httpClient.close();
}
}
/**
* 过滤参数
* @param dto
* @param
* @return
* @throws JsonProcessingException
*/
private String filterJavaBean(T dto) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode jsonNodes = objectMapper.valueToTree(dto);
jsonNodes.remove("wxPayBase");
return objectMapper.writeValueAsString(jsonNodes);
}
/**
* 创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
* @param mchId
* @param mchSerialNo
* @param privateKey
* @param certificate
* @return
* @throws IOException
* @throws URISyntaxException
*/
private CloseableHttpClient createHttpClientWithCertificate(String mchId, String mchSerialNo,String privateKey, String certificate) {
CloseableHttpClient httpClient;
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
new ByteArrayInputStream(certificate.getBytes(StandardCharsets.UTF_8)));
ArrayList listCertificates = new ArrayList<>();
listCertificates.add(wechatPayCertificate);
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withWechatPay(listCertificates)
.build();
return httpClient;
}
/**
* 创建加载商户私钥、加载平台证书、初始化httpClient的通用方法
* 使用定时更新的签名验证器,不需要传入证书
* @param mchId
* @param mchSerialNo
* @param privateKey
* @param apiV3Key
* @return
* @throws IOException
* @throws URISyntaxException
*/
private CloseableHttpClient createHttpClientWithVerifier(String mchId, String mchSerialNo,String privateKey, String apiV3Key) {
CloseableHttpClient httpClient;
ScheduledUpdateCertificatesVerifier verifier;
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
// 使用定时更新的签名验证器,不需要传入证书
verifier = new ScheduledUpdateCertificatesVerifier(
new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)),
apiV3Key.getBytes(StandardCharsets.UTF_8));
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier))
.build();
return httpClient;
}
}
记一次“微信支付开发API V3”接口调用,解决“应答的微信支付签名验证失败”_fyh60318647的博客-CSDN博客_应答的微信支付签名验证失败https://blog.csdn.net/fyh60318647/article/details/112689022