1、微信支付
<!-- 微信 pay -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.2.2</version>
</dependency>
@Data
@Configuration
@ConfigurationProperties(prefix = "wchatpay")
public class WechatPayConfig {
//微信APPID
private String appid;
//微信商户号
private String mchid;
//微信支付通知
private String notifyUrl;
//微信支付 证书编号
private String mchSerialNo;
// 证书路径
private String merchantPrivateKey;
//商户APIV3密钥
private String apiV3key;
private PrivateKey privateKey;
private WechatPayHttpClientBuilder builder;
private AutoUpdateCertificatesVerifier verifier;
private AesUtil aesUtil;
@Bean
public void initWechatPay() throws IOException {
FileInputStream fis = new FileInputStream(merchantPrivateKey);
aesUtil = new AesUtil(apiV3key.getBytes(StandardCharsets.UTF_8));
privateKey = PemUtil.loadPrivateKey(fis);
fis.close();
//不需要传入微信支付证书了
verifier = new AutoUpdateCertificatesVerifier(
new WechatPay2Credentials(mchid, new PrivateKeySigner(mchSerialNo, privateKey)),
apiV3key.getBytes(StandardCharsets.UTF_8));
builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchid, mchSerialNo, privateKey)
.withValidator(new WechatPay2Validator(verifier));
}
}
public class WechatUtil {
/**
* 微信调起支付参数
* @param jsonObject
* @param wxpayAppid
* @param privateKey
* @return
* @throws Exception
*/
public static JSONObject WechatPayTuneUp(JSONObject jsonObject, String wxpayAppid, PrivateKey privateKey) throws Exception {
System.out.println("success,return body = " + jsonObject.toJSONString());
//预支付交易会话标识
String prepayId = jsonObject.getString("prepay_id");
//随机字符串
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
//时间戳
long timestamp = System.currentTimeMillis() / 1000;
//从下往上依次生成
String message = buildMessage(wxpayAppid, timestamp, nonceStr, prepayId);
//签名
String signature = sign(message.getBytes("utf-8"), privateKey);
JSONObject params = new JSONObject();
params.put("appId", wxpayAppid);
params.put("timeStamp", timestamp);
params.put("nonceStr", nonceStr);
params.put("package", "prepay_id=" + prepayId);
params.put("signType", "RSA");
params.put("paySign", signature);
return params;
}
private static String sign(byte[] message, PrivateKey privateKey) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
//签名方式
Signature sign = Signature.getInstance("SHA256withRSA");
//私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
sign.initSign(privateKey);
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 按照前端签名文档规范进行排序,\n是换行
* @param appId
* @param timestamp
* @param nonceStr
* @param prepayId
* @return
*/
private static String buildMessage(String appId, long timestamp, String nonceStr, String prepayId) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ "prepay_id=" + prepayId + "\n";
}
public static String success() {
JSONObject jsonResponse = new JSONObject();
jsonResponse.put("code", "SUCCESS");
jsonResponse.put("message", "成功");
return jsonResponse.toString();
}
public static String fail() {
JSONObject jsonResponse = new JSONObject();
jsonResponse.put("code", "FAIL");
jsonResponse.put("message", "失败");
return jsonResponse.toString();
}
}
public interface IWechatPayService {
/**
* 生成预支付交易单 - 小程序支付
* @param openid 用户openid
* @param paymentNo 支付流水编号
* @param goodsName 商品名称
* @param totalFee 订单金额(单位:分)
*/
JSONObject wechatPayXcxPay(String openid, String paymentNo, String goodsName, int totalFee) throws Exception;
/**
* 验签 & 解密
* @param wechatpaySerial
* @param wechatpaySignature
* @param wechatpayTimestamp
* @param wechatpayNonce
* @param callback
* @return
*/
PayNotifyResDTO decryptBody(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String callback) throws Exception;
/**
* 根据支付流水编号,查询订单信息
* @param paymentNo
* @return
*/
PayNotifyResDTO getPayOrderInfo(String paymentNo);
}
@Service
public class WechatPayServiceImpl implements IWechatPayService {
@Autowired
private WechatPayConfig wechatPayConfig;
// 小程序下单地址
public static final String WECHAT_PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
// 查询订单地址
public static final String WECHAT_GET_ORDER = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}";
@Override
public JSONObject wechatPayXcxPay(String openid, String orderSn, String goodsName, int totalFee) throws Exception {
CloseableHttpClient httpClient = wechatPayConfig.getBuilder().build();
try {
//请求URL
HttpPost httpPost = new HttpPost(WECHAT_PAY_URL);
// 请求body参数
JSONObject reqJson = new JSONObject();
JSONObject amountJson = new JSONObject();
amountJson.put("total", totalFee);
amountJson.put("currency", "CNY");
reqJson.put("amount", amountJson);
reqJson.put("mchid", wechatPayConfig.getMchid());
reqJson.put("description", goodsName);
reqJson.put("notify_url", wechatPayConfig.getNotifyUrl());
JSONObject payerJson = new JSONObject();
payerJson.put("openid", openid);
reqJson.put("payer", payerJson);
reqJson.put("out_trade_no", orderSn);
reqJson.put("goods_tag", "WXG");
reqJson.put("appid", wechatPayConfig.getAppid());
System.out.println("reqJson = " + reqJson.toJSONString());
StringEntity entity = new StringEntity(reqJson.toJSONString(), "utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
httpPost.setHeader("Accept", "application/json");
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
if (jsonObject.containsKey("prepay_id")) {
return WechatUtil.WechatPayTuneUp(jsonObject, wechatPayConfig.getAppid(), wechatPayConfig.getPrivateKey());
} else {
System.out.println("return body = " + jsonObject.toJSONString());
throw new IOException("request failed");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
}
return null;
}
/**
* 验签
* @return
*/
private Boolean verify(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String callback) {
//按照文档要求拼接验签串
String verifySignature = wechatpayTimestamp + "\n"
+ wechatpayNonce + "\n"
+ callback + "\n";
//使用官方验签工具进行验签
return wechatPayConfig.getVerifier().verify(wechatpaySerial, verifySignature.getBytes(), wechatpaySignature);
}
@Override
public PayNotifyResDTO decryptBody(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp,
String wechatpayNonce, String callback) throws Exception {
//判断验签的结果
if (!verify(wechatpaySerial, wechatpaySignature, wechatpayTimestamp, wechatpayNonce, callback)) {
//验签失败,应答接口
return null;
}
JSONObject parseObject = JSONObject.parseObject(callback);
if ("TRANSACTION.SUCCESS".equals(parseObject.getString("event_type"))
&& "encrypt-resource".equals(parseObject.getString("resource_type"))) {
//通知的类型,支付成功通知的类型为TRANSACTION.SUCCESS
//通知的资源数据类型,支付成功通知为encrypt-resource
JSONObject resourceJson = JSONObject.parseObject(parseObject.getString("resource"));
String associated_data = resourceJson.getString("associated_data");
String nonce = resourceJson.getString("nonce");
String ciphertext = resourceJson.getString("ciphertext");
//解密,如果这里报错,就一定是APIv3密钥错误
String resourceData = wechatPayConfig.getAesUtil().decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
System.out.println("解密后=" + resourceData);
JSONObject resource = JSONObject.parseObject(resourceData);
if (resource.containsKey("out_trade_no")) {
/**支付成功,补充业务**/
PayNotifyResDTO resDTO = new PayNotifyResDTO();
// 第三方支付id
resDTO.setNotifyId(parseObject.getString("id"));
// 商户订单号
resDTO.setOutTradeNo(resource.getString("out_trade_no"));
// 微信支付订单号
resDTO.setTradeNo(resource.getString("transaction_id"));
resDTO.setTradeStatus(resource.getString("trade_state"));
return resDTO;
}
}
return null;
}
@Override
public PayNotifyResDTO getPayOrderInfo(String paymentNo) {
//返回模型
CloseableHttpResponse response = null;
CloseableHttpClient httpClient = wechatPayConfig.getBuilder().build();
try {
String str = WECHAT_GET_ORDER.replace("{out_trade_no}", paymentNo) + "?mchid=" + wechatPayConfig.getMchid();
HttpGet httpGet = new HttpGet(str);
httpGet.addHeader("Accept", "application/json");
response = httpClient.execute(httpGet);
if (response != null) {
JSONObject resource = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
System.out.println("resource = " + resource);
PayNotifyResDTO resDTO = new PayNotifyResDTO();
// 商户订单号
resDTO.setOutTradeNo(resource.getString("out_trade_no"));
// 微信支付订单号
resDTO.setTradeNo(resource.getString("transaction_id"));
// 状态
resDTO.setTradeStatus(resource.getString("trade_state"));
return resDTO;
}
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
httpClient.close();
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
}
/**
* 微信回调
* @param wechatpaySerial
* @param wechatpaySignature
* @param wechatpayTimestamp
* @param wechatpayNonce
* @param callback
* @return
*/
@PostMapping(value = "wechat")
public String wechatPayNotify(@RequestHeader("Wechatpay-Serial") String wechatpaySerial,
@RequestHeader("Wechatpay-Signature") String wechatpaySignature,
@RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
@RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
@RequestBody String callback) {
try {
// 回调通知验签和解密
PayNotifyResDTO resDTO = wechatPayService.decryptBody(wechatpaySerial, wechatpaySignature, wechatpayTimestamp, wechatpayNonce, callback);
if (resDTO == null) {
return WechatUtil.fail();
}
// 回调业务
} catch (Exception e) {
e.printStackTrace();
return WechatUtil.fail();
}
return WechatUtil.success();
}
2、支付宝支付
<!-- 阿里 pay -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-easysdk</artifactId>
<version>2.1.0</versionk>
</dependency>
@Data
@Configuration
@ConfigurationProperties(prefix = "alipay")
public class AliPayConfig {
/**应用ID*/
private String appId;
/** 应用私钥 */
private String merchantPrivateKey;
/** 应用公钥证书文件路径 */
private String merchantCertPath;
/** 支付宝公钥证书文件路径 */
private String alipayCertPath;
/** 应用公钥证书文件路径 */
private String alipayRootCertPath;
/** 回调地址 */
private String notifyUrl;
/**
* 支付宝支付,配置类
* @return
*/
@Bean
public Config getOptions() {
Config config = new Config();
config.protocol = "https";
config.gatewayHost = "openapi.alipay.com";
config.signType = "RSA2";
/**<-- 请填写您的AppId,例如:2019091767145019 -->*/
config.appId = appId;
// 请填写您的应用私钥
config.merchantPrivateKey = merchantPrivateKey;
//注:证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径,优先从文件系统中加载,加载失败后会继续尝试从CLASS_PATH中加载
config.merchantCertPath = merchantCertPath; //"<-- 请填写您的应用公钥证书文件路径,例如:/foo/appCertPublicKey_2019051064521003.crt -->";
config.alipayCertPath = alipayCertPath; //"<-- 请填写您的支付宝公钥证书文件路径,例如:/foo/alipayCertPublicKey_RSA2.crt -->";
config.alipayRootCertPath = alipayRootCertPath; //"<-- 请填写您的支付宝根证书文件路径,例如:/foo/alipayRootCert.crt -->";
//注:请填写您的支付宝公钥: 如果采用非证书模式,则无需赋值上面的三个证书路径,改为赋值如下的支付宝公钥字符串即可
// config.alipayPublicKey = "";
//可设置异步通知接收服务地址(可选):<-- 请填写您的支付类接口异步通知接收服务地址,例如:https://www.test.com/callback -->
config.notifyUrl = notifyUrl;
//可设置AES密钥,调用AES加解密相关接口时需要(可选)
config.encryptKey = "";
return config;
}
@Bean
public void initAliPay() {
Factory.setOptions(getOptions());
}
}
public interface IAliPayService {
/**
* 支付宝APP支付
* @param subject 订单介绍
* @param outTradeNo 订单编号
* @param totalAmount 订单价格(元,小数点后面两位)
* @return
*/
String aliPayAppPay(String subject, String outTradeNo, String totalAmount);
/**
* 回调验签
* @return
*/
PayNotifyResDTO verifyNotify(Map<String, String> data);
/**
* 查询订单信息
* @param outTradeNo
* @return
*/
PayNotifyResDTO getPayOrderInfo(String outTradeNo);
}
@Slf4j
@Service
public class AliPayServiceImpl implements IAliPayService {
@Override
public String aliPayAppPay(String subject, String outTradeNo, String totalAmount) {
// App支付
try {
AlipayTradeAppPayResponse response = Factory.Payment.App().pay(subject, outTradeNo, totalAmount);
if (ResponseChecker.success(response)) {
log.info("{}:支付宝APP支付,发起成功", outTradeNo);
return response.getBody();
} else {
log.info("{}:支付宝App支付,发起失败:{}", outTradeNo, response.body);
}
} catch (Exception e) {
log.error("{}:支付宝App支付异常:{}", outTradeNo, e);
}
return null;
}
@Override
public PayNotifyResDTO verifyNotify(Map<String, String> data) {
try {
Boolean aBoolean = Factory.Payment.Common().verifyNotify(data);
if (!aBoolean) {
return null;
}
// 封装返回参数
PayNotifyResDTO resDTO = new PayNotifyResDTO();
resDTO.setOutTradeNo(data.get("out_trade_no"));
resDTO.setNotifyId(data.get("notify_id"));
resDTO.setTradeNo(data.get("trade_no"));
resDTO.setTradeStatus(data.get("trade_status"));
return resDTO;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public PayNotifyResDTO getPayOrderInfo(String outTradeNo) {
try {
AlipayTradeQueryResponse response = Factory.Payment.Common().query(outTradeNo);
System.out.println("response = " + JSONObject.parseObject(response.getBody()));
if (ResponseChecker.success(response)) {
log.info("{}:查询App支付订单,成功", outTradeNo);
JSONObject data = JSONObject.parseObject(response.getBody());
PayNotifyResDTO resDTO = new PayNotifyResDTO();
resDTO.setOutTradeNo(data.getString("out_trade_no"));
resDTO.setTradeNo(data.getString("trade_no"));
resDTO.setTradeStatus(data.getString("trade_status"));
return resDTO;
} else {
log.info("{}:查询App支付订单,失败:{}", outTradeNo, response.getSubMsg());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
/**
* 阿里支付回调
* @param data
* @return
*/
@PostMapping("ali")
public String aliPayNotify(@RequestParam Map<String, String> data) {
PayNotifyResDTO resDTO = aliPayService.verifyNotify(data);
if (resDTO == null) {
return "sign fail";
}
// 回调业务
return "success";
}