java实现微信v3支付,分账,退款
初次接触支付多少会有些不知所措,看微信支付官方文档的api不全,只能作参考,下面是已实现的支付实现
1、微信下单,支付(JSAPI支付,Native支付),分账,分账退回,退款
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONArray;
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.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.security.cert.X509Certificate;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpHeaders.CONTENT_TYPE;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* V3新版支付
*/
@Component
public class WeChatPayUtil {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/** 支付回调地址 */
@Value("${wechat.notifyUrl}")
private String notifyUrl;
/** 支付扫码回调地址 */
@Value("${wechat.notifyNativeUrl}")
private String notifyNativeUrl;
/** 退款回调地址 */
@Value("${wechat.refundNotifyUrl}")
private String refundNotifyUrl;
/** 商户号 */
@Value("${wechat.mchId}")
private String mchId;
/** appId */
@Value("${wechat.miniProgramAppid}")
private String miniProgramAppid;
/** 公钥证书地址apiclient_key.pem */
@Value("${wechat.wechatPayCertPath}")
private String apiclientKey;
/** 证书序列号 */
@Value("${wechat.wechatPaySerial}")
private String wechatPaySerial;
/** v3秘钥用于获取支付证书 */
@Value("${wechat.apiV3Key}")
private String apiV3Key;
//1、Jsapi支付接口:1、微信下单;2、微信支付;3、支付通知;-4、添加分账
//2、Native扫码支付接口:1、微信下单;2、微信扫码;3、微信支付;4、微信查询支付信息;5、支付通知;6、添加分账
//3、微信退款接口:1、申请退款,2、退款审核;3、微信退款;4、退款通知
//4、微信分账接口:1、支付成功-添加分账;2、申请付款-请求分账;3、分账通知;4、分账回退;6、删除分账;7、解除剩余资金
/**
* 微信v3 post请求
* @param url
* @param body
* @return
* @throws Exception
*/
public String v3Post(String url, String body) throws Exception {
logger.info("微信v3 post请求参数:" + body);
CloseableHttpClient httpClient = getClient();
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString());
httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
httpPost.addHeader("Wechatpay-Serial", wechatPaySerial);
httpPost.setEntity(new StringEntity(body, "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
logger.info("微信v3 post请求返回信息:" + bodyAsString);
return bodyAsString;
} finally {
httpClient.close();
response.close();
}
}
/**
* 微信v3 get请求
* @param url
* @return
* @throws Exception
*/
public String v3Get(String url) throws Exception {
logger.info("微信v3 get请求URL:" + url);
CloseableHttpClient httpClient = getClient();
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader(ACCEPT, APPLICATION_JSON.toString());
httpGet.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
httpGet.addHeader("Wechatpay-Serial", wechatPaySerial);
CloseableHttpResponse response = httpClient.execute(httpGet);
try {
String bodyAsString = EntityUtils.toString(response.getEntity());
logger.info("微信v3 get请求返回信息:" + bodyAsString);
return bodyAsString;
} finally {
httpClient.close();
response.close();
}
}
/**
* 微信通讯client
* @return CloseableHttpClient
*/
public CloseableHttpClient getClient() throws NotFoundException, GeneralSecurityException, IOException, HttpCodeException {
/**第一种方式读取:配置本地文件私钥证书读取*/
// Resource resource = new ClassPathResource(apiclientKey);
// InputStream apiclientKeyInputStream = resource.getInputStream();
/**第二种直接读取:商户证书私钥文件*/
// InputStream apiclientKeyFile = ClassUtils
// .getDefaultClassLoader()
// .getResourceAsStream(apiclientKey);
// Reader reader = new InputStreamReader(apiclientKeyFile, "UTF-8");
//通过生成的jar包获取路径
InputStream apiclientKeyFile = Files.newInputStream(Paths.get(apiclientKey));
/** 获取jar包下面的路径--------------------*/
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyFile);
/**微信支付平台证书文件*/
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息 同望
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
new PrivateKeySigner(wechatPaySerial, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// ... 若有多个商户号,可继续调用putMerchant添加商户信息 华帝
// certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
// new PrivateKeySigner(wechatPaySerial, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, wechatPaySerial, merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
/**
* 转换为分数,money小数长度不允许超过2位
* @param money
* @return
*/
public int conversionFraction(BigDecimal money) {
int fractionMonery = 0;
if (money != null) {
fractionMonery = money.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue();
}
return fractionMonery;
}
/**
* 获取时间戳
* @return
*/
private static String getTimeStamp() {
return String.valueOf(System.currentTimeMillis() / 1000);
}
/**
* 生成签名
* @param message
* @return
* @throws Exception
*/
public String sign(byte[] message) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
// 商户证书私钥
sign.initSign(getPrivateKey());
sign.update(message);
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取商户证书私钥
* @return 私钥对象
*/
public PrivateKey getPrivateKey() throws IOException {
// 本地获取证书路径
// Resource resource = new ClassPathResource(apiclientKey);
// InputStream apiclientKeyFile = resource.getInputStream();
// jar包获取路径
InputStream apiclientKeyFile = Files.newInputStream(Paths.get(apiclientKey));
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyFile);
return merchantPrivateKey;
}
/**
* 构造签名串
* @param signMessage 待签名的参数
* @return 构造后带待签名串
*/
static String buildSignMessage(ArrayList<String> signMessage) {
if (signMessage == null || signMessage.size() <= 0) {
return null;
}
StringBuilder sbf = new StringBuilder();
for (String str : signMessage) {
sbf.append(str).append("\n");
}
return sbf.toString();
}
/**
* 小程序Jsapi支付
* @param orderNum 商户订单号
* @param total 总金额
* @param openid 个人用户openId
* @throws Exception
*/
public JSONObject payJsapi(String orderNum,BigDecimal total,String openid,boolean profitSharing) throws Exception {
String transactionsJsapiBody = transactionsJsapi(miniProgramAppid,"订单支付",orderNum,total,openid,profitSharing);
JSONObject transactionsJsapiBodyJSON = JSON.parseObject(transactionsJsapiBody);
String prepayId = transactionsJsapiBodyJSON.getString("prepay_id");
if(StringUtils.isNotBlank(prepayId)){
String timeStamp = getTimeStamp();
String nonceStr = RandomStringUtils.randomNumeric(32);
ArrayList<String> list = new ArrayList<>();
list.add(miniProgramAppid);
list.add(timeStamp);
list.add(nonceStr);
list.add("prepay_id=" + prepayId);
//二次签名,调起支付需要重新签名,注意这个是buildSignMessage()
String packageSign = sign(buildSignMessage(list).getBytes());
JSONObject jsonObject = new JSONObject();
jsonObject.put("timeStamp", timeStamp);
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("package", "prepay_id=" + prepayId);
jsonObject.put("signType", "RSA");
jsonObject.put("paySign", packageSign);
logger.info("Jsapi-pay-get-sign:==="+jsonObject);
return jsonObject;
}else{
return transactionsJsapiBodyJSON;
}
}
/**
* 分润退回
* @param orderNum 商户订单号
* @param total 总金额
* @param openid 个人用户openId
* @throws Exception
*/
public JSONObject payJsapiBenefit(String orderNum,BigDecimal total,String openid,boolean profitSharing) throws Exception {
String transactionsJsapiBody = transactionsJsapi(miniProgramAppid,"分润回退",orderNum,total,openid,profitSharing);
JSONObject transactionsJsapiBodyJSON = JSON.parseObject(transactionsJsapiBody);
String prepayId = transactionsJsapiBodyJSON.getString("prepay_id");
if(StringUtils.isNotBlank(prepayId)){
String timeStamp = getTimeStamp();
String nonceStr = RandomStringUtils.randomNumeric(32);
ArrayList<String> list = new ArrayList<>();
list.add(miniProgramAppid);
list.add(timeStamp);
list.add(nonceStr);
list.add("prepay_id=" + prepayId);
//二次签名,调起支付需要重新签名,注意这个是buildSignMessage()
String packageSign = sign(buildSignMessage(list).getBytes());
JSONObject jsonObject = new JSONObject();
jsonObject.put("timeStamp", timeStamp);
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("package", "prepay_id=" + prepayId);
jsonObject.put("signType", "RSA");
jsonObject.put("paySign", packageSign);
logger.info("Jsapi-pay-get-sign:==="+jsonObject);
return jsonObject;
}else{
return transactionsJsapiBodyJSON;
}
}
/**
* JSAPI下单
* @param appid 对应openid的应用ID
* @param description 商品描述
* @param orderNum 商户订单号
* @param total 总金额
* @param openid 个人用户openId
* @throws Exception
*/
public String transactionsJsapi(String appid, String description, String orderNum, BigDecimal total, String openid,boolean profitSharing) throws Exception {
JSONObject transactionsJsapiJSON = new JSONObject();
transactionsJsapiJSON.put("appid", appid);
transactionsJsapiJSON.put("mchid", mchId);
transactionsJsapiJSON.put("description", description);
transactionsJsapiJSON.put("out_trade_no", orderNum);
transactionsJsapiJSON.put("notify_url", notifyUrl);
JSONObject amount = new JSONObject();
amount.put("total", conversionFraction(total));
amount.put("currency", "CNY");
transactionsJsapiJSON.put("amount", amount);
JSONObject payer = new JSONObject();
payer.put("openid", openid);
transactionsJsapiJSON.put("payer", payer);
JSONObject profit = new JSONObject();
profit.put("profit_sharing", profitSharing);
transactionsJsapiJSON.put("settle_info", profit);
String body = transactionsJsapiJSON.toJSONString();
logger.info("mchId===="+mchId);
logger.info("notifyUrl===="+notifyUrl);
logger.info("amount===="+amount);
logger.info("appid===="+appid);
logger.info("openid===="+openid);
logger.info("body===="+body);
return v3Post("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", body);
}
/**
* 添加分账用户
* @param openid 个人用户openId
* @throws Exception
*/
public String profitSharingAdd(String openid,String name) throws Exception {
JSONObject profitSharingJSON = new JSONObject();
profitSharingJSON.put("appid", miniProgramAppid);
profitSharingJSON.put("type","PERSONAL_OPENID");
profitSharingJSON.put("account",openid);
// profitSharingJSON.put("name",);
profitSharingJSON.put("relation_type","CUSTOM");
profitSharingJSON.put("custom_relation","工程师");
String body = profitSharingJSON.toJSONString();
logger.info("微信添加分账profitSharingAdd===="+body);
return v3Post("https://api.mch.weixin.qq.com/v3/profitsharing/receivers/add", body);
}
/**
* 支付成功后请求分账
* @param transactionId 微信订单号
* @param outOrderNo 商户分账单号
* @param openid 个人用户openId
* @param total 分账金额
* @throws Exception
*/
public String profitSharingApply(String transactionId,String outOrderNo,String openid,BigDecimal total) throws Exception {
JSONObject profitSharingJSON = new JSONObject();
profitSharingJSON.put("appid", miniProgramAppid);
profitSharingJSON.put("transaction_id",transactionId);
profitSharingJSON.put("out_order_no",outOrderNo);
JSONArray jsonArray = new JSONArray();
JSONObject receiversJSON = new JSONObject();
receiversJSON.put("type","PERSONAL_OPENID");
receiversJSON.put("account",openid);
// receiversJSON.put("name","");
receiversJSON.put("amount",Integer.valueOf(total.intValue()));
receiversJSON.put("description","分润");
jsonArray.add(receiversJSON);
profitSharingJSON.put("receivers",jsonArray);
profitSharingJSON.put("unfreeze_unsplit",true);
String body = profitSharingJSON.toJSONString();
logger.info("微信请求分账profitSharingApply===="+body);
return v3Post("https://api.mch.weixin.qq.com/v3/profitsharing/orders", body);
}
/**
* 退款成功后分账退回
* @param orderId 微信分账单号
* @param outOrderNo 商户分账单号
* @param outReturnNo 分润单号
* @param amount 回退金额
* @throws Exception
*/
public String domesticBenefits(String orderId,String outOrderNo,String outReturnNo,BigDecimal amount) throws Exception {
JSONObject profitSharingJSON = new JSONObject();
profitSharingJSON.put("return_mchid", mchId);
profitSharingJSON.put("order_id",orderId);
profitSharingJSON.put("out_order_no",outOrderNo);
profitSharingJSON.put("out_return_no",outReturnNo);
profitSharingJSON.put("amount",Integer.valueOf(amount.intValue()));
profitSharingJSON.put("description","用户退款");
String body = profitSharingJSON.toJSONString();
logger.info("微信分账退回profitSharingBody===="+body);
return v3Post("https://api.mch.weixin.qq.com/v3/profitsharing/return-orders", body);
}
/**
* 小程序Native支付
* @param orderNum 商户订单号
* @param total 总金额
* @throws Exception
*/
public JSONObject payNative(String orderNum,BigDecimal total) throws Exception {
String transactionsJsapiBody = transactionsNative(miniProgramAppid,"订单支付",orderNum,total);
JSONObject transactionsJsapiBodyJSON = JSON.parseObject(transactionsJsapiBody);
String codeUrl = transactionsJsapiBodyJSON.getString("code_url");
if(StringUtils.isNotBlank(codeUrl)){
String timeStamp = getTimeStamp();
String nonceStr = RandomStringUtils.randomNumeric(32);
JSONObject jsonObject = new JSONObject();
jsonObject.put("timeStamp", timeStamp);
jsonObject.put("nonceStr", nonceStr);
jsonObject.put("codeUrl", codeUrl);
jsonObject.put("signType", "RSA");
logger.info("Native-pay-get-code_url:==="+jsonObject);
return jsonObject;
}else{
return transactionsJsapiBodyJSON;
}
}
/**
* Native下单
* @param appid 对应openid的应用ID
* @param description 商品描述
* @param orderNum 商户订单号
* @param total 总金额
* @throws Exception
*/
public String transactionsNative(String appid, String description, String orderNum, BigDecimal total) throws Exception {
JSONObject transactionsJsapiJSON = new JSONObject();
transactionsJsapiJSON.put("appid", appid);
transactionsJsapiJSON.put("mchid", mchId);
transactionsJsapiJSON.put("description", description);
transactionsJsapiJSON.put("out_trade_no", orderNum);
transactionsJsapiJSON.put("notify_url", notifyUrl);
JSONObject amount = new JSONObject();
amount.put("total", conversionFraction(total));
amount.put("currency", "CNY");
transactionsJsapiJSON.put("amount", amount);
JSONObject profit = new JSONObject();
profit.put("profit_sharing", true);
transactionsJsapiJSON.put("settle_info", profit);
String body = transactionsJsapiJSON.toJSONString();
logger.info("mchId===="+mchId);
logger.info("notifyUrl===="+notifyNativeUrl);
logger.info("amount===="+amount);
logger.info("appid===="+appid);
logger.info("body===="+body);
return v3Post("https://api.mch.weixin.qq.com/v3/pay/transactions/native", body);
}
/**
* 申请退款
* @param transactionId 微信支付订单号
* @param outRefundNo 商户退款单号
* @param refund 退款金额
* @param total 原订单金额
* @return
* @throws Exception
*/
public String domesticRefunds(String transactionId,String outRefundNo,BigDecimal refund,BigDecimal total) throws Exception {
JSONObject domesticRefundsJSON = new JSONObject();
domesticRefundsJSON.put("transaction_id", transactionId);
domesticRefundsJSON.put("out_refund_no", outRefundNo);
JSONObject amount = new JSONObject();
amount.put("refund",conversionFraction(refund));
amount.put("total",conversionFraction(total));
amount.put("currency","CNY");
domesticRefundsJSON.put("amount",amount);
domesticRefundsJSON.put("notify_url", refundNotifyUrl);
String body = domesticRefundsJSON.toJSONString();
String domesticRefundsBody = v3Post("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds",body);
JSONObject domesticRefundsBodyJSON = JSON.parseObject(domesticRefundsBody);
if(StringUtils.isNotBlank(domesticRefundsBodyJSON.getString("status")) && (domesticRefundsBodyJSON.getString("status").equals("SUCCESS") || domesticRefundsBodyJSON.getString("status").equals("PROCESSING"))){
return "200";
}else{
return "code="+domesticRefundsBodyJSON.getString("code")+",message="+domesticRefundsBodyJSON.getString("message");
}
}
/**
* 小程序Jsapi支付
* @param transactionId 微信交易订单号
* @throws Exception
*/
public JSONObject queryOrder(String transactionId) throws Exception {
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/id/"+transactionId);
uriBuilder.setParameter("mchid", mchId);
String queryBody = v3Get(String.valueOf(uriBuilder.build()));
JSONObject queryBodyJson = JSON.parseObject(queryBody);
return queryBodyJson;
}
/**
* 小程序Jsapi支付
* @param orderNumber 商户订单号
* @throws Exception
*/
public JSONObject queryWeChat(String orderNumber) throws Exception{
URIBuilder uriBuilder = new URIBuilder("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"+orderNumber);
uriBuilder.setParameter("mchid", mchId);
String queryBody = v3Get(String.valueOf(uriBuilder.build()));
JSONObject queryBodyJson = JSON.parseObject(queryBody);
return queryBodyJson;
}
}
2、支付,退款回调
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.etwowin.fwpt.common.core.util.R;
import com.etwowin.fwpt.common.security.annotation.Inner;
import com.etwowin.fwptbasic.serviceMarketing.saleOrder.service.SlOrderService;
import com.etwowin.fwptbasic.serviceMarketing.saleOrder.utils.WeChatPayUtil;
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.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.core.parameters.P;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v3/pay")
@Api(value = "WeChatPayController", tags = "微信支付回调")
public class WeChatPayController {
private static Logger logger = LoggerFactory.getLogger(WeChatPayController.class);
/** 商户号 */
@Value("${wechat.mchId}")
private String mchId;
/** 公钥证书地址apiclient_key.pem */
// private static final String apiclientKey = "wechat/apiclient_key.pem";
@Value("${wechat.wechatPayCertPath}")
private String apiclientKey;
/** 证书序列号 */
@Value("${wechat.wechatPaySerial}")
private String wechatPaySerial;
/** v3秘钥用于获取支付证书,详情参考官方文档 */
@Value("${wechat.apiV3Key}")
private String apiV3Key;
private final WeChatPayUtil weChatPayUtil;
/**
* v3处理微信JsApi支付异步回调
* @param request
* @param response
*/
@PostMapping("/clientWeChatNotify")
@Inner(value = false)
public void clientWeChatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
//获取微信回调结果
JSONObject dataJSON = getCallback(request);
logger.info("v3处理微信JsApi支付异步回调================"+dataJSON);
// String outTradeNo = dataJSON.getString("out_trade_no");//商户平台订单号
// String transactionId = dataJSON.getString("transaction_id");//微信支付交易号
//处理业务逻辑
//........
//返回给微信
response.setStatus(200);
}catch (Exception e){
e.printStackTrace();
logger.error("v3处理微信支付JsApi异步回调:错误-"+e.getMessage());
}
}
/**
* v3处理微信Native支付异步回调
* @param request
* @param response
*/
@PostMapping("/clientWeChatNativeNotify")
@Inner(value = false)
public void clientWeChatNativeNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
//获取微信回调结果
JSONObject dataJSON = getCallback(request);
logger.info("v3处理微信Native支付异步回调================"+dataJSON);
//处理业务逻辑
//.......
//返回给微信
response.setStatus(200);
}catch (Exception e){
e.printStackTrace();
logger.error("v3处理微信支付Native异步回调:错误-"+e.getMessage());
}
}
/**
* v3处理微信退款异步回调
* @param request
* @param response
*/
@PostMapping("/refundWeChatNotify")
@Inner(value = false)
public void refundWeChatNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
//获取微信回调结果
JSONObject dataJSON = getCallback(request);
logger.info("v3处理微信退款异步回调================"+dataJSON);
// String outRefundNo = dataJSON.getString("out_refund_no");//商户退款单号
// String refundStatus = dataJSON.getString("refund_status");//退款状态,枚举值
//处理业务逻辑
//.......
//返回给微信
response.setStatus(200);
}catch (Exception e){
e.printStackTrace();
logger.error("v3处理微信退款异步回调:错误-"+e.getMessage());
}
}
@PostMapping("/benefitRefundNotify")
@Inner(value = false)
public void benefitRefundNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
//获取微信分账回调结果
JSONObject dataJSON = getCallback(request);
logger.info("分账回退通知================"+dataJSON);
//处理业务逻辑
//.........
//返回给微信
response.setStatus(200);
}catch (Exception e){
e.printStackTrace();
logger.error("v3处理微信分账回退异步回调:错误-"+e.getMessage());
}
}
/**
* 获取微信回调结果
* @param request
* @return
* @throws Exception
*/
public JSONObject getCallback(HttpServletRequest request) throws Exception {
//获取报文
String body = getRequestBody(request);
//随机串
String nonce = request.getHeader("Wechatpay-Nonce");
//微信传递过来的签名
String signature = request.getHeader("Wechatpay-Signature");
//证书序列号(微信平台)
String wechatPaySerial = request.getHeader("Wechatpay-Serial");
//时间戳
String timestamp = request.getHeader("Wechatpay-Timestamp");
// 验签
NotificationRequest notificationRequest = new NotificationRequest.Builder()
.withSerialNumber(wechatPaySerial)
.withNonce(nonce)
.withTimestamp(timestamp)
.withSignature(signature)
.withBody(body)
.build();
NotificationHandler handler = new NotificationHandler(verifier(), apiV3Key.getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
Notification notification = handler.parse(notificationRequest);
return JSON.parseObject(notification.getDecryptData());
}
/**
* 获取报文
* @param request
* @return
* @throws Exception
*/
private String getRequestBody(HttpServletRequest request) throws Exception {
StringBuffer stringBuffer = new StringBuffer();
ServletInputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
stringBuffer.append(line);
}
return stringBuffer.toString();
}
/**
* 获得验签器
* @return
* @throws Exception
*/
public Verifier verifier() throws Exception {
//获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,new PrivateKeySigner(wechatPaySerial, getPrivateKey())), apiV3Key.getBytes(StandardCharsets.UTF_8));
// 从证书管理器中获取verifier
Verifier verifier = certificatesManager.getVerifier(mchId);
return verifier;
}
/**
* 获取商户证书私钥
* @return 私钥对象
*/
public PrivateKey getPrivateKey() throws IOException {
// Resource resource = new ClassPathResource(apiclientKey);
// InputStream apiclientKeyFile = resource.getInputStream();
InputStream apiclientKeyFile = Files.newInputStream(Paths.get(apiclientKey));
PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(apiclientKeyFile);
// PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
// new FileInputStream(apiclientKey));
return merchantPrivateKey;
}
@ApiOperation("v3处理微信JsApi支付异步回调")
@PostMapping("/wechatOrderPay")
public @Valid
@ResponseBody R wechatOrderPay(@RequestBody JSONObject dataJSON,Integer payType){
try {
slOrderService.wechatOrderPay(dataJSON,payType);
// logger.info("WeChatPayController:===="+r);
return R.ok();
}catch (Exception e){
logger.error(e.getMessage(), e);
return R.failed(e.getMessage());
}
}
@ApiOperation("v3处理微信退款异步回调")
@PostMapping("/wechatOrderRefund")
public @Valid
@ResponseBody R wechatOrderRefund(@RequestBody JSONObject dataJSON){
try {
//处理退款业务逻辑
return R.ok();
}catch (Exception e){
logger.error(e.getMessage(), e);
return R.failed(e.getMessage());
}
}
/**
* 获取微信查询支付信息
* @param requestMap
*/
@ApiOperation("微信查询支付信息")
@PostMapping("/queryOrder")
public @Valid
@ResponseBody R queryOrder(@RequestBody Map<String,Object> requestMap){
try {
//处理查询支付信息的数据存储
logger.info("WeChatPayController:===="+r);
return r;
}catch (Exception e){
logger.error(e.getMessage(), e);
return R.failed(e.getMessage());
}
}
@ApiOperation("微信查询支付成功信息")
@PostMapping("/queryWeChat")
public @Valid
@ResponseBody void queryWeChat(@RequestParam String orderNumber){
try {
JSONObject jsonObject = weChatPayUtil.queryWeChat(orderNumber);
logger.info("v3处理微信查询支付成功信息================"+jsonObject);
//处理业务逻辑
//..........
}catch (Exception e){
e.printStackTrace();
}
}
}
踩过的坑,不想再次踩,希望能帮助需要的同学,不懂的地方或者配置不知道的随时可以找我