WeChat Pay APIv3 Java SDK

repository·main·Indexed 23 days ago

https://github.com/wechatpay-apiv3/wechatpay-java

Official Java client library for interacting with the WeChat Pay APIv3. The SDK is divided into a core library for HTTP client request signing, response verification, and encryption, and a service layer providing high-level business interfaces for payment scenarios. It supports Native, JSAPI, and APP payments, callback notification parsing, and automatic platform certificate updates via RSAAutoCertificateConfig.

Tokens
8.2K
Snippets
17
Records
27
Agent score
80%

What's inside wechatpay-java

  1. Understand the WeChat Pay APIv3 Java SDK structure

    main

    The SDK is divided into two main components:

    • core: The foundational library. It provides an HTTP client with automatic request signing and response verification, callback handling, and encryption/decryption utilities.
    • service: The business layer. It contains high-level business interfaces and usage examples for specific payment scenarios.

    Developers typically interact with the service layer after configuring the core component.

  2. Understand certificate serial number mismatches

    main

    It is normal for the certificate serial number in a request to differ from the one in the response:

    • Merchant Requests: Use the Merchant API Private Key and include the Merchant Certificate Serial Number.
    • WeChat Pay Responses: Use the WeChat Pay Platform Private Key and return the WeChat Pay Platform Certificate Serial Number.

    This discrepancy is required so that the verifying party can correctly identify which key was used for the digital signature.

  3. Enable dual-domain disaster recovery

    main

    To improve stability, the SDK supports dual-domain disaster recovery. If the primary domain api.mch.weixin.qq.com fails, the SDK can automatically switch to the backup domain api2.wechatpay.cn.

    Recommended Strategy: To avoid increasing total retry counts and latency, it is recommended to combine disableRetryOnConnectionFailure() (to stop OkHttp from retrying multiple IPs on the same domain) and enableRetryMultiDomain() (to trigger the switch to the backup domain). This results in a retry sequence of [primary_ip1, backup_ip1] instead of exhausting all IPs on the primary domain first.

    // Recommended: Enable dual-domain retry and disable OkHttp's default connection failure retry
    HttpClient httpClient =
        new DefaultHttpClientBuilder()
            .config(config)
            .disableRetryOnConnectionFailure()
            .enableRetryMultiDomain()
            .build();
    
    JsapiService service = new JsapiService.Builder().httpclient(httpClient).build();
  4. How to use both ShangMi and RSA in the same application

    main
    The SDK allows you to support both RSA and ShangMi algorithms simultaneously by creating separate service instances. Use RSAConfig to build services for standard RSA requests and SMConfig to build services for ShangMi requests. Choose the appropriate service instance based on your business logic requirements.
  5. Generate Payment Parameters for JSAPI and APP Payments

    main

    For JSAPI and APP payment flows, use the specialized extension classes JsapiServiceExtension or AppServiceExtension. These classes include a method prepayWithRequestPayment() which returns all the necessary parameters required by the frontend to trigger the payment UI.

    JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
    
    // 跟之前下单示例一样,填充预下单参数
    PrepayRequest request = new PrepayRequest();
    
    // response包含了调起支付所需的所有参数,可直接用于前端调起支付
    PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
  6. Configure logging for the SDK

    main
    The SDK uses the SLF4j interface for logging. To see logs, you must include a logging implementation (such as Logback, Log4j2, or SLF4j-simple) in your project's dependencies. If no implementation is found, the SDK defaults to a NOP (No-Operation) implementation and will not record any logs.
  7. Handle WeChat Pay callback notifications

    main

    To process callback notifications from WeChat Pay, you must create a public HTTP endpoint and use NotificationParser to verify and decrypt the payload.

    Implementation Steps:

    1. Construct RequestParam: Use the raw HTTP request body (do not use a serialized JSON string to avoid signature mismatches) and the following headers:
      • Wechatpay-Signature: The signature from WeChat Pay.
      • Wechatpay-Serial: The serial number of the platform certificate used for verification.
      • Wechatpay-Nonce: The nonce in the signature.
      • Wechatpay-Timestamp: The timestamp in the signature.
      • Wechatpay-Signature-Type: The signature type.
    2. Initialize NotificationConfig: Choose one based on your security setup:
      • RSAPublicKeyNotificationConfig: If using WeChat Pay public/private keys.
      • RSAAutoCertificateConfig: If using the automatic platform certificate mechanism.
      • RSACombinedNotificationConfig: If performing a grayscale transition between platform certificates and public/private keys.
    3. Initialize NotificationParser with the chosen config.
    4. Parse the notification: Call NotificationParser.parse(requestParam, TargetClass.class). If verification fails, it throws a ValidationException.
    5. Respond to WeChat Pay: Return 200 OK if processing succeeds. Return 4xx or 5xx (e.g., 500 Internal Server Error) if processing fails so WeChat Pay can retry.

    Supported Notification Types:

    • Payment: Transaction
    • Refund: RefundNotification
    • Unknown types: Use Map.class (nested JSON objects will be converted to LinkedTreeMap).
    // 1. Construct RequestParam
    RequestParam requestParam = new RequestParam.Builder()
            .serialNumber(wechatPaySerial)
            .nonce(wechatpayNonce)
            .signature(wechatSignature)
            .timestamp(wechatTimestamp)
            .body(requestBody)
            .build();
    
    // 2. Initialize NotificationConfig (Example using RSAPublicKeyNotificationConfig)
    NotificationConfig config = new RSAPublicKeyNotificationConfig.Builder()
            .publicKeyFromPath(publicKeyPath)
            .publicKeyId(publicKeyId)
            .apiV3Key(apiV3Key)
            .build();
    
    // 3. Initialize NotificationParser
    NotificationParser parser = new NotificationParser(config);
    
    try {
      // 4. Parse (e.g., for a Transaction)
      Transaction transaction = parser.parse(requestParam, Transaction.class);
    } catch (ValidationException e) {
      // Signature verification failed
      logger.error("sign verification failed", e);
      return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
    }
    
    // 5. Handle business logic success/failure
    if (/* process error */) {
      return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
    }
    return ResponseEntity.status(HttpStatus.OK);
  8. Install the wechatpay-java-shangmi extension

    main

    To use ShangMi (Guomi) algorithms for WeChat Pay APIv3, you must install both the core wechatpay-java SDK and the wechatpay-java-shangmi extension. The extension is based on the Tencent Kona SM Suite and provides support for SM2/SM4 algorithms.

    ### Gradle
    ```groovy
    implementation 'com.github.wechatpay-apiv3:wechatpay-java:0.2.2'
    implementation 'com.github.wechatpay-apiv3:wechatpay-java-shangmi:0.2.2'

    Maven

    <dependency>
      <groupId>com.github.wechatpay-apiv3</groupId>
      <artifactId>wechatpay-java</artifactId>
      <version>0.2.2</version>
    </dependency>
    <dependency>
      <groupId>com.github.wechatpay-apiv3</groupId>
      <artifactId>wechatpay-java-shangmi</artifactId>
      <version>0.2.2</version>
    </dependency>
  9. Manually encrypt or decrypt sensitive information

    main

    While the SDK automatically handles encryption/decryption for supported interfaces (like Merchant Transfer), you can manually handle sensitive data using RSAPrivacyEncryptor and RSAPrivacyDecryptor.

    Encryption (using a Public Key)

    If you have the WeChat Pay public key (from platform certificates or the public key service):

    PrivacyEncryptor encryptor = new RSAPrivacyEncryptor(wechatPayPublicKey);
    String ciphertext = encryptor.encryptToString(plaintext);

    If you are using RSAAutoCertificateConfig, you can obtain the encryptor and the required serial number directly from the config:

    PrivacyEncryptor encryptor = config.createEncryptor();
    String wechatPayCertificateSerialNumber = encryptor.getWechatpaySerial();
    String ciphertext = encryptor.encryptToString(plaintext);

    Decryption (using a Private Key)

    To decrypt data using your merchant private key:

    PrivacyDecryptor decryptor = new RSAPrivacyDecryptor(merchantPrivateKey);
    String plaintext = decryptor.decryptToString(ciphertext);
  10. Use ApacheHttpClient for HTTP requests

    main

    The SDK supports using Apache HttpClient via ApacheHttpClientAdapter. This is useful if you want to integrate with existing Apache HttpClient infrastructure. It automatically handles signature generation and verification.

    Steps to use:

    1. Initialize ApacheHttpClientAdapter using ApacheHttpClientBuilder.
    2. Construct an HttpRequest using the HttpRequest.Builder.
    3. Execute the request using httpClient.execute (supports GET, PUT, POST, PATCH, DELETE) or httpClient.get.
    // 1. Initialize configuration
    Config config = new RSAAutoCertificateConfig.Builder()
        .merchantId(merchantId)
        .privateKeyFromPath(privateKeyPath)
        .merchantSerialNumber(merchantSerialNumber)
        .apiV3Key(apiV3Key)
        .build();
    
    // 2. Initialize ApacheHttpClient
    HttpClient httpClient = new ApacheHttpClientBuilder().config(config).build();
    
    // 3. Construct HttpRequest
    HttpRequest httpRequest = new HttpRequest.Builder()
        .httpMethod(HttpMethod.GET)
        .url(requestPath)
        .headers(headers)
        .build();
    
    // 4. Send request and receive response
    HttpResponse<ExampleResponse> httpResponse = httpClient.execute(httpRequest, ExampleResponse.class);
  11. Configure network settings using DefaultHttpClientBuilder

    main

    The SDK uses OkHttp as the default HTTP client. You can use DefaultHttpClientBuilder to customize connection settings such as timeouts and proxies.

    Available configuration methods:

    • readTimeoutMs(long): Default 10000ms. Sets the default read timeout for new connections.
    • writeTimeoutMs(long): Default 10000ms. Sets the default write timeout for new connections.
    • connectTimeoutMs(long): Default 10000ms. Sets the default connection timeout for new connections.
    • proxy(Proxy): Sets the HTTP proxy used when creating connections.
    • disableRetryOnConnectionFailure(): Disables OkHttp's default retry on connection failure.
    • enableRetryMultiDomain(): Enables automatic retry using the backup domain api2.wechatpay.cn if the primary domain api.mch.weixin.qq.com is unreachable.
    HttpClient httpClient =
        new DefaultHttpClientBuilder()
            .config(config)
            .connectTimeoutMs(500)
            .build();
    
    // Initialize a service (e.g., JsapiService) with the custom client
    JsapiService service = new JsapiService.Builder().httpclient(httpClient).build();
  12. Download WeChat Pay bills

    main

    Bill downloading is a two-step process to balance performance and security:

    1. Request a download link and obtain a bill summary via /v3/bill/tradebill.
    2. Download the file via /v3/billdownload/file (this request requires a signature, but the response is not signed).

    Use HttpClient.download(downloadUrl) to get an InputStream.

    Warning: You must manually verify the integrity of the downloaded file using the bill summary obtained in step 1. Always close the input stream after use.

    InputStream inputStream = httpClient.download(downloadUrl);
    
    // For non-compressed bills, you can use IOUtil to read into a String
    String respBody = IOUtil.toString(inputStream);
    inputStream.close();