接入微信APP支付准备:
1:申请商户号(mchid),入驻微信商户平台
2:申请APPID,开通微信开放平台,创建移动应用(第三步)
3:绑定APPID和MCHID,开放平台应用关联商户平台。可关系多个,多对多关系 获取APPID和商户号
4:配置APIkey,微信商户平台,账号中心=》AIP安全 配置APIV3秘钥和申请API证书 申请API证书需下载证书工具(申请API证书步骤里有工具安装包)
5:没了,有了APPID、商户号 、商户证书序列号、V3Api密钥、商户私钥。就开始上手吧。
6:导入依赖
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.10</version>
</dependency>
1.配置支付信息,工具类
/**
* 微信支付回调body实体
* @author liaozan8888@163.com
*/
@Data
public class SuccessCallBackObj {
private String summary;
private String event_type;
private String create_time;
private String resource_type;
private String id;
private Resource resource;
}
/**
* 微信回调Resource 实体
* @author liaozan8888@163.com
*/
@Data
public class Resource implements Serializable {
private String original_type;
private String algorithm;
private String ciphertext;
private String associated_data;
private String nonce;
}
/**
* 支付参数配置
* @author liaozan8888@163.com
*/
public interface PayConstants {
String NOTIFY_URL = "XXXX"; //回调地址
String MCH_ID = "XXXX"; //商户号
String MCH_SERIAL_NO = "XXXX"; //商户证书序列号
String API_3KEY = "XXXX"; //Api密钥
String APP_ID = "XXXX"; //ApId
String PACKAGE = "Sign=WXPay"; //签名国定字符串
String privateKey = "XXXX";//商户私钥
}
/**
* 回调签名配置
* @author liaozan8888@163.com
*/
public class AesUtil {
static final int KEY_LENGTH_BYTE = 32;
static final int TAG_LENGTH_BIT = 128;
private final byte[] aesKey;
public AesUtil(byte[] key) {
if (key.length != KEY_LENGTH_BYTE) {
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
}
this.aesKey = key;
}
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);
}
}
}
2.调起APP支付
/**
* 下单工具类
* @author liaozan8888@163.com
*/
public class PayUtil {
private CloseableHttpClient httpClient;
private CertificatesManager certificatesManager;
private Verifier verifier;
/**
* App下单 具体下单场景查看官方文档
*
* @param total
* @param description
* @return
* @throws Exception
*/
public String requestwxChatPay(String orderSn, int total, String description) throws Exception {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.privateKey.getBytes("utf-8")));
// 获取证书管理器实例
certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(PayConstants.MCH_ID, new WechatPay2Credentials(PayConstants.MCH_ID,
new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_3KEY.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
verifier = certificatesManager.getVerifier(PayConstants.MCH_ID);
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(PayConstants.MCH_ID, PayConstants.MCH_SERIAL_NO, merchantPrivateKey)
.withValidator(new WechatPay2Validator(certificatesManager.getVerifier(PayConstants.MCH_ID)))
.build();
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
//组合请求参数JSON格式
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", PayConstants.MCH_ID)
.put("appid", PayConstants.APP_ID)
.put("notify_url", PayConstants.NOTIFY_URL + "notify")
.put("description", description)
.put("out_trade_no", orderSn);
rootNode.putObject("amount")
.put("total", total)
.put("currency", "CNY");
try {
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
//获取预支付ID
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
//微信成功响应
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
//时间戳
String timestamp = System.currentTimeMillis() / 1000 + "";
//随机字符串
String nonce = RandomUtil.randomString(32);
StringBuilder builder = new StringBuilder();
// Appid
builder.append(PayConstants.APP_ID).append("\n");
// 时间戳
builder.append(timestamp).append("\n");
// 随机字符串
builder.append(nonce).append("\n");
JsonNode jsonNode = objectMapper.readTree(bodyAsString);
// 预支付会话ID
builder.append(jsonNode.get("prepay_id").textValue()).append("\n");
//获取签名
String sign = this.sign(builder.toString().getBytes("utf-8"), merchantPrivateKey);
JSONObject jsonMap = new JSONObject();
jsonMap.put("noncestr", nonce);
jsonMap.put("timestamp", timestamp);
jsonMap.put("prepayid", jsonNode.get("prepay_id").textValue());
jsonMap.put("sign", sign);
jsonMap.put("package", PayConstants.PACKAGE);
jsonMap.put("appid", PayConstants.APP_ID);
jsonMap.put("partnerid", PayConstants.MCH_ID);
return jsonMap.toJSONString();//响应签名数据,前端拿着响应数据调起微信SDK
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 计算签名
*
* @param message
* @param yourPrivateKey
* @return
*/
private String sign(byte[] message, PrivateKey yourPrivateKey) {
try {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(yourPrivateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (java.security.InvalidKeyException e) {
e.printStackTrace();
}
return "";
}
/**
* 回调验证签名
*
* @param serial
* @param message
* @param signatrue
* @return
*/
public static boolean signVerify(String serial, String message, String signatrue) {
CertificatesManager certificatesManager = null;
Verifier verifier = null;
try {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(PayConstants.privateKey.getBytes("utf-8")));
// 获取证书管理器实例
certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(PayConstants.MCH_ID, new WechatPay2Credentials(PayConstants.MCH_ID,
new PrivateKeySigner(PayConstants.MCH_SERIAL_NO, merchantPrivateKey)),
PayConstants.API_3KEY.getBytes(StandardCharsets.UTF_8));
return verifier.verify(serial, message.getBytes("utf-8"), signatrue);
} catch (Exception e) {
}
return false;
}
/**
* 回调通知解密
*
* @param successCallBackObj
* @return
*/
public static String decryptToString(SuccessCallBackObj successCallBackObj) {
if (successCallBackObj.getResource().getAssociated_data() == null) {
return null;
}
//附加数据
String associated_data = successCallBackObj.getResource().getAssociated_data();
//随机串
String nonce = successCallBackObj.getResource().getNonce();
//数据密文
String ciphertext = successCallBackObj.getResource().getCiphertext();
//解密Resource
AesUtil aesUtil = new AesUtil(PayConstants.API_3KEY.getBytes());
try {
return aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}