import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.wxpay.sdk.WXPayUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
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.util.EntityUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Base64;
/**
* @Description: 微信支付
* @Date: 2019/5/10
*/
@Component
@Transactional(readOnly = true)
@Slf4j
public class WxPayUtil {
private static String spAppid = "wxf33"; //服务商appid
private static String merchantId = "16206"; // 服务商商户号
private static String merchantSerialNumber = "4AE2F8FFA270";// 服务商 证书序列号
private static String privateKey = "MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC5a3MAzAMv6o3m\n" +
"dfRCDFKktSzq+wGbmUD2AkqH8aW3H6hATBS9UlFAxdh3ars6hTd8Kxeq7GQy8ZDE\n" +
"xGj41smLW7/56eELbdVFiEDT8TO0Qf4c58zP9Xatd04v/PjTavt0PUVTeRCvrfds\n" +
3YddrD38kcUusjMvlh8Ve9CV/rSW\n" +
"+MwWXGbOoXbTlVPKH1FudabglQ==";// 服务商私钥
private static String apiV3Key = "WCnqH";// apiV3Key
private static String subAppid = "wx81c"; //子appid
private static String subMerchantId = "16431"; //子商户号
/**
* 使用AK&SK初始化账号Client
*
* @return Client
* @throws Exception
*/
public static CloseableHttpClient createClient() throws Exception {
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(merchantId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
// CloseableHttpClient httpClient = builder.build();
return builder.build();
}
public static String sign(String timeStamp, String nonceStr, String package1) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, UnsupportedEncodingException {
String message = subAppid + "\n"
+ timeStamp + "\n"
+ nonceStr + "\n"
+ package1 + "\n";
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(privateKey);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(merchantPrivateKey);
sign.update(message.getBytes("utf-8"));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 创建订单
* @param payNum
* @param payPrice
* @param notifyUrl
* @param description
* @param openId
* @return
* @throws Exception
*/
public static JSONObject createJsApiOrder(String payNum, BigDecimal payPrice, String notifyUrl, String description, String openId) throws Exception {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("sp_appid", spAppid)
.put("sp_mchid", merchantId)
.put("sub_mchid", subMerchantId)
.put("sub_appid", subAppid)
.put("description", description)
.put("notify_url", notifyUrl)
.put("out_trade_no", payNum);
rootNode.putObject("amount")
.put("total",payPrice.multiply(new BigDecimal(100)).intValue() ); // payPrice.multiply(new BigDecimal(100)).intValue();
rootNode.putObject("payer")
.put("sub_openid", openId);
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = createClient().execute(httpPost);
JSONObject bodyAsString = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
System.err.println(bodyAsString);
JSONObject jo = new JSONObject();
Long timeStamp = System.currentTimeMillis() / 1000;
String nonceStr = WXPayUtil.generateNonceStr();
jo.put("appId", spAppid);
jo.put("timeStamp", timeStamp + "");
jo.put("nonceStr", nonceStr);
jo.put("package", "prepay_id=" + bodyAsString.get("prepay_id"));
jo.put("signType", "RSA");
jo.put("paySign", sign(timeStamp + "", nonceStr, "prepay_id=" + bodyAsString.get("prepay_id")));
return jo;
}
/**
* 解析小程序支付回调报文
* @param request
* @return
* @throws IOException
* @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException
* @throws BadPaddingException
* @throws IllegalBlockSizeException
* @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException
*/
public static JSONObject parseJsApiMessage(HttpServletRequest request) throws IOException, NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException {
InputStream is = null;
is = request.getInputStream();
StringBuffer buf = new StringBuffer();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line = null;
while ((line = reader.readLine()) != null) {
buf.append(line);
buf.append("\r\n");
}
System.out.println("接收到的报文:" + buf.toString());
JSONObject jo = JSONObject.parseObject(buf.toString());
JSONObject jo1 = JSONObject.parseObject(jo.getString("resource"));
String nonce = jo1.getString("nonce");
String associatedData = jo1.getString("associated_data");
String ciphertext = jo1.getString("ciphertext");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
// apiV3Key
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
String str = new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
return JSONObject.parseObject(str);
}
/**
* 微信退款
* @param outTradeNo 商户订单号
* @param outRefundNo
* @param refund
* @param total
* @param notifyUrl
* @param reason
* @return
* @throws Exception
*/
public static JSONObject wxRefund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total, String notifyUrl, String reason) throws Exception {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("sub_mchid", subMerchantId)
.put("out_trade_no", outTradeNo)
.put("out_refund_no", outRefundNo)
.put("reason", reason)
.put("notify_url", notifyUrl);
rootNode.putObject("amount")
.put("refund", refund.multiply(new BigDecimal(100)).intValue())
.put("total", total.multiply(new BigDecimal(100)).intValue())
.put("currency", "CNY");
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = createClient().execute(httpPost);
JSONObject bodyAsString = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
log.info("退款返回信息开始---------------");
log.info("退款返回信息:{}",bodyAsString);
log.info("退款返回信息结束---------------");
return bodyAsString;
// if(oConvertUtils.isNotEmpty(bodyAsString.getString("status"))){
// return bodyAsString.getString("status");
// }else {
// return bodyAsString.getString("message");
// }
}
}
正文完