按照惯例,记个笔记,网上也很多介绍v3和v2的区别,这里不做介绍,选择v3完全是因为restful风格和josn的数据格式,平时比较熟悉。
准备工作之证书准备
前三个api证书由微信提供详细请看:pay.weixin.qq.com/wiki/doc/ap…
第四个证书是由公钥和私钥生成的平台证书,后续有需要,以下是jsApiPay支付方式
<dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-WxPay</artifactId>
<version>2.8.0</version>
</dependency>
V3:
appId: ****
mchId: ****
apiKey3: ****
keyPath:/cert/apiclient_key.pem
certPath: /cert/apiclient_cert.pem
certP12Path: /cert/apiclient_cert.p12
platformCertPath: /cert/platform_cert.pem
domain: ***
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "v3")
public class WxPayV3Properties {
private String appId;
private String mchId;
private String apiKey3;
private String keyPath;
private String certPath;
private String platformCertPath;
private String domain;
}
private String getSerialNumber() {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wxPayV3Properties.getCertPath()));
return certificate.getSerialNumber().toString(16).toUpperCase();
}
生成平台证书
public void v3Get() {
// 获取平台证书列表
try {
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.GET,
WxDomain.CHINA.toString(),
WxApiType.GET_CERTIFICATES.toString(),
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
""
);
/* String timestamp = response.getHeader("Wechatpay-Timestamp");
String nonceStr = response.getHeader("Wechatpay-Nonce");
String signature = response.getHeader("Wechatpay-Signature");*/
String serialNumber = response.getHeader("Wechatpay-Serial");
String body = response.getBody();
int status = response.getStatus();
log.info("serialNumber: {}", serialNumber);
log.info("status: {}", status);
log.info("body: {}", body);
int isOk = 200;
if (status == isOk) {
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONArray dataArray = jsonObject.getJSONArray("data");
// 默认认为只有一个平台证书
JSONObject encryptObject = dataArray.getJSONObject(0);
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
String associatedData = encryptCertificate.getStr("associated_data");
String cipherText = encryptCertificate.getStr("ciphertext");
String nonce = encryptCertificate.getStr("nonce");
String serialNo = encryptObject.getStr("serial_no");
final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, wxPayV3Properties.getPlatformCertPath());
log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
}
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
log.info("verifySignature:" + verifySignature);
} catch (Exception e) {
e.printStackTrace();
}
}
private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
try {
AesUtil aesUtil = new AesUtil(wxPayV3Properties.getApiKey3().getBytes(StandardCharsets.UTF_8));
// 平台证书密文解密
String publicKey = aesUtil.decryptToString(
associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
cipherText
);
// 保存证书
log.info("获取证书key:{},保存路径platformCert:{}", publicKey, certPath);
//将生成的证书写入指定路径,文件名为:cert.pem
FileOutputStream fos = new FileOutputStream(certPath);
fos.write(publicKey.getBytes());
fos.close();
// 获取平台证书序列号
X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
return certificate.getSerialNumber().toString(16).toUpperCase();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
微信支付
微信支付正常流程是发起支付获取下单参数,由微信调起支付接口
public Map<String, String> createOrder() throws Exception {
Map<String, String> map =new HashMap<>();
String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
String openId="";
String total="1";
//系统内唯一订单号(也可随机id)
String outTradeNo="123456";
Map<String,Object> model= requestWxPayParam(openId,total,outTradeNo, timeExpire);
log.info("统一下单参数 {}", JSONUtil.toJsonStr(model));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.POST,
WxDomain.CHINA.toString(),
WxApiType.JS_API_PAY.toString(),
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
JSONUtil.toJsonStr(model));
log.info("统一下单响应 {}", response);
if (response.getStatus() == 200) {
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
if (verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
String prepayId = jsonObject.getStr("prepay_id");
map = WxPayKit.jsApiCreateSign(wxPayV3Properties.getAppId(), prepayId,wxPayV3Properties.getKeyPath());
}
}
return map;
}
private Map<String, Object> requestWxPayParam(String openId,String total,String outTradeNo, String timeExpire) {
Map<String, Object> data = new HashMap<>();
Map<String, String> user = new HashMap<>();
user.put("openid", openId);
Map<String, Object> fee = new HashMap<>();
//分为单位
fee.put("total", new BigDecimal(total).multiply(new BigDecimal(100)).longValue());
data.put("appid", wxPayV3Properties.getAppId());
data.put("mchid", wxPayV3Properties.getMchId());
data.put("description", "测试");
data.put("out_trade_no", outTradeNo);
data.put("amount", fee);
//默认2小时
data.put("time_expire", timeExpire);
//回调地址
data.put("notify_url", wxPayV3Properties.getDomain().concat("/pay/callBackNotify"));
data.put("payer", user);
return data;
}
public void callBack(HttpServletRequest request, HttpServletResponse response) {
log.info("======微信支付回调======");
Map<String, String> map = new HashMap<>(12);
try {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
String platformCertPath =wxPayV3Properties.getPlatformCertPath();
String mckKey=wxPayV3Properties.getApiKey3();
//出于安全考虑,验证是否是微信回调
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,mckKey, platformCertPath);
log.info("支付通知明文 {}", plainText);
//逻辑处理
//回复微信
if (StrUtil.isNotEmpty(plainText)) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
log.error("微信回调失败:{}",e.getMessage());
}
}
微信查单
public String queryOrderByNo(String outTradeNo) {
try {
String url = String.format(WxApiType.ORDER_QUERY_BY_NO.getType(), outTradeNo);
url =url.concat("?mchid=").concat(wxPayV3Properties.getMchId());
log.info("查询订单url:{}", url);
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.GET,
WxDomain.CHINA.toString(),
url,
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
""
);
log.info("查询订单响应:{}", response);
if (response.getStatus() == 200) {
//平台证书
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
if (verifySignature) {
String body = response.getBody();
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONObject amountObject=JSONUtil.parseObj(jsonObject.get("amount"));
log.info("订单金额结果:{}",amountObject.get("total"));
log.info("订单响应结果:{}", jsonObject);
return body;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
微信关单
public String closeOrderByNo(String outTradeNo) throws Exception {
String url = String.format(WxApiType.CLOSE_ORDER_BY_NO.getType(), outTradeNo);
log.info("关闭订单url:{}", url);
Map<String,Object> model=new HashMap<>();
model.put("mchid",wxPayV3Properties.getMchId());
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.POST,
WxDomain.CHINA.toString(),
url,
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
JSONUtil.toJsonStr(model)
);
if (response.getStatus() == 204) {
//平台证书
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
if (verifySignature) {
return response.getBody();
}
}
return null;
}
微信退款
public String refund() throws Exception {
Map<String,Object> model=new HashMap<>();
//交易单号(支付的订单号)
model.put("out_trade_no","123456");
model.put("out_refund_no",WxPayKit.generateStr());
model.put("notify_url",wxPayV3Properties.getDomain().concat("/pay/callBackRefund"));
Map<String, Object> fee = new HashMap<>();
//退款(单位:分)
fee.put("refund", 1);
//订单总额(可发起多笔,最多50笔,详见微信官网)
fee.put("total",1);
fee.put("currency","CNY");
model.put("amount",fee);
log.info("微信订单退款参数 {}", JSONUtil.toJsonStr(model));
IJPayHttpResponse response = WxPayApi.v3(
RequestMethod.POST,
WxDomain.CHINA.toString(),
WxApiType.DOMESTIC_REFUNDS.toString(),
wxPayV3Properties.getMchId(),
getSerialNumber(),
null,
wxPayV3Properties.getKeyPath(),
JSONUtil.toJsonStr(model)
);
log.info("微信订单退款响应 {}", response);
if(response.getStatus()==200){
boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Properties.getPlatformCertPath());
log.info("verifySignature: {}", verifySignature);
if (verifySignature) {
return response.getBody();
}
}else{
throw new ServiceException("订单退款响应失败");
}
return null;
}
public void callBackRefund(HttpServletRequest request, HttpServletResponse response) {
log.info("收到微信退款回调");
Map<String, String> map = new HashMap<>(12);
try {
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String serialNo = request.getHeader("Wechatpay-Serial");
String signature = request.getHeader("Wechatpay-Signature");
log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
String result = HttpKit.readData(request);
String platformCertPath =wxPayV3Properties.getPlatformCertPath();
String mckKey=wxPayV3Properties.getApiKey3();
String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,mckKey, platformCertPath);
log.info("退款通知明文 {}", plainText);
//逻辑处理
//回复微信
if (StrUtil.isNotEmpty(plainText)) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
log.error("微信回调失败:{}",e.getMessage());
}
}
人不狠话不多,存个代码自己看