@Service
public class CcbUtil {
/**
* 日志管理器
*/
private static final Logger LOGGER = LoggerFactory.getLogger(CcbUtil.class);
/**
* 线上支付
*
* @param orderNo 订单号
* @param openId 微信openId
* @param proinfo 商品类型信息
* @param payment 付款金额 单位:元,精确到两位小数
* @return 支付参数
*/
public static Map<String, String> pay(String orderNo, String openId, String proinfo, BigDecimal payment) {
// 按指定顺序处理参数,进行签名
Map<String, Object> map = new LinkedHashMap<>();
map.put("appid", WeChatApiUtil.APP_ID);//应用ID
map.put("mchid", WeChatApiUtil.MCH_ID);//直连商户号
map.put("time_expire", TimeUtil.timeStampToRfc3339(System.currentTimeMillis() + 300000)); // 订单超时时间,yyyyMMddhhmmss
map.put("description", proinfo);//商品描述
map.put("out_trade_no", orderNo);//商户订单号
map.put("notify_url", WeChatApiUtil.NOTIFY_URL);//通知地址
Map<String, Object> payMap = new HashMap<>();
payMap.put("total", payment.multiply(BigDecimal.valueOf(100)).intValue());
payMap.put("currency", "CNY");
map.put("amount", payMap); // 总金额 单位为分
Map<String, Object> payerMap = new HashMap<>();
payerMap.put("openid", openId);
map.put("payer", payerMap);//用户标识
// 处理签名
String token = getToken("POST", HttpUrl.parse(WeChatApiUtil.URL), JsonUtil.toJsonStr(map), orderNo);
// 请求头
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "WECHATPAY2-SHA256-RSA2048 " + token);
headers.put("Accept", "application/json");
LOGGER.info("微信支付发起请求:\n报文={}\n请求头={}", JsonUtil.toJsonStr(map), JsonUtil.toJsonStr(headers));
// 请求
String res = OkHttpUtil.postForString(MediaType.APPLICATION_JSON_VALUE, WeChatApiUtil.URL, JsonUtil.toJsonStr(map), headers);
LOGGER.info("微信支付响应信息:\n报文={}", res);
if (!CommonUtil.isEmpty(res)) {
// 解析
Map<String, String> resMap = JsonUtil.toMap(res);
// 判断是否包含支付要素
if (resMap.containsKey("prepay_id")) {
String prepayId = resMap.get("prepay_id");
// 组装支付要素
Map<String, String> payInfo = new HashMap<>();
payInfo.put("appId", WeChatApiUtil.APP_ID);
payInfo.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payInfo.put("nonceStr", RandomStringUtils.randomAlphanumeric(32));
payInfo.put("package", "prepay_id=" + prepayId);
payInfo.put("signType", "RSA");
// 生成签名
String content = String.format("%s\n%s\n%s\n%s\n", WeChatApiUtil.APP_ID, payInfo.get("timeStamp"), payInfo.get("nonceStr"), payInfo.get("package"));
payInfo.put("paySign", sign(content));
return payInfo;
}
throw new QualityException(ErrorMsg.PAYMENT_FAILED);
}
throw new QualityException(ErrorMsg.PAYMENT_FAILED);
}
private static String getToken(String method, HttpUrl url, String body, String orderNo) {
String nonceStr = RandomStringUtils.randomAlphanumeric(32);
long timestamp = System.currentTimeMillis() / 1000;
String signature = sign(buildMessage(method, url, timestamp, nonceStr, body));
return "mchid=\"" + WeChatApiUtil.MCH_ID + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + WeChatApiUtil.SERIAL_NO + "\","
+ "signature=\"" + signature + "\"";
}
private static String sign(String message) {
String sign = "";
try {
String content = new String(Files.readAllBytes(Paths.get(WeChatApiUtil.MCH_PRIVATE_KEY_PATH)), "utf-8");
// byte[] keyBytes = IOUtils.toByteArray(new FileInputStream());
// PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
// KeyFactory kf = KeyFactory.getInstance("RSA");
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey newPrivateKey = kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
Signature signature;
signature = Signature.getInstance("SHA256withRSA");
signature.initSign(newPrivateKey);
signature.update(message.getBytes("utf-8"));
sign = Base64.getEncoder().encodeToString(signature.sign());
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException | IOException | InvalidKeySpecException e) {
LOGGER.error("签名错误:{}", e);
e.printStackTrace();
}
return sign;
}
private static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.encodedPath();
if (url.encodedQuery() != null) {
canonicalUrl += "?" + url.encodedQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
/**
* 取消订单
*/
public static void cancel(String outTradeNo) {
// 按指定顺序处理参数,进行签名
Map<String, String> map = new LinkedHashMap<>();
map.put("mchid", WeChatApiUtil.MCH_ID);//直连商户号
// 处理签名
String token = getToken("POST", HttpUrl.parse(WeChatApiUtil.CANCEL(outTradeNo)), JsonUtil.toJsonStr(map), outTradeNo);
// 请求头
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "WECHATPAY2-SHA256-RSA2048 " + token);
headers.put("Accept", "application/json");
LOGGER.info("微信支付发起请求:\n报文={}\n请求头={}", JsonUtil.toJsonStr(map), JsonUtil.toJsonStr(headers));
OkHttpUtil.postForString(MediaType.APPLICATION_JSON_VALUE, WeChatApiUtil.CANCEL(outTradeNo), JsonUtil.toJsonStr(map), headers);
}
}
public class WeChatApiUtil {
public static final String APP_ID = "wx1f1a699111111111122";
public static final String APP_SECRET = "fb411117809111111df49c18691940";
public static final String WX_OPENID_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
public static final String WX_USERINFO_URL = "https://api.weixin.qq.com/wxa/getpaidunionid?access_token=%s&openid=%s";
public static final String WX_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
public static final String MCH_ID = "1500000001";
public static final String API_V3_KEY = "test201900000n0712";
public static final String SERIAL_NO = "12021555CDD4BC54045151245A3A8CE";
public static final String URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
public static final String NOTIFY_URL = "https://www.test.com/test/api/pay-notify/wx";
public static final String MCH_PRIVATE_KEY_PATH = "/data/test/apiclient_key.pem";
//public static final String MCH_PRIVATE_KEY_PATH = "F:\\项目代码\\test\\apiclient_key.pem";
public static final String CANCEL(String out_trade_no) {
return "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/" + out_trade_no + "/close";
}
}