From cfd935231bbfcc171e9250ff8cb67008f49b929a Mon Sep 17 00:00:00 2001 From: KKSU <15274802129@163.com> Date: Wed, 17 Jan 2024 14:00:52 +0800 Subject: [PATCH] fapiao --- src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java | 228 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 170 insertions(+), 58 deletions(-) diff --git a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java index 0182525..f35b58f 100644 --- a/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java +++ b/src/main/java/cc/mrbird/febs/pay/service/impl/WxFaPiaoServiceImpl.java @@ -11,19 +11,25 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.aliyun.oss.internal.SignUtils; import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier; import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; 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.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.AesUtil; +import com.wechat.pay.java.core.notification.Resource; +import io.undertow.util.Certificates; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import okhttp3.HttpUrl; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; @@ -32,15 +38,19 @@ import org.apache.http.util.EntityUtils; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; +import org.springframework.util.Base64Utils; +import org.springframework.web.bind.annotation.RequestBody; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.nio.charset.StandardCharsets; import java.security.*; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.text.SimpleDateFormat; import java.util.Base64; import java.util.HashMap; import java.util.Map; @@ -200,71 +210,162 @@ } @Override - public Map<String, Object> fapiaoCallBack(HttpServletResponse response, HttpServletRequest request) { - log.info("微信电子发票回调接口...."); + public Map<String, Object> fapiaoCallBack(HttpServletRequest request, @RequestBody Map<String, Object> requestBody) throws IOException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { Map<String,Object> map = new HashMap<>(); - try { - BufferedReader br = request.getReader(); - String str = null; - StringBuilder sb = new StringBuilder(); - while ((str = br.readLine())!=null) { - sb.append(str); - } - // 构建request,传入必要参数 - NotificationRequest requests = new NotificationRequest.Builder() - .withSerialNumber(request.getHeader("Wechatpay-Serial")) - .withNonce(request.getHeader("Wechatpay-Nonce")) - .withTimestamp(request.getHeader("Wechatpay-Timestamp")) - .withSignature(request.getHeader("Wechatpay-Signature")) - .withBody(String.valueOf(sb)) - .build(); - //验签 - NotificationHandler handler = new NotificationHandler(getVerifier(AppContants.WX_CARD_NUM), xcxProperties.getWecharpaySecretV3().getBytes(StandardCharsets.UTF_8)); - //解析请求体 - Notification notification = handler.parse(requests); - log.info("微信电子发票回调接口....解析请求体:"+notification.toString()); - String decryptData = notification.getDecryptData();//可能是支付业务的回调数据 - log.info("微信电子发票回调接口....decryptData:"+notification.toString()); - Notification.Resource resource = notification.getResource();//电子发票的回调加密数据 - log.info("微信电子发票回调接口....resource:"+notification.toString()); + String signature = request.getHeader("Wechatpay-Signature"); + String timestamp = request.getHeader("Wechatpay-Timestamp"); + String nonce = request.getHeader("Wechatpay-Nonce"); + //平台证书序列号不是API证书序列号 + String serial = request.getHeader("Wechatpay-Serial"); + String body = com.alibaba.fastjson.JSONObject.toJSONString(requestBody); + log.info("头信息---签名:" + signature); + log.info("头信息---时间戳:" + timestamp); + log.info("头信息---随机字符:" + nonce); + log.info("头信息---平台证书序列号:" + serial); + log.info("获取到的body信息:" + body); + //验签 + boolean signCheck = verifySign(request, body); +// boolean signCheck = signCheck(timestamp, nonce, requestBody, signature); + log.info("验签结果:" + signCheck); + if (signCheck) { +// //解密参数 +// Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class); +// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8")); +// String string = aesUtil.decryptToString(resource.getAssociated_data().getBytes("utf-8"), resource.getNonce().getBytes("utf-8"), resource.getCiphertext()); +// ComplaintInfo complaintInfo = JSONObject.parseObject(string, ComplaintInfo.class); +// //获取投诉详情 +// ComplaintDetail complaintDetail = CommonUtils.GetComplaintsInfo(complaintInfo.getComplaint_id()); +// data.put("code", "SUCCESS"); +// data.put("message", "成功"); +// return data; + try { + //解析请求体 +// Resource resource = com.alibaba.fastjson.JSONObject.parseObject(com.alibaba.fastjson.JSONObject.toJSONString(requestBody.get("resource")), Resource.class); + Notification notification = com.alibaba.fastjson.JSONObject.parseObject(String.valueOf(body),Notification.class); + log.info("微信电子发票回调接口....解析请求体:"+notification.toString()); + String decryptData = notification.getDecryptData();//可能是支付业务的回调数据 + log.info("微信电子发票回调接口....decryptData:"+notification.toString()); + Notification.Resource resource = notification.getResource();//电子发票的回调加密数据 + log.info("微信电子发票回调接口....resource:"+notification.toString()); - if ("FAPIAO.USER_APPLIED".equals(notification.getEventType())//用户发票抬头填写完成类型:FAPIAO.USER_APPLIED - && !"encryptresource".equals(notification.getResourceType())) {//通知的资源数据类型,确认成功通知为encryptresource。 - //解密 - AesUtil aesUtil = new AesUtil(xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); - String decryptToString = aesUtil.decryptToString( - resource.getAssociatedData().getBytes("utf-8"), - resource.getNonce().getBytes("utf-8"), - resource.getCiphertext()); - log.info("微信电子发票回调接口....resource解密:"+decryptToString); + if ("FAPIAO.USER_APPLIED".equals(notification.getEventType())//用户发票抬头填写完成类型:FAPIAO.USER_APPLIED + && !"encryptresource".equals(notification.getResourceType())) {//通知的资源数据类型,确认成功通知为encryptresource。 + //解密 + AesUtil aesUtil = new AesUtil(xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); + String decryptToString = aesUtil.decryptToString( + resource.getAssociatedData().getBytes("utf-8"), + resource.getNonce().getBytes("utf-8"), + resource.getCiphertext()); + log.info("微信电子发票回调接口....resource解密:"+decryptToString); - JSONObject parseObj = JSONUtil.parseObj(decryptToString); + JSONObject parseObj = JSONUtil.parseObj(decryptToString); - log.info("微信电子发票回调接口....resource解密-JSONObject:"+parseObj); + log.info("微信电子发票回调接口....resource解密-JSONObject:"+parseObj); - String mchid = String.valueOf(parseObj.get("mchid")); - String fapiao_apply_id = String.valueOf(parseObj.get("fapiao_apply_id")); - String apply_time = String.valueOf(parseObj.get("apply_time")); - MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectByOrderNo(fapiao_apply_id); - if(ObjectUtil.isNotEmpty(mallOrderInfo)){ - //省略查询订单 - //此处处理业务 - map.put("code","SUCCESS"); - map.put("message","成功"); - //消息推送成功 - return map; + String mchid = String.valueOf(parseObj.get("mchid")); + String fapiao_apply_id = String.valueOf(parseObj.get("fapiao_apply_id")); + String apply_time = String.valueOf(parseObj.get("apply_time")); + MallOrderInfo mallOrderInfo = mallOrderInfoMapper.selectByOrderNo(fapiao_apply_id); + if(ObjectUtil.isNotEmpty(mallOrderInfo)){ + //省略查询订单 + //此处处理业务 + map.put("code","SUCCESS"); + map.put("message","成功"); + //消息推送成功 + return map; + } } + map.put("code","RESOURCE_NOT_EXISTS"); + map.put("message", "订单不存在"); + return map; + }catch (Exception e) { + e.printStackTrace(); } - map.put("code","RESOURCE_NOT_EXISTS"); - map.put("message", "订单不存在"); - return map; - }catch (Exception e) { - e.printStackTrace(); } map.put("code","FAIL"); map.put("message", "失败"); return map; } + +// /** +// * 验证签名 +// * +// * @param timestamp 微信平台传入的时间戳 +// * @param nonce 微信平台传入的随机字符串 +// * @param requestBody 微信平台传入的消息体 +// * @param signature 微信平台传入的签名 +// * @return +// * @throws NoSuchAlgorithmException +// * @throws SignatureException +// * @throws IOException +// * @throws InvalidKeyException +// */ +// public static boolean signCheck(String timestamp, String nonce, Map<String, Object> requestBody, String signature) throws NoSuchAlgorithmException, SignatureException, IOException, InvalidKeyException { +// //构造验签名串 +// String signatureStr = timestamp + "\n" + nonce + "\n" + com.alibaba.fastjson.JSONObject.toJSONString(requestBody) + "\n"; +// // 加载SHA256withRSA签名器 +// Signature signer = Signature.getInstance("SHA256withRSA"); +// // 用微信平台公钥对签名器进行初始化(调上一节中的获取平台证书方法) +// signer.initVerify(getCertificates()); +// // 把我们构造的验签名串更新到签名器中 +// signer.update(signatureStr.getBytes(StandardCharsets.UTF_8)); +// // 把请求头中微信服务器返回的签名用Base64解码 并使用签名器进行验证 +// boolean result = signer.verify(Base64Utils.decodeFromString(signature)); +// return result; +// } + + + /** + * 获取平台证书 + */ +// public static X509Certificate getCertificates() throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException { +// SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); +// CloseableHttpClient httpClient = CommonUtils.httpClient(); +// //请求URL +// HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/certificates"); +// httpGet.setHeader("Accept", "application/json"); +// //生成签名 +// httpGet.setHeader("Authorization ", SignUtils.getSign("GET", HttpUrl.parse("https://api.mch.weixin.qq.com/v3/certificates"), "")); +// httpGet.setHeader("User-Agent", "https://zh.wikipedia.org/wiki/User_agent"); +// //完成签名并执行请求 +// CloseableHttpResponse response = httpClient.execute(httpGet); +// X509Certificate x509Certificate = null; +// try { +// int statusCode = response.getStatusLine().getStatusCode(); +// if (statusCode == 200) { //处理成功 +//// System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())); +// CertificateVo certificateVo = JSONObject.parseObject(EntityUtils.toString(response.getEntity()), CertificateVo.class); +// for (Certificates certificates : certificateVo.getData()) { +// if (format.parse(certificates.getEffective_time()).before(new Date()) && format.parse(certificates.getExpire_time()).after(new Date())) { +// EncryptCertificate encrypt_certificate = certificates.getEncrypt_certificate(); +// //解密 +// AesUtil aesUtil = new AesUtil(CommonParameters.apiV3Key.getBytes("utf-8")); +// String pulicKey = aesUtil.decryptToString(encrypt_certificate.getAssociated_data().getBytes("utf-8"), encrypt_certificate.getNonce().getBytes("utf-8"), encrypt_certificate.getCiphertext()); +// +// //获取平台证书 +// final CertificateFactory cf = CertificateFactory.getInstance("X509"); +// +// ByteArrayInputStream inputStream = new ByteArrayInputStream(pulicKey.getBytes(StandardCharsets.UTF_8)); +// +// x509Certificate = (X509Certificate) cf.generateCertificate(inputStream); +// } +// } +// return x509Certificate; +// } else if (statusCode == 204) { //处理成功,无返回Body +// System.out.println("success"); +// return x509Certificate; +// } else { +// System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity())); +// return x509Certificate; +// } +// } catch (GeneralSecurityException | ParseException e) { +// e.printStackTrace(); +// return null; +// } finally { +// response.close(); +// CommonUtils.after(httpClient); +// } +// } /** * 功能描述: 验证签名 @@ -326,6 +427,15 @@ verifier = new AutoUpdateCertificatesVerifier(credentials , xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier); + +// AutoUpdateCertificatesVerifier verifierNew = new AutoUpdateCertificatesVerifier( +// new WechatPay2Credentials( +// xcxProperties.getWecharpayMchid(), +// new PrivateKeySigner( +// mchSerialNo, +// privateKey)), +// xcxProperties.getWecharpaySecretV3().getBytes("utf-8")); +// new WechatPay2Validator(verifierNew).; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { @@ -373,11 +483,13 @@ } public static void main(String[] args) { - HeaderDto headerDto = new HeaderDto(); - headerDto.setCallback_url("https://api.blnka.cn/api/xcxPay/fapiaoCallBack"); - headerDto.setShow_fapiao_cell(true); - String parseObj = JSONUtil.parseObj(headerDto).toString(); - System.out.println(parseObj); + byte[] bytes = new byte[0]; + try { + bytes = "DVREEVEREBERykpbgqcfsdsfggsdg".getBytes("utf-8"); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + System.out.println(bytes.length); } } -- Gitblit v1.9.1