APP下单API
接口说明
适用对象: 直连商户
请求URL:https://api.mch.weixin.qq.com/v3/pay/transactions/app
请求方式:POST
- pom文件包
com.github.wechatpay-apiv3 wechatpay-apache-httpclient 0.2.2
- 返回包装实体类实体类
public class AppOrderApi {
private String appId; //应用ID appId
private String mchId; //商户号
private String nonceStr; //随机字符串
private String sign; //签名
private String prepay_id; //预支付交易会话ID
private String time; //时间戳
}
- 工具类
public class WxPayHandler{
public AppOrderApi createAppOrderAPI(PayOrder order, String appId) {
log.info("===info=== wx pay createAppOrderAPI unionId: {}, paymentId: {}", order.getUnionId(), order.getPaymentId());
try {
JSONObject params = new JSONObject();
params.put("appid", appId); // 应用ID
params.put("mchid", MCH_ID); // 直连商户号
params.put("description", order.getGoodsTitle()); //商品描述
params.put("out_trade_no", order.getUnionId()); // 商户系统内部订单号,只能是数字、大小写字母_-*且在同一个商户号下唯一
JSONObject jsonObject = new JSONObject();
jsonObject.put("total", order.getAmount());
params.put("amount", jsonObject); //订单金额信息
params.put("notify_url", PAY_CALLBACK_URL); // 通知URL必须为直接可访问的URL,不允许携带查询串,要求必须为https地址。
AppOrderApi result = CreateOrder(params.toJSONString(),order);
// 密钥加密方式【最后一个也要有换行符号】:
//应用id
//时间戳以秒为单位
//随机字符串
//下单后的Prepay_id
result.setSign(sign((appId + "n" + (order.getAddTime().getTime() / 1000) + "n" + order.getUnionId() + "n" + result.getPrepay_id() + "n").getBytes())); // 封装签名
return result;
} catch (Exception e) {
log.error("===error=== wx pay executeQuery error msg: {}", e.getMessage());
return null;
}
}
public AppOrderApi CreateOrder(String reqdata,PayOrder order) throws Exception {
// MCH_ID:商户号id,SERIAL_NUMBER:证书序列号,CERTICATE_PRIVATE_PATH:私钥地址,SECRET_KEY_V3:秘钥
CloseableHttpClient httpClient = CloseableHttpClientUtil.init(MCH_ID, SERIAL_NUMBER, CERTICATE_PRIVATE_PATH, SECRET_KEY_V3);
//请求URL
HttpPost httpPost = new HttpPost(WxConstants.APP_ORDER_API);
// 请求body参数
StringEntity entity = new StringEntity(reqdata, "UTF-8");
log.info("请求参数:" + reqdata);
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
AppOrderApi wxRefundInfo = JSONObject.parseObject(EntityUtils.toString(response.getEntity()), AppOrderApi.class);
return wxRefundInfo;
} else if (statusCode == 204) { //处理成功,无返回Body
System.out.println("success");
} else {
System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
throw new IOException("request failed");
}
} finally {
response.close();
}
return null;
}
// SHA256加密
String sign(byte[] message) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(CERTICATE_PRIVATE_PATH)); // 私钥地址
sign.update(message);
return base64.getEncoder().encodeToString(sign.sign());
}
// 获取私钥
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(base64.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
//封装Client
public class CloseableHttpClientUtil {
public static CloseableHttpClient init(String merchantId, String serialNumber, String filename, String secret_key) {
try {
PrivateKey privateKey = WxPayHandler.getPrivateKey(filename);
// 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(merchantId, new PrivateKeySigner(serialNumber, privateKey)), secret_key.getBytes(StandardCharsets.UTF_8));
return WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, serialNumber, privateKey)
.withValidator(new WechatPay2Validator(verifier)).build();
} catch (Exception e) {
throw new RuntimeException("初始化 CloseableHttpClient 失败", e);
}
}
}
//获取私钥
public class PrivateKeyUtils {
public static PrivateKey getPrivateKey(String filename) {
// 加载商户私钥(privateKey:私钥字符串)
try {
return PemUtil.loadPrivateKey(new ByteArrayInputStream(analysisPem(filename).getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
throw new RuntimeException("加载 PrivateKey 失败", e);
}
}
private static String analysisPem(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\s+", "");
return privateKey;
} catch (Exception e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
}
}
}



