Java 接入微信支付API V3 接口开发案例
关于API v3
为了在保证支付安全的前提下,带给商户简单、一致且易用的开发体验,我们推出了全新的微信支付API v3。
相较于之前的微信支付API,主要区别是:
- 遵循统一的REST的设计风格
- 使用JSON作为数据交互的格式,不再使用XML
- 使用基于非对称密钥的SHA256-RSA的数字签名算法,不再使用MD5或HMAC-SHA256
- 不再要求携带HTTPS客户端证书(仅需携带证书序列号)
- 使用AES-256-GCM,对回调中的关键信息进行加密保护
最近接微信支付API v3接口,踩了一些坑,分享一下,帮助码友避免采坑,话不多少,直接上代码。
WeiXinPaySignUtils
-
public class WeiXinPaySignUtils {
-
-
/**
-
* 生成组装请求头
-
*
-
* @param method 请求方式
-
* @param url 请求地址
-
* @param mercId 商户ID
-
* @param serial_no 证书序列号
-
* @param privateKeyFilePath 私钥路径
-
* @param body 请求体
-
* @return 组装请求的数据
-
* @throws Exception
-
*/
-
public static String getToken(String method, HttpUrl url, String mercId,
-
String serial_no, String privateKeyFilePath, String body) throws Exception {
-
String nonceStr = UUID.randomUUID().toString().replace("-", "");
-
long timestamp = System.currentTimeMillis() / 1000;
-
String message = buildMessage(method, url, timestamp, nonceStr, body);
-
String signature = sign(message.getBytes("UTF-8"), privateKeyFilePath);
-
return "mchid=\"" mercId "\","
-
"nonce_str=\"" nonceStr "\","
-
"timestamp=\"" timestamp "\","
-
"serial_no=\"" serial_no "\","
-
"signature=\"" signature "\"";
-
}
-
-
-
/**
-
* 生成签名
-
*
-
* @param message 请求体
-
* @param privateKeyFilePath 私钥的路径
-
* @return 生成base64位签名信息
-
* @throws Exception
-
*/
-
public static String sign(byte[] message, String privateKeyFilePath) throws Exception {
-
Signature sign = Signature.getInstance("SHA256withRSA");
-
sign.initSign(getPrivateKey(privateKeyFilePath));
-
sign.update(message);
-
return Base64.getEncoder().encodeToString(sign.sign());
-
}
-
-
/**
-
* 组装签名加载
-
*
-
* @param method 请求方式
-
* @param url 请求地址
-
* @param timestamp 请求时间
-
* @param nonceStr 请求随机字符串
-
* @param body 请求体
-
* @return 组装的字符串
-
*/
-
public 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";
-
}
-
-
/**
-
* 获取私钥。
-
*
-
* @param filename 私钥文件路径 (required)
-
* @return 私钥对象
-
*/
-
public static PrivateKey getPrivateKey(String filename) throws IOException {
-
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("当前Java环境不支持RSA", e);
-
} catch (InvalidKeySpecException e) {
-
throw new RuntimeException("无效的密钥格式");
-
}
-
}
-
-
/**
-
* 构造签名串
-
*
-
* @param signMessage 待签名的参数
-
* @return 构造后带待签名串
-
*/
-
public 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();
-
}
-
-
/**
-
* v3 支付异步通知验证签名
-
*
-
* @param body 异步通知密文
-
* @param key api 密钥
-
* @return 异步通知明文
-
* @throws Exception 异常信息
-
*/
-
public static String verifyNotify(String body, String key) throws Exception {
-
// 获取平台证书序列号
-
cn.hutool.json.JSONObject resultObject = JSONUtil.parseObj(body);
-
cn.hutool.json.JSONObject resource = resultObject.getJSONObject("resource");
-
String cipherText = resource.getStr("ciphertext");
-
String nonceStr = resource.getStr("nonce");
-
String associatedData = resource.getStr("associated_data");
-
AesUtil aesUtil = new AesUtil(key.getBytes(StandardCharsets.UTF_8));
-
// 密文解密
-
return aesUtil.decryptToString(
-
associatedData.getBytes(StandardCharsets.UTF_8),
-
nonceStr.getBytes(StandardCharsets.UTF_8),
-
cipherText
-
);
-
}
-
-
/**
-
* 处理返回对象
-
*
-
* @param request
-
* @return
-
*/
-
public static String readData(HttpServletRequest request) {
-
BufferedReader br = null;
-
try {
-
StringBuilder result = new StringBuilder();
-
br = request.getReader();
-
for (String line; (line = br.readLine()) != null; ) {
-
if (result.length() > 0) {
-
result.append("\n");
-
}
-
result.append(line);
-
}
-
return result.toString();
-
} catch (IOException e) {
-
throw new RuntimeException(e);
-
} finally {
-
if (br != null) {
-
try {
-
br.close();
-
} catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
-
}
AesUtil
-
public class AesUtil {
-
static final int KEY_LENGTH_BYTE = 32;
-
static final int TAG_LENGTH_BIT = 128;
-
private final byte[] aesKey;
-
-
/**
-
* @param key APIv3 密钥
-
*/
-
public AesUtil(byte[] key) {
-
if (key.length != KEY_LENGTH_BYTE) {
-
throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
-
}
-
this.aesKey = key;
-
}
-
-
/**
-
* 证书和回调报文解密
-
*
-
* @param associatedData associated_data
-
* @param nonce nonce
-
* @param cipherText ciphertext
-
* @return {String} 平台证书明文
-
* @throws GeneralSecurityException 异常
-
*/
-
public String decryptToString(byte[] associatedData, byte[] nonce, String cipherText) throws GeneralSecurityException {
-
try {
-
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
-
SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
-
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
-
cipher.init(Cipher.DECRYPT_MODE, key, spec);
-
cipher.updateAAD(associatedData);
-
return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)), StandardCharsets.UTF_8);
-
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
-
throw new IllegalStateException(e);
-
} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
-
throw new IllegalArgumentException(e);
-
}
-
}
-
-
/**
-
* 敏感信息加密
-
*
-
* @param message
-
* @param certificate
-
* @return
-
* @throws IllegalBlockSizeException
-
* @throws IOException
-
*/
-
public static String rsaEncryptOAEP(String message, X509Certificate certificate)
-
throws IllegalBlockSizeException, IOException {
-
try {
-
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
-
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
-
-
byte[] data = message.getBytes("utf-8");
-
byte[] cipherdata = cipher.doFinal(data);
-
return Base64.getEncoder().encodeToString(cipherdata);
-
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
-
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
-
} catch (InvalidKeyException e) {
-
throw new IllegalArgumentException("无效的证书", e);
-
} catch (IllegalBlockSizeException | BadPaddingException e) {
-
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
-
}
-
}
-
-
/**
-
* 敏感信息解密
-
*
-
* @param ciphertext
-
* @param privateKey
-
* @return
-
* @throws BadPaddingException
-
* @throws IOException
-
*/
-
public static String rsaDecryptOAEP(String ciphertext, PrivateKey privateKey)
-
throws BadPaddingException, IOException {
-
try {
-
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
-
cipher.init(Cipher.DECRYPT_MODE, privateKey);
-
-
byte[] data = Base64.getDecoder().decode(ciphertext);
-
return new String(cipher.doFinal(data), "utf-8");
-
} catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
-
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
-
} catch (InvalidKeyException e) {
-
throw new IllegalArgumentException("无效的私钥", e);
-
} catch (BadPaddingException | IllegalBlockSizeException e) {
-
throw new BadPaddingException("解密失败");
-
}
-
}
-
-
}
WeiXinV3FundinFacade
下单:
-
@PostMapping("/pay")
-
public ResultWrapper<Map<String,Object>> fundin(@RequestBody String request){
-
logger.info("PayChannelOrder->Channel微信V3支付渠道请求参数:" request);
-
ChannelFundResult result = new ChannelFundResult();
-
ChannelFundRequest req = JSON.parseObject(request, ChannelFundRequest.class);
-
logger.info("PayChannelOrder->Channel微信V3支付渠道请求参数转换对象:" req);
-
Properties properties = propertyHelper.getProperties(req.getFundChannelCode());
-
//判断mock开关是否打开,是否要返回mock数据
-
String mock_switch = properties.getProperty(WXPAYFundChannelKey.MOCK_SWITCH);
-
if("true".equals(mock_switch)){//开关开启返回mock数据
-
result.setApiType(req.getApiType());
-
result.setRealAmount(req.getAmount());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setProcessTime(new Date());
-
result = MockResultData.mockResule(result);
-
logger.info("注意这是mock数据!");
-
return ResultWrapper.ok().putData(result);
-
}
-
try {
-
H5V3WxPayVO h5V3WxPayVO = new H5V3WxPayVO();
-
String appId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_APPID);
-
h5V3WxPayVO.setAppid(appId);
-
logger.info("【微信V3支付配置】->【微信appID】:" appId);
-
String mchId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHID);
-
h5V3WxPayVO.setMchid(mchId);
-
logger.info("【微信V3支付配置】->【微信商户ID】:" mchId);
-
String notifyUrl = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_NOTIFYURL);
-
h5V3WxPayVO.setNotify_url(notifyUrl);
-
logger.info("【微信V3支付配置】->【异步通知URL】:" notifyUrl);
-
String description = req.getExtension().get("description");
-
h5V3WxPayVO.setDescription(description);
-
logger.info("【微信V3支付配置】->【商品描述】:" description);
-
String outTradeNo = req.getInstOrderNo();
-
h5V3WxPayVO.setOut_trade_no(outTradeNo);
-
logger.info("【微信V3支付配置】->【商户订单号】:" outTradeNo);
-
String attach = req.getExtension().get("attach");
-
if(StringUtils.isNotBlank(attach)){
-
h5V3WxPayVO.setAttach(attach);
-
}
-
-
AmountVO amount = new AmountVO();
-
amount.setTotal(MoneyUtil.Yuan2Fen(req.getAmount().doubleValue()));
-
amount.setCurrency("CNY");
-
h5V3WxPayVO.setAmount(amount);
-
PayerVO payer = new PayerVO();
-
String openId = req.getExtension().get("openId");
-
payer.setOpenid(openId);
-
h5V3WxPayVO.setPayer(payer);
-
String isDetail = req.getExtension().get("isDetail");
-
if("true".equals(isDetail)){
-
DetailVO detail = new DetailVO();
-
int costPrice = MoneyUtil.Yuan2Fen(req.getAmount().doubleValue());
-
detail.setCostprice(costPrice);
-
String invoiceId = req.getExtension().get("invoiceId");
-
detail.setInvoiceId(invoiceId);
-
String goodsDetailJson = req.getExtension().get("goodsDetail");
-
List<GoodsDetailVO> goodsDetailVOList = JSON.parseArray(goodsDetailJson,GoodsDetailVO.class);
-
detail.setGoods_detail(goodsDetailVOList);
-
h5V3WxPayVO.setDetail(detail);
-
}
-
SceneInfoVO sceneInfoVO = new SceneInfoVO();
-
String payerClientIp = req.getExtension().get("payerClientIp");
-
sceneInfoVO.setPayer_client_ip(payerClientIp);
-
String deviceId = req.getExtension().get("deviceId");
-
if(StringUtils.isNotBlank(deviceId)){
-
sceneInfoVO.setDevice_id(deviceId);
-
}
-
String storeInfoJson = req.getExtension().get("storeInfo");
-
if(StringUtils.isNotBlank(storeInfoJson)){
-
StoreInfoVO storeInfo = JSON.parseObject(storeInfoJson,StoreInfoVO.class);
-
sceneInfoVO.setStore_info(storeInfo);
-
h5V3WxPayVO.setScene_info(sceneInfoVO);
-
}
-
SettleInfoVO settleInfo = new SettleInfoVO();
-
String profitSharing = req.getExtension().get("profitSharing");
-
if("true".equals(profitSharing)){
-
settleInfo.setProfit_sharing(true);
-
}else{
-
settleInfo.setProfit_sharing(false);
-
}
-
h5V3WxPayVO.setSettle_info(settleInfo);
-
String jsonStr = JSON.toJSONString(h5V3WxPayVO);
-
logger.info("【微信V3支付】->请求参数JSON:{}",jsonStr);
-
// 发送请求
-
String url =properties.getProperty(WXPAYFundChannelKey.JSAPI_CREAT_URL);
-
logger.info("【微信V3支付配置】->【请求URL】:{}",url);
-
//创建httpclient对象
-
CloseableHttpClient client = HttpClients.createDefault();
-
//创建post方式请求对象
-
HttpPost httpPost = new HttpPost(url_prex url);
-
//装填参数
-
StringEntity s = new StringEntity(jsonStr, charset);
-
s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
-
"application/json"));
-
//设置参数到请求对象中
-
httpPost.setEntity(s);
-
String mchSerialNo = properties.getProperty(WXPAYFundChannelKey.MCH_SERIAL_NO);
-
String privateKeyFilePath = properties.getProperty(WXPAYFundChannelKey.PRIVATE_KEY_FILE_PATH);
-
String token = WeiXinPaySignUtils.getToken("POST", HttpUrl.parse(url_prex url), mchId, mchSerialNo, privateKeyFilePath, jsonStr);
-
//设置header信息
-
//指定报文头【Content-type】、【User-Agent】
-
httpPost.setHeader("Content-type", "application/json");
-
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
-
httpPost.setHeader("Accept", "application/json");
-
httpPost.setHeader("Authorization",
-
"WECHATPAY2-SHA256-RSA2048 " token);
-
//执行请求操作,并拿到结果(同步阻塞)
-
CloseableHttpResponse response = client.execute(httpPost);
-
//获取结果实体
-
HttpEntity entity = response.getEntity();
-
String body = "";
-
if (entity != null) {
-
//按指定编码转换结果实体为String类型
-
body = EntityUtils.toString(entity, charset);
-
}
-
EntityUtils.consume(entity);
-
//释放链接
-
response.close();
-
String responseJson = JSONObject.fromObject(body).getString("prepay_id");
-
logger.info("【微信V3支付】->返回结果->prepay_id:{}",responseJson);
-
StatusLine statusLine = response.getStatusLine();
-
if(StringUtils.isBlank(responseJson)){
-
result.setApiResultCode(String.valueOf(statusLine.getStatusCode()));
-
result.setApiResultMessage(statusLine.getReasonPhrase());
-
result.setResultMessage(statusLine.getReasonPhrase());
-
result.setSuccess(false);
-
result.setRealAmount(req.getAmount());
-
result.setProcessTime(new Date());
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setApiType(FundChannelApiType.DEBIT);
-
result.setExtension("");
-
result.setInstOrderNo(req.getInstOrderNo());
-
logger.info("返回支付平台结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}else{
-
//
-
JSONObject jsonObject = WxTuneUp(responseJson, appId, privateKeyFilePath);
-
result.setApiResultCode("0000");
-
result.setApiResultSubCode("SUCCESS");
-
result.setApiResultMessage("微信支付下单成功");
-
result.setResultMessage("微信支付下单成功");
-
result.setSuccess(true);
-
result.setRealAmount(req.getAmount());
-
result.setProcessTime(new Date());
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setApiType(FundChannelApiType.DEBIT);
-
result.setExtension(jsonObject.toString());
-
result.setInstOrderNo(req.getInstOrderNo());
-
logger.info("返回支付平台结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
-
}catch (Exception e) {
-
logger.error("资金源[" req.getFundChannelCode() "]支付异常", e);
-
Map<String, String> map = new HashMap<String,String>();
-
map.put("fundsChannel", req.getFundChannelCode());
-
result.setExtension(JSON.toJSONString(map));
-
result = builFalidFundinResponse(req, "支付异常", ReturnCode.FAILED, ReturnCode.FAILED,
-
StringUtils.EMPTY_STRING);
-
ResultWrapper.error().putData(result);
-
}
-
return null;
-
}
-
/**
-
* 微信调起支付参数
-
* 返回参数如有不理解 请访问微信官方文档
-
* https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_1_4.shtml
-
*
-
* @param prepayId 微信下单返回的prepay_id
-
* @param appId 应用ID(appid)
-
* @param privateKeyFilePath 私钥的地址
-
* @return 当前调起支付所需的参数
-
* @throws Exception
-
*/
-
private JSONObject WxTuneUp(String prepayId, String appId, String privateKeyFilePath) throws Exception {
-
String time = System.currentTimeMillis() / 1000 "";
-
String nonceStr = UUID.randomUUID().toString().replace("-", "");
-
String packageStr = "prepay_id=" prepayId;
-
ArrayList<String> list = new ArrayList<>();
-
list.add(appId);
-
list.add(time);
-
list.add(nonceStr);
-
list.add(packageStr);
-
//加载签名
-
String packageSign = WeiXinPaySignUtils.sign(WeiXinPaySignUtils.buildSignMessage(list).getBytes(), privateKeyFilePath);
-
JSONObject jsonObject = new JSONObject();
-
jsonObject.put("appId", appId);
-
jsonObject.put("timeStamp", time);
-
jsonObject.put("nonceStr", nonceStr);
-
jsonObject.put("packages", packageStr);
-
jsonObject.put("signType", "RSA");
-
jsonObject.put("paySign", packageSign);
-
return jsonObject;
-
}
查询:
-
@PostMapping("/query")
-
public ResultWrapper<Map<String,Object>> query(@RequestBody String request) {
-
-
logger.info("PayChannelOrder->Channel微信V3支付结果查询请求参数:" request);
-
ChannelFundResult result = new ChannelFundResult();
-
QueryRequest req = JSON.parseObject(request, QueryRequest.class);
-
result.setApiType(req.getApiType());
-
logger.info("PayChannelOrder->Channel微信V3支付结果查询请求参数转换对象:" req);
-
Properties properties = propertyHelper.getProperties(req.getFundChannelCode());
-
try {
-
String mock_switch = properties.getProperty(WXPAYFundChannelKey.MOCK_SWITCH);
-
if("true".equals(mock_switch)){//开关开启返回mock数据
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setSuccess(true);
-
result.setApiType(req.getApiType());
-
result.setRealAmount(req.getAmount());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode("0000");
-
result.setApiResultSubCode("SUCCESS");
-
result.setApiResultMessage("注意:当前为mock数据!:查询成功");
-
result.setResultMessage("注意:当前为mock数据!:交易成功");
-
result.setApiResultSubMessage("注意:当前为mock数据!:交易成功");
-
logger.info("注意这是mock数据!");
-
return ResultWrapper.ok().putData(result);
-
}
-
String mchId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHID);
-
String url =properties.getProperty(WXPAYFundChannelKey.QUERY_ORDER_URL);
-
url = url.replace("{out_trade_no}",req.getInstOrderNo());
-
url = url.concat("?mchid=").concat(mchId);
-
logger.info("【微信V3支付】->请求URL:{}",url);
-
String mchSerialNo = properties.getProperty(WXPAYFundChannelKey.MCH_SERIAL_NO);
-
String privateKeyFilePath = properties.getProperty(WXPAYFundChannelKey.PRIVATE_KEY_FILE_PATH);
-
String token = WeiXinPaySignUtils.getToken("GET", HttpUrl.parse(url_prex url),
-
mchId, mchSerialNo, privateKeyFilePath, "");
-
//创建httpclient对象
-
CloseableHttpClient client = HttpClients.createDefault();
-
HttpGet httpGet = new HttpGet(url_prex url);
-
//设置header信息
-
//指定报文头【Content-type】、【User-Agent】
-
httpGet.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
-
httpGet.setHeader("Accept", "application/json");
-
httpGet.setHeader("Authorization",
-
"WECHATPAY2-SHA256-RSA2048 " token);
-
CloseableHttpResponse response = client.execute(httpGet);
-
String bodyString = EntityUtils.toString(response.getEntity());//得到我的这个请求的body请求信息
-
logger.info("【微信V3支付】->返回结果->Entity:{}",bodyString);
-
Map<String, String> resultMap = MapUtil.jsonToMap(bodyString);
-
int statusCode = response.getStatusLine().getStatusCode();
-
if (statusCode == 200) {
-
if("SUCCESS".equals(resultMap.get("trade_state"))){
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(resultMap.get("trade_state"));
-
result.setRealAmount(req.getAmount());
-
result.setApiResultSubCode(resultMap.get("trade_state"));
-
result.setResultMessage(resultMap.get("trade_state_desc"));
-
result.setApiResultMessage(resultMap.get("trade_state_desc"));
-
result.setApiResultSubMessage(resultMap.get("trade_state_desc"));
-
result.setSuccess(true);
-
result.setInstReturnOrderNo(resultMap.get("transaction_id"));
-
result.setExtension(bodyString);
-
logger.info("查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}else{
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(resultMap.get("trade_state"));
-
result.setRealAmount(req.getAmount());
-
result.setApiResultSubCode(resultMap.get("trade_state"));
-
result.setResultMessage(resultMap.get("trade_state_desc"));
-
result.setApiResultMessage(resultMap.get("trade_state_desc"));
-
result.setApiResultSubMessage(resultMap.get("trade_state_desc"));
-
result.setSuccess(false);
-
result.setInstReturnOrderNo(resultMap.get("transaction_id"));
-
result.setExtension(bodyString);
-
logger.info("查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
}else if(statusCode == 204){
-
logger.info("请求状态码为204");
-
}else {
-
logger.info("查询订单失败,响应码 ==>{},响应信息是===>{}",statusCode,bodyString);
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(String.valueOf(statusCode));
-
result.setRealAmount(req.getAmount());
-
result.setApiResultMessage(bodyString);
-
result.setResultMessage(bodyString);
-
result.setSuccess(false);
-
result.setExtension(bodyString);
-
logger.info("查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
}catch (Exception ex) {
-
logger.error("查询异常", ex);
-
result = buildFaildChannelFundResult("签约支付异常", ReturnCode.FAILED, FundChannelApiType.SINGLE_QUERY);
-
return ResultWrapper.error().putData(result);
-
}
-
return null;
-
}
支付成功异步通知:
-
@PostMapping("/notify/{fundChannelCode}")
-
public Object notify(@PathVariable("fundChannelCode") String fundChannelCode,@RequestBody String data) {
-
logger.info("通知数据:" data);
-
logger.info("fundChannelCode:" fundChannelCode);
-
ChannelRequest channelRequest = new ChannelRequest();
-
channelRequest.setFundChannelCode(fundChannelCode);
-
channelRequest.setApiType(FundChannelApiType.DEBIT);
-
channelRequest.getExtension().put("notifyMsg", data);
-
Properties properties = propertyHelper.getProperties(channelRequest.getFundChannelCode());
-
String v3key =properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHSECRETKEY);
-
ChannelFundResult result = wxPayResultNotifyService.v3notify(channelRequest,v3key);
-
//调用发送MQ消息,更新订单状态
-
Map<String,Object> map = new HashMap<String,Object>();
-
map.put("message", result);
-
//消息被序列化后发送
-
AmqoRequrst requrst = new AmqoRequrst();
-
requrst.setExchange("exchange.payresult.process");
-
requrst.setRoutingKey("key.payresult.process");
-
requrst.setMap(map);
-
logger.info("发送MQ消息:" JSON.toJSONString(requrst));
-
amqpService.sendMessage(requrst);
-
logger.info("MQ消息发送完毕");
-
//通知业务系统
-
//resultNotifyFacade.notifyBiz(instOrderResult.getInstOrderNo(),xmlToMap);
-
String return_result = "{ \n"
-
" \"code\": \"SUCCESS\",\n"
-
" \"message\": \"成功\"\n"
-
"}";
-
return return_result;
-
}
退款:
-
@PostMapping("/refund")
-
public ResultWrapper<Map<String,Object>> refund(@RequestBody String request) {
-
logger.info("PayChannelOrder->Channel微信支付V3退款渠道请求参数:" request);
-
ChannelFundResult result = new ChannelFundResult();
-
ChannelFundRequest req = JSON.parseObject(request, ChannelFundRequest.class);
-
logger.info("PayChannelOrder->Channel微信支付V3退款渠道请求参数转换对象:" req);
-
Properties properties = propertyHelper.getProperties(req.getFundChannelCode());
-
//判断mock开关是否打开,是否要返回mock数据
-
String mock_switch = properties.getProperty(WXPAYFundChannelKey.MOCK_SWITCH);
-
if("true".equals(mock_switch)){//开关开启返回mock数据
-
result.setApiType(req.getApiType());
-
result.setRealAmount(req.getAmount());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setProcessTime(new Date());
-
result = MockResultData.mockResule(result);
-
logger.info("注意这是mock数据!");
-
return ResultWrapper.ok().putData(result);
-
}
-
try {
-
RefundVO refundVO = new RefundVO();
-
// transaction_id
-
String transactionId = req.getExtension().get("transactionId");
-
if(StringUtils.isNotBlank(transactionId)){
-
refundVO.setTransaction_id(transactionId);
-
}
-
String outTradeNo = req.getExtension().get("originalOutTradeNo");
-
if(StringUtils.isNotBlank(outTradeNo)){
-
refundVO.setOut_trade_no(outTradeNo);
-
}
-
refundVO.setOut_refund_no(req.getInstOrderNo());
-
String refundReason = req.getExtension().get("refundReason");
-
if(StringUtils.isNotBlank(refundReason)){
-
refundVO.setReason(refundReason);
-
}
-
String refundNotifyUrl = req.getExtension().get("refundNotifyUrl");
-
if(StringUtils.isNotBlank(refundNotifyUrl)){
-
refundVO.setNotify_url(refundNotifyUrl);
-
}
-
refundVO.setFunds_account("AVAILABLE");
-
RefounAmount amount = new RefounAmount();
-
String originalAmount = req.getExtension().get("originalAmount");
-
String refounAmount = req.getExtension().get("refounAmount");
-
int total = Integer.parseInt(AmountUtils.Yuan2Fen(originalAmount));
-
int refund = Integer.parseInt(AmountUtils.Yuan2Fen(refounAmount));
-
amount.setTotal(total);
-
amount.setCurrency("CNY");
-
amount.setRefund(refund);
-
refundVO.setAmount(amount);
-
String jsonStr = JSON.toJSONString(refundVO);
-
logger.info("【微信V3支付】->退款请求参数JSON:{}",jsonStr);
-
// 发送请求
-
String url =properties.getProperty(WXPAYFundChannelKey.REFUNDS_QUERY_URL);
-
logger.info("【微信V3支付配置】->【退款请求URL】:{}",url);
-
//创建httpclient对象
-
CloseableHttpClient client = HttpClients.createDefault();
-
//创建post方式请求对象
-
HttpPost httpPost = new HttpPost(url_prex url);
-
//装填参数
-
StringEntity s = new StringEntity(jsonStr, charset);
-
s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
-
"application/json"));
-
//设置参数到请求对象中
-
httpPost.setEntity(s);
-
String mchSerialNo = properties.getProperty(WXPAYFundChannelKey.MCH_SERIAL_NO);
-
String privateKeyFilePath = properties.getProperty(WXPAYFundChannelKey.PRIVATE_KEY_FILE_PATH);
-
String mchId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHID);
-
logger.info("【微信V3支付配置】->【微信商户ID】:" mchId);
-
String token = WeiXinPaySignUtils.getToken("POST", HttpUrl.parse(url_prex url), mchId, mchSerialNo, privateKeyFilePath, jsonStr);
-
//设置header信息
-
//指定报文头【Content-type】、【User-Agent】
-
httpPost.setHeader("Content-type", "application/json");
-
httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
-
httpPost.setHeader("Accept", "application/json");
-
httpPost.setHeader("Authorization",
-
"WECHATPAY2-SHA256-RSA2048 " token);
-
//执行请求操作,并拿到结果(同步阻塞)
-
CloseableHttpResponse response = client.execute(httpPost);
-
//获取结果实体
-
HttpEntity entity = response.getEntity();
-
String body = "";
-
if (entity != null) {
-
//按指定编码转换结果实体为String类型
-
body = EntityUtils.toString(entity, charset);
-
}
-
EntityUtils.consume(entity);
-
//释放链接
-
response.close();
-
JSONObject jsonObject = JSONObject.fromObject(body);
-
logger.info("【微信V3支付】->返回结果->:{}",jsonObject);
-
StatusLine statusLine = response.getStatusLine();
-
logger.info("【微信支付】发起退款, request={}", JsonUtil.toJson(refundVO));
-
int statusCode = response.getStatusLine().getStatusCode();
-
if(statusCode == 200){
-
String refundId = (String)jsonObject.get("refund_id");
-
if(StringUtils.isBlank(refundId)){
-
result.setApiResultCode(String.valueOf(statusLine.getStatusCode()));
-
result.setApiResultMessage(statusLine.getReasonPhrase());
-
result.setResultMessage(statusLine.getReasonPhrase());
-
result.setSuccess(false);
-
result.setRealAmount(req.getAmount());
-
result.setProcessTime(new Date());
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setApiType(FundChannelApiType.DEBIT);
-
result.setExtension(body);
-
result.setInstOrderNo(req.getInstOrderNo());
-
logger.info("返回支付平台结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}else{
-
result.setApiResultCode((String)jsonObject.get("status"));
-
result.setApiResultMessage((String)jsonObject.get("user_received_account"));
-
result.setResultMessage((String)jsonObject.get("user_received_account"));
-
result.setSuccess(true);
-
result.setRealAmount(new BigDecimal(refounAmount));
-
result.setProcessTime(new Date());
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setApiType(FundChannelApiType.DEBIT);
-
result.setExtension(body);
-
result.setInstOrderNo(req.getInstOrderNo());
-
logger.info("返回支付平台结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
}else{
-
logger.info("查询订单失败,响应码 ==>{},响应信息是===>{}",statusCode,body);
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(String.valueOf(statusCode));
-
result.setRealAmount(req.getAmount());
-
result.setApiResultMessage(body);
-
result.setResultMessage(body);
-
result.setSuccess(false);
-
result.setExtension(body);
-
logger.info("查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
-
}catch (Exception e) {
-
logger.error("资金源[" req.getFundChannelCode() "]支付异常", e);
-
Map<String, String> map = new HashMap<String,String>();
-
map.put("fundsChannel", req.getFundChannelCode());
-
result.setExtension(JSON.toJSONString(map));
-
result = builFalidFundinResponse(req, "支付异常", ReturnCode.FAILED, ReturnCode.FAILED,
-
StringUtils.EMPTY_STRING);
-
ResultWrapper.error().putData(result);
-
}
-
return null;
-
}
退款查询:
-
@PostMapping("/refundQuery")
-
public ResultWrapper<Map<String,Object>> refundQuery(@RequestBody String request) {
-
-
logger.info("PayChannelOrder->Channel微信支付退款结果查询请求参数:" request);
-
ChannelFundResult result = new ChannelFundResult();
-
QueryRequest req = JSON.parseObject(request, QueryRequest.class);
-
result.setApiType(req.getApiType());
-
logger.info("PayChannelOrder->Channel微信支付退款结果查询请求参数转换对象:" req);
-
Properties properties = propertyHelper.getProperties(req.getFundChannelCode());
-
try {
-
String mock_switch = properties.getProperty(WXPAYFundChannelKey.MOCK_SWITCH);
-
if("true".equals(mock_switch)){//开关开启返回mock数据
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setSuccess(true);
-
result.setApiType(req.getApiType());
-
result.setRealAmount(req.getAmount());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode("0000");
-
result.setApiResultSubCode("SUCCESS");
-
result.setApiResultMessage("注意:当前为mock数据!:查询成功");
-
result.setResultMessage("注意:当前为mock数据!:交易成功");
-
result.setApiResultSubMessage("注意:当前为mock数据!:交易成功");
-
logger.info("注意这是mock数据!");
-
return ResultWrapper.ok().putData(result);
-
}
-
String mchId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHID);
-
String url =properties.getProperty(WXPAYFundChannelKey.REFUNDS_QUERY_URL);
-
url = url.replace("{out_refund_no}",req.getOriginalInstOrderNo());
-
logger.info("【微信V3支付】->退款查询请求URL:{}",url);
-
String mchSerialNo = properties.getProperty(WXPAYFundChannelKey.MCH_SERIAL_NO);
-
String privateKeyFilePath = properties.getProperty(WXPAYFundChannelKey.PRIVATE_KEY_FILE_PATH);
-
String token = WeiXinPaySignUtils.getToken("GET", HttpUrl.parse(url_prex url),
-
mchId, mchSerialNo, privateKeyFilePath, "");
-
//创建httpclient对象
-
CloseableHttpClient client = HttpClients.createDefault();
-
HttpGet httpGet = new HttpGet(url_prex url);
-
//设置header信息
-
//指定报文头【Content-type】、【User-Agent】
-
httpGet.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
-
httpGet.setHeader("Accept", "application/json");
-
httpGet.setHeader("Authorization",
-
"WECHATPAY2-SHA256-RSA2048 " token);
-
CloseableHttpResponse response = client.execute(httpGet);
-
String bodyString = EntityUtils.toString(response.getEntity());//得到我的这个请求的body请求信息
-
logger.info("【微信V3支付】->返回结果->Entity:{}",bodyString);
-
Map<String, String> resultMap = MapUtil.jsonToMap(bodyString);
-
int statusCode = response.getStatusLine().getStatusCode();
-
String refund_id = resultMap.get("refund_id");
-
if (statusCode == 200 && StringUtils.isNotBlank(refund_id)) {
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(resultMap.get("status"));
-
Map<String,String> amountMap = MapUtil.jsonToMap(resultMap.get("amount"));
-
result.setRealAmount(new BigDecimal(AmountUtils.Fen2Yuan(Long.parseLong(amountMap.get("refund")))));
-
result.setResultMessage(resultMap.get("user_received_account"));
-
result.setApiResultMessage(resultMap.get("user_received_account"));
-
result.setSuccess(true);
-
result.setInstReturnOrderNo(refund_id);
-
result.setExtension(bodyString);
-
logger.info("退款查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
-
}else {
-
logger.info("查询订单失败,响应码 ==>{},响应信息是===>{}",statusCode,bodyString);
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setApiResultCode(String.valueOf(statusCode));
-
result.setRealAmount(req.getAmount());
-
result.setApiResultMessage(bodyString);
-
result.setResultMessage(bodyString);
-
result.setSuccess(false);
-
result.setExtension(bodyString);
-
logger.info("查询响应结果:" JSON.toJSONString(result));
-
return ResultWrapper.ok().putData(result);
-
}
-
}catch (Exception ex) {
-
logger.error("查询异常", ex);
-
result = buildFaildChannelFundResult("签约支付异常", ReturnCode.FAILED, FundChannelApiType.SINGLE_QUERY);
-
return ResultWrapper.error().putData(result);
-
}
-
}
下载对账文件
-
@PostMapping("/downloadBill")
-
public ResultWrapper<Map<String,Object>> downloadBill(@RequestBody String request) {
-
logger.info("PayChannelOrder->Channel微信V3支付账单请求参数:" request);
-
ChannelFundResult result = new ChannelFundResult();
-
ChannelFundRequest req = JSON.parseObject(request, ChannelFundRequest.class);
-
logger.info("PayChannelOrder->Channel微信V3支付账单渠道请求参数转换对象:" req);
-
Properties properties = propertyHelper.getProperties(req.getFundChannelCode());
-
//判断mock开关是否打开,是否要返回mock数据
-
String mock_switch = properties.getProperty(WXPAYFundChannelKey.MOCK_SWITCH);
-
if("true".equals(mock_switch)){//开关开启返回mock数据
-
result.setApiType(req.getApiType());
-
result.setRealAmount(req.getAmount());
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setProcessTime(new Date());
-
result = MockResultData.mockResule(result);
-
logger.info("注意这是mock数据!");
-
return ResultWrapper.ok().putData(result);
-
}
-
try {
-
Map<String, String> extension = req.getExtension();
-
String bill_dowload_url = properties.getProperty(WXPAYFundChannelKey.KEY_TRADE_BILL_URL);
-
logger.info("【微信对账下载】->【对账单下载】:" bill_dowload_url);
-
String billType = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_BILL_TYPE);
-
// 对账类型: ALL,返回当日所有订单信息,默认值 SUCCESS,返回当日成功支付的订单 REFUND,返回当日退款订单
-
logger.info("【微信对账下载】->【微信对账类型】:" billType);
-
String billDirPath = properties.getProperty(WXPAYFundChannelKey.KEY_BILL_DIR_PATH);
-
logger.info("【微信对账下载】->【对账文件路径】:" billDirPath);
-
Map<String,String> map = new HashMap<String,String>();
-
String mchSerialNo = properties.getProperty(WXPAYFundChannelKey.MCH_SERIAL_NO);
-
logger.info("【微信对账下载】->【微信证书编号】:" mchSerialNo);
-
String privateKeyFilePath = properties.getProperty(WXPAYFundChannelKey.PRIVATE_KEY_FILE_PATH);
-
logger.info("【微信对账下载】->【微信秘钥路径】:" privateKeyFilePath);
-
String mchId = properties.getProperty(WXPAYFundChannelKey.KEY_WEIXIN_MCHID);
-
logger.info("【微信对账下载】->【微信商户号】:" mchId);
-
map.put("bill_dowload_url", url_prex bill_dowload_url);
-
map.put("bill_date", extension.get("billDate"));
-
map.put("billDirPath", billDirPath);
-
map.put("bill_type", billType);
-
map.put("tar_type", "GZIP");
-
map.put("mchSerialNo", mchSerialNo);
-
map.put("privateKeyFilePath", privateKeyFilePath);
-
map.put("mchId", mchId);
-
File file = winXinFileDown.v3fileDown(map);
-
result.setSuccess(true);
-
String bill_file = file.getCanonicalPath();
-
Map<String, String> extensionMap = new HashMap<String, String>();
-
extensionMap.put("bill_file", bill_file);
-
result.setInstOrderNo(req.getInstOrderNo());
-
result.setExtension(JSON.toJSONString(extensionMap));
-
result.setFundChannelCode(req.getFundChannelCode());
-
result.setApiResultCode("0000");
-
result.setRealAmount(req.getAmount());
-
result.setResultMessage("对账文件下载成功");
-
result.setApiResultMessage("对账文件下载成功");
-
result.setSuccess(true);
-
return ResultWrapper.ok().putData(result);
-
}catch (Exception e) {
-
logger.error("资金源[" req.getFundChannelCode() "]账单下载异常", e);
-
Map<String, String> map = new HashMap<String,String>();
-
map.put("fundsChannel", req.getFundChannelCode());
-
result.setExtension(JSON.toJSONString(map));
-
result = builFalidFundinResponse(req, "账单下载异常", ReturnCode.FAILED, ReturnCode.FAILED,
-
StringUtils.EMPTY_STRING);
-
ResultWrapper.error().putData(result);
-
}
-
return null;
-
}
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgcehie
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01