把微信证书文件放入resources/wxCert文件夹中,读取证书文件类是pipIrrGlobal模块的WxCertUtil类。
New file |
| | |
| | | package com.dy.pipIrrGlobal.cert; |
| | | |
| | | import org.springframework.core.io.Resource; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import java.io.InputStream; |
| | | |
| | | /** |
| | | * @Author: liurunyu |
| | | * @Date: 2024/8/19 19:58 |
| | | * @Description |
| | | */ |
| | | public class WxCertUtil { |
| | | |
| | | public static final String cert_p12 = "classpath:wxCert/apiclient_cert.p12" ;//证书pkcs12格式 |
| | | public static final String cert_pem = "classpath:wxCert/apiclient_cert.pem" ;//证书pem格式 |
| | | public static final String key_pem = "classpath:wxCert/apiclient_key.pem" ;//证书密钥pem格式 |
| | | public static final String wxp_cert_pem = "classpath:wxCert/wxp_cert.pem" ; |
| | | |
| | | public static InputStream getCert_p12InputStream(ResourceLoader resourceLoader) throws Exception{ |
| | | Resource resource = resourceLoader.getResource(cert_p12); |
| | | InputStream in = resource.getInputStream() ; |
| | | return in ; |
| | | } |
| | | |
| | | public static byte[] getKey_pemBytes(ResourceLoader resourceLoader) throws Exception{ |
| | | Resource resource = resourceLoader.getResource(key_pem); |
| | | InputStream in = resource.getInputStream() ; |
| | | byte[] bs = new byte[in.available()] ; |
| | | return bs ; |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | -----BEGIN CERTIFICATE----- |
| | | MIIEKDCCAxCgAwIBAgIUUtZapmQFxzhnA3f0ZxePTJUOFgYwDQYJKoZIhvcNAQEL |
| | | BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT |
| | | FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg |
| | | Q0EwHhcNMjMwNDEyMDgzNjQ1WhcNMjgwNDEwMDgzNjQ1WjCBgTETMBEGA1UEAwwK |
| | | MTY0MDcyMTUyMDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMS0wKwYDVQQL |
| | | DCTlpKfnprnoioLmsLTnp5HmioDnoJTnqbbmnInpmZDlhazlj7gxCzAJBgNVBAYM |
| | | AkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC |
| | | AQoCggEBAL+whdzpweaD3UKZaPpz1mJg29sPrf7+m/DCQpKlVpryEQ15dkBtvugV |
| | | mhzGNoYHZK7Xw55WKhdPMheGWNeeqRSZIPLdz99jTCYKtFjtXyMqB9laod/I4fT1 |
| | | zd+sOPnehaFoMKSsJ3+vNIVlgmN27/EUbbGGgupT8AcrhO9KkrRF+dd5LZMyDfcB |
| | | ad8VHzt6nw+MP5LSAUQxWN/Up1/MvTbveAyaokH+p3dRQSB0nhWKq+Br8Vy+mWzk |
| | | qwnqlNoQwYyq5tLGuNMipGUaIKRBTp/zXaF29GL69PYLxsgpvwA/ZtGG1ooKtQAI |
| | | DG/6VrErkzkonxpfb23clwW3CHf0dGsCAwEAAaOBuTCBtjAJBgNVHRMEAjAAMAsG |
| | | A1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2Y2Eu |
| | | aXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJDMDRC |
| | | MDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJCMjdB |
| | | OUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQCpCiju |
| | | jkR5m9DJcu0sqzguxyIgoMLDNTTnNkTumEvXs0Pc3lQyP5P4UhqZ0DGkc6n/YuJi |
| | | +cB3aFLbl/aGqflfZTB4l+xdo3ZnAsfr6SIQllJCjPlujPtKb5JxOr4z6KwMnEXR |
| | | yh6V6zohZZ4BhSgMmQ1JFaArmYe5BCuXY8vaweNNarpnAxVLNYI3bF+0nn3Fd2k7 |
| | | GK6niXfCsCZySwWfXGPcqsHvPB5WZUXWfgJtt8LOnpDDBYw4DN0XJd3Qmu+RZpsd |
| | | N1bdIWeWF/4zFKbfsiJ2nSznBVuoTsvbCXlZ+2Xi0RVUeVvnVusJrK9TpL88iAa9 |
| | | Dfoi2opn4mIKVVKe |
| | | -----END CERTIFICATE----- |
New file |
| | |
| | | -----BEGIN PRIVATE KEY----- |
| | | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/sIXc6cHmg91C |
| | | mWj6c9ZiYNvbD63+/pvwwkKSpVaa8hENeXZAbb7oFZocxjaGB2Su18OeVioXTzIX |
| | | hljXnqkUmSDy3c/fY0wmCrRY7V8jKgfZWqHfyOH09c3frDj53oWhaDCkrCd/rzSF |
| | | ZYJjdu/xFG2xhoLqU/AHK4TvSpK0RfnXeS2TMg33AWnfFR87ep8PjD+S0gFEMVjf |
| | | 1KdfzL0273gMmqJB/qd3UUEgdJ4Viqvga/Fcvpls5KsJ6pTaEMGMqubSxrjTIqRl |
| | | GiCkQU6f812hdvRi+vT2C8bIKb8AP2bRhtaKCrUACAxv+laxK5M5KJ8aX29t3JcF |
| | | twh39HRrAgMBAAECggEATS3yx96qfnirVoxUEzsen1+mRc5LXTcAbHCaw1akxyAd |
| | | s5IrBYfqbKF9+oXoIBDL/gXLsm245sexQH2MOcydATkiZgbfEm91kn+O25s/SfY7 |
| | | UM1IDcVhDPMHID7Edf6dST6dD0BtauCNQgR7+jZCJ4v1GpwxC84d/5ULIhmhdclg |
| | | hoiD3PuAlhA3Phwv61fgDgpmPyDbVBEOy8GcFHbb7S4ZkLPPkZhZOUu+XG9bzz8Q |
| | | txhkk6unOV0gyu9aBqVdbg0XUxWKZ0YZf3utgiUo6j2KEVtQAMRw0QD7W2aXfiJC |
| | | MMCTpfljS0dLJgjekjQW1ad+ZOMkszLCrNvwEJLAgQKBgQDgtpdDMgw+pmolE6yW |
| | | oD21SjbZ3XpCXh86LiTgd3E+VCZyNtuhj4b0ydRpZbNJUjW8pS64Gzz7KR3rnlBT |
| | | CxaoK6VebLv5t2QhDmojmmWrktS5hbPFnDOkfXwU/KTG3yTUZeGprXvjYUWccUoV |
| | | vIbnVyQn+UCTrlGjoGP8arSbwQKBgQDaYN/zsrJw2mV8cMyvOVH4x76ygJVqT1c3 |
| | | kjhNMtGjxYPLKSOAICqAvwnXbVGODj/4KkeKL+8pYzxXUekzV91kFBJut2R2qs+c |
| | | F4IKgZmipkvRsDIKhTDByHUH1gleulrhtnekHC580gcNxW25FPuC6jZU9MTFn/qA |
| | | r5/HaC0LKwKBgHrsJu6BECWtTt8dVgnHejoTcNHYz6pCZn6jA1UuwWnBCo7r51UH |
| | | eGGrjmBhW6O+LtsU2OupKsCRi2Z6YaIrrKctyPZQMLu3UDSs9+6l5PvoBCnAM8jj |
| | | W3SdmCS610BajIELglgZKG4HpdagOlCNopYmGwcq1+JxNwN1F3zauhWBAoGAFQTm |
| | | CWrVyg77XbLtwJ/fx/ZR4JSHzSe92Vd1m9icXQsR2GgpRpfR/3pj1BxtkSLF/Xj0 |
| | | UdSzTGSE1lLIA20dnhhgfc+hF9rxCLEqjyBSTVl8Sr6O0nxnaP+GZC6x0DBXtwn9 |
| | | Egq0XsdIbobmPL5MRxmMCTlWBz+SuPguhN7+FukCgYBgix+SSUqn+8ZfrBQGk3vG |
| | | E3XJ43Q6sAKNUSibv5cGaYPu58hPTIMQ2bqBk+tcMzZcnFVlv5yuTvBQFBpNEFVE |
| | | FXVFO+oRZEiCYiNxJBYfAuXRQuPvqtBpDmD12vwPlctqu4/XBsYGMnQg5+LCKsYH |
| | | nT+8fuGVLh21vzZelPXaNA== |
| | | -----END PRIVATE KEY----- |
New file |
| | |
| | | -----BEGIN CERTIFICATE----- |
| | | MIIEFDCCAvygAwIBAgIUaIg43HtGZi3HO77mPqwzd6yjzIMwDQYJKoZIhvcNAQEL |
| | | BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT |
| | | FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg |
| | | Q0EwHhcNMjMwNDEyMDgzNjQ1WhcNMjgwNDEwMDgzNjQ1WjBuMRgwFgYDVQQDDA9U |
| | | ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl |
| | | bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo |
| | | ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCuUv41FcXIwC2nEMC4 |
| | | V791Dn4ZTRuSUA0bcGZcPpHudDoqYLbZWcoQ+rYf0l4GKueLNxGAHcr/Lufbq79M |
| | | 2MTLllJUm20XWWp1iGxJs+p2xGDEIc9udQpkGwwoMz9/+dvaEEZjhhAo/esoO2mN |
| | | DbJQ2H0K1LwAxro+TNfmu65p37KQYP5lIbhjwXaCBQp9RCh/xLmJHN6Rj0fuHqBl |
| | | fhV/8iZMh7/diHUth7JyrllEVvfDy74jnmX1pFzFHw+sBk9NAxKjh81qAUNypLx8 |
| | | d4xgJBbzdBp5Vm4gb4ie/XMZmwoFIeiLwt8zkTjIN3ZW+YOicSh4u3jvDMkcrRVw |
| | | S+O5AgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB |
| | | kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv |
| | | aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 |
| | | RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 |
| | | MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAPLa6Qqk3gIZZgo7t9NfiKpjjh/Vnw+lD |
| | | M3tdAfZKRn7uSmGfLQ3RbM62VZ7O9S4EgAwY16UN+XZVqwRsjNyv/gn886VgIgsX |
| | | CHg1VcWX597w0x+nrwBxj4Mt9INDwpW3t4wzLWcM7kEB+fUIBKB/L8+5atGOMhZt |
| | | cI5K+WLAMRHk1XcBGMSY4nYGI7smBAI/FIshmHqmgkkcynpstqmroF79QJA1wiFJ |
| | | bRjdmgVqBn4lqzzb4x6tOtnNRrGq0IDqtrywkz9xkt+KCKHeUbSY4YbpvYvkSDki |
| | | /Ej/iy6DPzmg5+pHFVENCijEchBhvElmGyfYR5jTI+/0+Yz8ftBWwg== |
| | | -----END CERTIFICATE----- |
| | |
| | | package com.dy.pipIrrSell.config; |
| | | |
| | | import com.dy.pipIrrGlobal.cert.WxCertUtil; |
| | | import com.dy.pipIrrSell.wechatpay.PayInfo; |
| | | import okhttp3.OkHttpClient; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.http.client.ClientHttpRequestFactory; |
| | | import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; |
| | | |
| | |
| | | import javax.net.ssl.SSLContext; |
| | | import javax.net.ssl.TrustManagerFactory; |
| | | import javax.net.ssl.X509TrustManager; |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | | import java.io.InputStream; |
| | | import java.security.KeyStore; |
| | | |
| | | /** |
| | |
| | | |
| | | @Configuration |
| | | public class RestTemplateWechatCertConfig { |
| | | |
| | | String mchid = PayInfo.mchid; |
| | | @Autowired |
| | | private ResourceLoader resourceLoader; |
| | | |
| | | @Bean |
| | | @ConfigurationProperties(prefix = "org.liurb.core.rest-template.config.connection") |
| | | public ClientHttpRequestFactory wechatHttpRequestFactory() throws Exception { |
| | | |
| | | KeyStore keyStore = KeyStore.getInstance("PKCS12"); |
| | | //InputStream cp = this.getClass().getResourceAsStream("apiclient_cert.p12"); |
| | | FileInputStream instream = new FileInputStream(new File("C:\\webchat\\apiclient_cert.p12")); |
| | | keyStore.load(instream, mchid.toCharArray()); |
| | | InputStream in = WxCertUtil.getCert_p12InputStream(resourceLoader); |
| | | keyStore.load(in, mchid.toCharArray()); |
| | | |
| | | KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); |
| | | keyManagerFactory.init(keyStore, mchid.toCharArray()); |
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.pipIrrGlobal.cert.WxCertUtil; |
| | | import com.dy.pipIrrGlobal.pojoSe.SeVirtualCard; |
| | | import com.dy.pipIrrGlobal.voSe.VoOrders; |
| | | import com.dy.pipIrrSell.result.SellResultCode; |
| | |
| | | import com.dy.pipIrrSell.wechatpay.dto.RefundResponse; |
| | | import com.dy.pipIrrSell.wechatpay.dto.ToRefund; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.crypto.NoSuchPaddingException; |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.IOException; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Paths; |
| | | import java.security.*; |
| | | import java.security.cert.Certificate; |
| | | import java.security.cert.CertificateException; |
| | |
| | | private String resetUserSessionKeyUrl = PayInfo.resetUserSessionKeyUrl; |
| | | private String notifyUrl = PayInfo.notifyUrl; |
| | | private String schema = PayInfo.schema; |
| | | private String privateCertFileName = PayInfo.privateCertFileName; |
| | | private String refundUrl = PayInfo.refundUrl; |
| | | |
| | | // 平台证书公钥 |
| | |
| | | * @param filename 私钥文件路径 |
| | | * @return 私钥对象 |
| | | * @throws IOException |
| | | */ |
| | | |
| | | public 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 bs 私钥文件内容 |
| | | * @return 私钥对象 |
| | | * @throws IOException |
| | | */ |
| | | public PrivateKey getPrivateKey(byte[] bs) throws IOException { |
| | | String content = new String(bs, "utf-8"); |
| | | try { |
| | | String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") |
| | | .replace("-----END PRIVATE KEY-----", "") |
| | |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | * @throws IOException |
| | | */ |
| | | public String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { |
| | | Signature sign = Signature.getInstance("SHA256withRSA"); |
| | | sign.initSign(getPrivateKey(certFileName)); |
| | | sign.update(message); |
| | | return Base64.getEncoder().encodeToString(sign.sign()); |
| | | }*/ |
| | | /** |
| | | * 签名 |
| | | * @param message 被签名信息 |
| | | * @param certFileBs 私钥证书文件内容 |
| | | * @return signature签名值,签名信息中的一项,参与生成签名信息 |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | * @throws IOException |
| | | */ |
| | | public String sign(byte[] message, byte[] certFileBs) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { |
| | | Signature sign = Signature.getInstance("SHA256withRSA"); |
| | | sign.initSign(getPrivateKey(certFileBs)); |
| | | sign.update(message); |
| | | return Base64.getEncoder().encodeToString(sign.sign()); |
| | | } |
| | |
| | | * @return 签名信息,HTTP头中的签名信息 |
| | | * HTTP头:Authorization: 认证类型 签名信息 |
| | | * 认证类型,WECHATPAY2-SHA256-RSA2048 |
| | | */ |
| | | public String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { |
| | | String message = buildMessage_order(method, url, timestamp, nonceStr, body); |
| | | String signature = sign(message.getBytes("utf-8"), certFileName); |
| | | |
| | | return "mchid=\"" + PayInfo.mchid + "\"," |
| | | + "nonce_str=\"" + nonceStr + "\"," |
| | | + "timestamp=\"" + timestamp + "\"," |
| | | + "serial_no=\"" + PayInfo.serial_no + "\"," |
| | | + "signature=\"" + signature + "\""; |
| | | } |
| | | */ |
| | | |
| | | /** |
| | | * 获取签名信息 |
| | | * @param method |
| | | * @param url |
| | | * @param body |
| | | * @return 签名信息,HTTP头中的签名信息 |
| | | * HTTP头:Authorization: 认证类型 签名信息 |
| | | * 认证类型,WECHATPAY2-SHA256-RSA2048 |
| | | */ |
| | | public String getToken(String method, String url, String body, String nonceStr, Long timestamp, byte[] certFileBs) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { |
| | | String message = buildMessage_order(method, url, timestamp, nonceStr, body); |
| | | String signature = sign(message.getBytes("utf-8"), certFileBs); |
| | | |
| | | return "mchid=\"" + PayInfo.mchid + "\"," |
| | | + "nonce_str=\"" + nonceStr + "\"," |
| | |
| | | /** |
| | | * 重新下载证书 |
| | | */ |
| | | public void refreshCertificate() throws GeneralSecurityException, IOException { |
| | | public void refreshCertificate(byte[] keyPemBs) throws GeneralSecurityException, IOException, Exception { |
| | | String method = "GET"; |
| | | String httpUrl = "/v3/certificates"; |
| | | String nonceStr = generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, PayInfo.privateCertFileName); |
| | | String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, keyPemBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | */ |
| | | public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature) throws GeneralSecurityException, IOException { |
| | | public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature, byte[] keyPemBs) throws GeneralSecurityException, IOException, Exception { |
| | | if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { |
| | | CERTIFICATE_MAP.clear(); |
| | | refreshCertificate(); |
| | | refreshCertificate(keyPemBs); |
| | | } |
| | | Certificate certificate = (Certificate)CERTIFICATE_MAP.get(wechatpaySerial); |
| | | if(certificate == null) { |
| | |
| | | * @throws SignatureException |
| | | * @throws InvalidKeyException |
| | | */ |
| | | public BaseResponse<Boolean> refunds(Refund po) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | public BaseResponse<Boolean> refunds(Refund po, ResourceLoader resourceLoader) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { |
| | | String tradeNo = po.getTradeNo(); |
| | | String refundNo = po.getRefundNo(); |
| | | Integer refund = po.getRefund(); |
| | |
| | | String httpUrl = "/v3/refund/domestic/refunds"; |
| | | |
| | | String body = JSONObject.toJSONString(refundRequest); |
| | | String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, WxCertUtil.getKey_pemBytes(resourceLoader)); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | */ |
| | | public static String schema = "WECHATPAY2-SHA256-RSA2048"; |
| | | |
| | | /** |
| | | * 私钥文件路径 |
| | | */ |
| | | public static String privateCertFileName = "C:\\webchat\\apiclient_key.pem"; |
| | | |
| | | public static String publicCertFileName = "C:\\webchat\\wxp_cert.pem"; |
| | | |
| | | /* |
| | | * 微信订单号,优先使用 |
| | | */ |
| | |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.common.webUtil.ResultCodeMsg; |
| | | import com.dy.pipIrrGlobal.cert.WxCertUtil; |
| | | import com.dy.pipIrrGlobal.pojoSe.*; |
| | | import com.dy.pipIrrGlobal.voSe.VoClient; |
| | | import com.dy.pipIrrSell.client.ClientSv; |
| | |
| | | import jakarta.validation.Valid; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | @RequestMapping(path="payment") |
| | | @RequiredArgsConstructor |
| | | public class PaymentCtrl { |
| | | private final ResourceLoader resourceLoader; |
| | | |
| | | private final PaymentSv paymentSv; |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | private final PayHelper payHelper; |
| | | private final VirtualCardSv virtualCardSv; |
| | | private final ClientSv clientSv; |
| | | |
| | | private final String privateCertFileName = PayInfo.privateCertFileName; |
| | | //private final String privateCertFileName = PayInfo.privateCertFileName; |
| | | private final String appid = PayInfo.appid; |
| | | private final String secret = PayInfo.secret; |
| | | private final String mchid = PayInfo.mchid; |
| | |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, privateCertFileName); |
| | | byte[] keyPemBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, keyPemBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, job_body.toJSONString()); |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, keyPemBs); |
| | | |
| | | return BaseResponseUtils.buildSuccess(); |
| | | } |
| | |
| | | @PostMapping(path = "placeOrder") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | |
| | | String httpUrl = "/v3/pay/transactions/jsapi"; |
| | | |
| | | String body = job_body.toJSONString(); |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, WxCertUtil.getKey_pemBytes(resourceLoader)); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | String pkg = "prepay_id=" + prepayId; |
| | | String message = payHelper.buildMessage_signAgain(appid, timeStamp, nonceStr, pkg); |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName); |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), WxCertUtil.getKey_pemBytes(resourceLoader)); |
| | | |
| | | JSONObject job_result = new JSONObject(); |
| | | job_result.put("timeStamp", timeStamp); |
| | |
| | | @PostMapping(path = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE) |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @SsoAop() |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException { |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException, Exception { |
| | | JSONObject result = new JSONObject(); |
| | | |
| | | /** |
| | |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, bodyStr); |
| | | byte[] keyPemBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, keyPemBs); |
| | | if(!valid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |
| | |
| | | private final ClientSv clientSv; |
| | | private final AliyunSmsSv aliyunSmsSv; |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | private final String privateCertFileName = PayInfo.privateCertFileName; |
| | | private final String appid = PayInfo.appid; |
| | | private final String secret = PayInfo.secret; |
| | | private final String mchid = PayInfo.mchid; |
| | |
| | | */ |
| | | public static String schema = "WECHATPAY2-SHA256-RSA2048"; |
| | | |
| | | /** |
| | | * 私钥文件路径 |
| | | */ |
| | | public static String privateCertFileName = "C:\\webchat\\apiclient_key.pem"; |
| | | |
| | | public static String publicCertFileName = "C:\\webchat\\wxp_cert.pem"; |
| | | |
| | | /* |
| | | * 微信订单号,优先使用 |
| | | */ |
| | |
| | | private String resetUserSessionKeyUrl = PayInfo.resetUserSessionKeyUrl; |
| | | private String notifyUrl = PayInfo.notifyUrl; |
| | | private String schema = PayInfo.schema; |
| | | private String privateCertFileName = PayInfo.privateCertFileName; |
| | | private String refundUrl = PayInfo.refundUrl; |
| | | |
| | | // 平台证书公钥 |
| | |
| | | |
| | | /** |
| | | * 获取商户证书私钥对象 |
| | | * @param filename 私钥文件路径 |
| | | * @param certFileBs 私钥文件内容 |
| | | * @return 私钥对象 |
| | | * @throws IOException |
| | | */ |
| | | public 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("无效的密钥格式"); |
| | | } |
| | | }*/ |
| | | public PrivateKey getPrivateKey(byte[] certFileBs) throws IOException { |
| | | String content = new String(certFileBs, "utf-8"); |
| | | try { |
| | | String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "") |
| | | .replace("-----END PRIVATE KEY-----", "") |
| | |
| | | /** |
| | | * 签名 |
| | | * @param message 被签名信息 |
| | | * @param certFileName 私钥证书文件路径 |
| | | * @param certBs 私钥证书文件内容 |
| | | * @return signature签名值,签名信息中的一项,参与生成签名信息 |
| | | * @throws NoSuchAlgorithmException |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | * @throws IOException |
| | | */ |
| | | public String sign(byte[] message, String certFileName) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException { |
| | | Signature sign = Signature.getInstance("SHA256withRSA"); |
| | | sign.initSign(getPrivateKey(certFileName)); |
| | | sign.update(message); |
| | | return Base64.getEncoder().encodeToString(sign.sign()); |
| | | }*/ |
| | | public String sign(byte[] message, byte[] certBs) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException, IOException, Exception { |
| | | Signature sign = Signature.getInstance("SHA256withRSA"); |
| | | sign.initSign(getPrivateKey(certBs)); |
| | | sign.update(message); |
| | | return Base64.getEncoder().encodeToString(sign.sign()); |
| | | } |
| | |
| | | * @return 签名信息,HTTP头中的签名信息 |
| | | * HTTP头:Authorization: 认证类型 签名信息 |
| | | * 认证类型,WECHATPAY2-SHA256-RSA2048 |
| | | */ |
| | | public String getToken(String method, String url, String body, String nonceStr, Long timestamp, String certFileName) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, NoSuchPaddingException { |
| | | String message = buildMessage_order(method, url, timestamp, nonceStr, body); |
| | | String signature = sign(message.getBytes("utf-8"), certFileName); |
| | | |
| | | return "mchid=\"" + PayInfo.mchid + "\"," |
| | | + "nonce_str=\"" + nonceStr + "\"," |
| | | + "timestamp=\"" + timestamp + "\"," |
| | | + "serial_no=\"" + PayInfo.serial_no + "\"," |
| | | + "signature=\"" + signature + "\""; |
| | | }*/ |
| | | public String getToken(String method, String url, String body, String nonceStr, Long timestamp, byte[] certFileBs) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { |
| | | String message = buildMessage_order(method, url, timestamp, nonceStr, body); |
| | | String signature = sign(message.getBytes("utf-8"), certFileBs); |
| | | |
| | | return "mchid=\"" + PayInfo.mchid + "\"," |
| | | + "nonce_str=\"" + nonceStr + "\"," |
| | |
| | | /** |
| | | * 重新下载证书 |
| | | */ |
| | | public void refreshCertificate() throws GeneralSecurityException, IOException { |
| | | public void refreshCertificate(byte[] certFileBs) throws GeneralSecurityException, IOException , Exception { |
| | | String method = "GET"; |
| | | String httpUrl = "/v3/certificates"; |
| | | String nonceStr = generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, PayInfo.privateCertFileName); |
| | | String header = PayInfo.schema + " " + getToken(method, httpUrl, "", nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | * @throws InvalidKeyException |
| | | * @throws SignatureException |
| | | */ |
| | | public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature) throws GeneralSecurityException, IOException { |
| | | public Boolean responseSignVerify(String wechatpaySerial, String signatureStr, String wechatpaySignature, byte[] certFileBs) throws Exception { |
| | | if(CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) { |
| | | CERTIFICATE_MAP.clear(); |
| | | refreshCertificate(); |
| | | refreshCertificate(certFileBs); |
| | | } |
| | | Certificate certificate = (Certificate)CERTIFICATE_MAP.get(wechatpaySerial); |
| | | if(certificate == null) { |
| | |
| | | * @throws SignatureException |
| | | * @throws InvalidKeyException |
| | | */ |
| | | public BaseResponse<Boolean> refunds(Refund po) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | public BaseResponse<Boolean> refunds(Refund po, byte[] certFileBs) throws SignatureException, InvalidKeyException, Exception{ |
| | | String tradeNo = po.getTradeNo(); |
| | | String refundNo = po.getRefundNo(); |
| | | Integer refund = po.getRefund(); |
| | |
| | | String httpUrl = "/v3/refund/domestic/refunds"; |
| | | |
| | | String body = JSONObject.toJSONString(refundRequest); |
| | | String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | String header = schema + " " + getToken(method, httpUrl, body, nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | * HTTP头认证类型 |
| | | */ |
| | | public static String schema = "WECHATPAY2-SHA256-RSA2048"; |
| | | |
| | | /** |
| | | * 私钥文件路径 |
| | | */ |
| | | public static String privateCertFileName = "C:\\webchat\\apiclient_key.pem"; |
| | | |
| | | public static String publicCertFileName = "C:\\webchat\\wxp_cert.pem"; |
| | | |
| | | /* |
| | | * 微信订单号,优先使用 |
| | | */ |
| | |
| | | import com.dy.common.webUtil.BaseResponse; |
| | | import com.dy.common.webUtil.BaseResponseUtils; |
| | | import com.dy.common.webUtil.ResultCodeMsg; |
| | | import com.dy.pipIrrGlobal.cert.WxCertUtil; |
| | | import com.dy.pipIrrGlobal.pojoSe.*; |
| | | import com.dy.pipIrrGlobal.voSe.VoClient; |
| | | import com.dy.pipIrrWechat.result.WechatResultCode; |
| | |
| | | import jakarta.validation.Valid; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.core.io.ResourceLoader; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.BindingResult; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.crypto.NoSuchPaddingException; |
| | | import java.io.BufferedReader; |
| | | import java.io.IOException; |
| | | import java.security.GeneralSecurityException; |
| | |
| | | @RequestMapping(path="payment") |
| | | @RequiredArgsConstructor |
| | | public class PaymentCtrl { |
| | | private final ResourceLoader resourceLoader; |
| | | private final PaymentSv paymentSv; |
| | | private final RestTemplateUtil restTemplateUtil; |
| | | private final PayHelper payHelper; |
| | | private final VirtualCardSv virtualCardSv; |
| | | private final String privateCertFileName = com.dy.pipIrrWechat.wechatpay.PayInfo.privateCertFileName; |
| | | private final String appid = com.dy.pipIrrWechat.wechatpay.PayInfo.appid; |
| | | private final String secret = com.dy.pipIrrWechat.wechatpay.PayInfo.secret; |
| | | private final String mchid = com.dy.pipIrrWechat.wechatpay.PayInfo.mchid; |
| | |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | Long timestamp = System.currentTimeMillis() / 1000; |
| | | |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, privateCertFileName); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, "", nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, job_body.toJSONString()); |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, certFileBs); |
| | | |
| | | return BaseResponseUtils.buildSuccess(); |
| | | } |
| | |
| | | */ |
| | | @PostMapping(path = "placeOrder") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException { |
| | | public BaseResponse<Boolean> placeOrder(@RequestBody @Valid DtoOrder order, BindingResult bindingResult) throws NoSuchAlgorithmException, InvalidKeySpecException, IOException, SignatureException, InvalidKeyException, Exception { |
| | | if(bindingResult != null && bindingResult.hasErrors()){ |
| | | return BaseResponseUtils.buildFail(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); |
| | | } |
| | |
| | | String httpUrl = "/v3/pay/transactions/jsapi"; |
| | | |
| | | String body = job_body.toJSONString(); |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, privateCertFileName); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String header = schema + " " + payHelper.getToken(method, httpUrl, body, nonceStr, timestamp, certFileBs); |
| | | |
| | | Map<String, String> headers = new HashMap<>(); |
| | | headers.put("Authorization", header); |
| | |
| | | String nonceStr = payHelper.generateRandomString(); |
| | | String pkg = "prepay_id=" + prepayId; |
| | | String message = payHelper.buildMessage_signAgain(appid, timeStamp, nonceStr, pkg); |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), privateCertFileName); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | String paySign = payHelper.sign(message.getBytes("utf-8"), certFileBs); |
| | | |
| | | JSONObject job_result = new JSONObject(); |
| | | job_result.put("timeStamp", timeStamp); |
| | |
| | | }) |
| | | @PostMapping(path = "orderNotify", consumes = MediaType.APPLICATION_JSON_VALUE) |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException { |
| | | public JSONObject orderNotify(@RequestHeader HttpHeaders headers, HttpServletRequest request, HttpServletResponse response) throws IOException, GeneralSecurityException, Exception { |
| | | JSONObject result = new JSONObject(); |
| | | |
| | | /** |
| | |
| | | |
| | | // 构造验签名串 |
| | | String signatureStr = payHelper.responseSign(wechatpayTimestamp, wechatpayNonce, bodyStr); |
| | | byte[] certFileBs = WxCertUtil.getKey_pemBytes(resourceLoader) ; |
| | | // 验证签名 |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature); |
| | | Boolean valid = payHelper.responseSignVerify(wechatpaySerial, signatureStr, wechatpaySignature, certFileBs); |
| | | if(!valid) { |
| | | response.setStatus(500); |
| | | result.put("code", "FAIL"); |