package cn.changeHappy.exchange.controller;
import cn.changeHappy.exchange.utils.WxPayConfig;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import io.jsonwebtoken.SignatureException;
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.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.management.openmbean.InvalidKeyException;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("api/wxPay")
public class WxPayController {
/**
* 支付
* @author: jie.wang
* @param postData
* @throws Exception
*/
@PostMapping("/wxPay")
public Map<String, Object> createWxOrder(@RequestBody String postData) throws Exception {
Map<String, Object> map = new HashMap();
JSONObject jsonObject = JSONObject.parseObject(postData);
map.put("appid", WxPayConfig.app_id);
map.put("mchid", WxPayConfig.mch_id);
map.put("description", jsonObject.get("desc"));
//生成个订单号放进去
map.put("out_trade_no", "11111111");
map.put("notify_url", WxPayConfig.notify_order_url);
Map<String, Object> amount = new HashMap();
amount.put("total", 1);
amount.put("currency", "CNY");
map.put("amount", amount);
Map<String, Object> payermap = new HashMap();
payermap.put("openid", jsonObject.get("openId"));
map.put("payer", payermap);
ObjectMapper objectMapper = new ObjectMapper();
String body = objectMapper.writeValueAsString(map);
Map<String, Object> stringObjectMap = null;
HashMap<String, Object> dataMap = null;
stringObjectMap = doPostWexin("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", body);
if(!stringObjectMap.isEmpty()){
//根据订单号
}
dataMap = getTokenJSAPI(WxPayConfig.app_id, String.valueOf(stringObjectMap.get("prepay_id")));
return dataMap;
}
/**
* 微信回调接口
* @author: jie.wang
* @param body
* @param request
* @return
*/
@PostMapping("pay/callback")
public Map orderPayCallback(@RequestBody Map body, HttpServletRequest request) {
System.out.println("1----------->微信支付回调开始");
Map<String, Object> result = new HashMap();
//获取微信支付回调的获取签名信息
String timestamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
ObjectMapper objectMapper = new ObjectMapper();
try {
// 解析报文体
String data = objectMapper.writeValueAsString(body);
String message = timestamp + "\n" + nonce + "\n" + data + "\n";
String sign = request.getHeader("Wechatpay-Signature");
//获取证书
String serialNo = request.getHeader("Wechatpay-Serial");
if (!WxPayConfig.certificateMap.containsKey(serialNo)) {
WxPayConfig.certificateMap = refreshCertificate();
}
X509Certificate x509Certificate = WxPayConfig.certificateMap.get(serialNo);
if (!WechatPayUtils.verify(x509Certificate, message.getBytes(), sign)) {
throw new IllegalArgumentException("微信支付签名验证失败:" + message);
}
Map<String, String> resource = (Map) body.get("resource");
// 报文解密
AesUtil aesUtil = new AesUtil(WxPayConfig.v3Key.getBytes());
//解密后json字符串
String decryptToString = aesUtil.decryptToString(
resource.get("associated_data").getBytes(),
resource.get("nonce").getBytes(),
resource.get("ciphertext"));
//支付返回信息
Map<String, Object> jsonData = objectMapper.readValue(decryptToString, Map.class);
//支付成功
if ("SUCCESS".equals(jsonData.get("trade_state"))) {
//获取支付的交易单号,流水号
String out_trade_no = jsonData.get("out_trade_no").toString();
String transaction_id = jsonData.get("transaction_id").toString();
String attach = jsonData.get("attach").toString();
//TODO 根据交易单号 流水号来更新订单状态 改成已支付状态
//转成map 下面保存数据的时候好取
HashMap<String, Object> map = JSONObject.parseObject(attach, HashMap.class);
System.out.println(map);
//TODO 保存用户支付信息日志 每条订单保存一个支付信息
}
result.put("code", "SUCCESS");
result.put("message", "成功");
} catch (Exception e) {
result.put("code", "fail");
result.put("message", "系统错误");
e.printStackTrace();
}
return result;
}
/**
* 获取平台证书
* @author: jie.wang
* @return
*/
public static Map<String, X509Certificate> refreshCertificate() throws Exception {
Map<String, X509Certificate> certificateMap = new HashMap();
// 1: 执行get请求
JsonNode jsonNode = doGet(WxPayConfig.CERTIFICATESURL);
// 2: 获取平台验证的相关参数信息
JsonNode data = jsonNode.get("data");
if (data != null) {
for (int i = 0; i < data.size(); i++) {
JsonNode encrypt_certificate = data.get(i).get("encrypt_certificate");
//对关键信息进行解密
AesUtil aesUtil = new AesUtil(WxPayConfig.v3Key.getBytes());
String associated_data = encrypt_certificate.get("associated_data").toString().replaceAll("\"", "");
String nonce = encrypt_certificate.get("nonce").toString().replaceAll("\"", "");
String ciphertext = encrypt_certificate.get("ciphertext").toString().replaceAll("\"", "");
//证书内容
String certStr = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
//证书内容转成证书对象
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(certStr.getBytes("utf-8"))
);
String serial_no = data.get(i).get("serial_no").toString().replaceAll("\"", "");
certificateMap.put(serial_no, x509Cert);
}
}
return certificateMap;
}
/**
* 生成随机数
* @author: jie.wang
*/
public static String getNonceStr() {
return UUID.randomUUID().toString()
.replaceAll("-", "")
.substring(0, 32);
}
/**
* @author: jie.wang
* @param appId
* @param prepay_id
* @throws IOException
* @throws SignatureException
* @throws NoSuchAlgorithmException
* @throws InvalidKeyException
*/
public static HashMap<String, Object> getTokenJSAPI(String appId, String prepay_id) throws Exception {
// 获取随机字符串
String nonceStr = getNonceStr();
// 获取微信小程序支付package
String packagestr = "prepay_id=" + prepay_id;
long timestamp = System.currentTimeMillis() / 1000;
//签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
//获取对应的签名
String signature = sign(message.getBytes("utf-8"));
// 组装返回
HashMap<String, Object> map = new HashMap<>();
map.put("appId", appId);
map.put("timeStamp", String.valueOf(timestamp));
map.put("nonceStr", nonceStr);
map.put("package", packagestr);
map.put("signType", "RSA");
map.put("paySign", signature);
return map;
}
/**
* 拼接加签
*
* @author: jie.wang
*/
private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ packag + "\n";
}
private static final ObjectMapper JSON=new ObjectMapper();
/**
* post请求
* @author: jie.wang
*
* @return
*/
public static Map<String,Object> doPostWexin(String url, String body){
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type","application/json;chartset=utf-8");
httpPost.addHeader("Accept", "application/json");
try{
String token =getToken("POST", new URL(url), body);
httpPost.addHeader("Authorization", token);
if(body==null){
throw new IllegalArgumentException("data参数不能为空");
}
StringEntity stringEntity = new StringEntity(body,"utf-8");
httpPost.setEntity(stringEntity);
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
if(httpResponse.getStatusLine().getStatusCode() == 200){
String jsonResult = EntityUtils.toString(httpEntity);
return JSON.readValue(jsonResult, HashMap.class);
}else{
System.err.println("微信支付错误信息"+EntityUtils.toString(httpEntity));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try{
httpClient.close();
}catch (Exception e){
e.printStackTrace();
}
}
return null;
}
/**
* get请求 并获取证书
* @param url
* @return
*/
public static JsonNode doGet(String url){
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpGet httpget = new HttpGet(url);
httpget.addHeader("Content-Type", "application/json;charset=UTF-8");
httpget.addHeader("Accept", "application/json");
try{
String token = getToken("GET", new URL(url), "");
httpget.addHeader("Authorization", token);
CloseableHttpResponse httpResponse = httpClient.execute(httpget);
if(httpResponse.getStatusLine().getStatusCode() == 200){
String jsonResult = EntityUtils.toString( httpResponse.getEntity());
return JSON.readTree(jsonResult);
}else{
System.err.println(EntityUtils.toString( httpResponse.getEntity()));
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
httpClient.close();
}catch (Exception e){
e.printStackTrace();
}
}
return null;
}
/**
* 生成签名
* @author: jie.wang
* @param method
* @param url
* @param body
* @return
* @throws Exception
*/
public static String getToken(String method, URL url, String body) throws Exception {
String nonceStr = getNonceStr();
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, url, timestamp, nonceStr, body);
String signature = sign(message.getBytes("utf-8"));
return "WECHATPAY2-SHA256-RSA2048 " + "mchid=\"" + WxPayConfig.mch_id + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"" + WxPayConfig.mchSerialNo + "\","
+ "signature=\"" + signature + "\"";
}
/**
* 生成签名串
* @author: jie.wang
* @param method
* @param url
* @param timestamp
* @param nonceStr
* @param body
* @return
*/
public static String buildMessage(String method, URL url, long timestamp, String nonceStr, String body) {
String canonicalUrl = url.getPath();
if (url.getQuery() != null) {
canonicalUrl += "?" + url.getQuery();
}
return method + "\n"
+ canonicalUrl + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ body + "\n";
}
/**
* 生成签名
* @author: jie.wang
* @param message
* @return
* @throws Exception
*/
public static String sign(byte[] message) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(WxPayConfig.KeyPath));
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥
* @author: jie.wang
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
System.out.println("filename:" + filename);
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(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效");
}
}
}
|