# API Authentication > 🔐 **Important Note**: Please strictly follow the instructions in this document for API authentication and signature to ensure secure communication. ## Required Header Parameters ### Request Header Parameters | Parameter Name | Description | Type | Required | | --- | --- | --- | --- | | `x-paykka-appid` | Unique identifier of the caller | String[1,64] | ✅ | | `x-paykka-timestamp` | Timestamp in milliseconds, with an error margin of no more than 5 minutes from the current time | String[1,11] | ✅ | | `x-paykka-nonce` | Random number for replay prevention, unique for each request | String[10,100] | ✅ | | `x-paykka-sign` | Request signature | String[1,500] | ✅ | | `x-paykka-sign-alg` | Signature type, fixed value `SHA256_WITH_RSA` | String[1,10] | ✅ | ### Response Header Parameters | Parameter Name | Description | Type | Required | | --- | --- | --- | --- | | `x-paykka-timestamp` | Timestamp | String[1,11] | ✅ | | `x-paykka-nonce` | Random number for replay prevention, unique for each response | String[10,100] | ✅ | | `x-paykka-sign` | Response signature | String[1,500] | ✅ | ## Examples ### Request Body ```json { "merchant_id": "18356675194960", "payment_type": "PURCHASE", "authorisation_type": "FINAL_AUTH", "capture_method": "AUTOMATIC", "trans_id": "t202311081113", "amount": 445, "currency": "EUR", "return_url": "https://www.baidu.com/returnUrl", "payment": { "payment_method": "BankCard", "store_payment_method": false, "token_usage": "CARD_ON_FILE", "shopper_reference": "user1234567890", "encrypted_card_no": "string", "encrypted_exp_year": "string", "encrypted_exp_month": "string", "encrypted_cvv": "string" } } ``` ### Response Body ```json { "ret_code": "000000", "ret_msg": "Success", "data": { "merchant_id": "18356675194960", "trans_id": "t202311081113", "order_id": "GW20598371023658327", "status": "AUTHORIZED", "authorisation_type": "FINAL_AUTH", "capture_method": "AUTOMATIC", "amount": 445, "currency": "EUR", "payment": { "payment_method": "BANKCARD" }, "card_info": { "bin": "424242", "last4": "4242", "card_brand": "VISA" }, "balances": { "authed_amount": 445, "captured_amount": 0, "able_to_capture_amount": 445, "voided_amount": 0, "able_to_void_amount": 445, "refunded_amount": 0, "able_to_refund_amount": 0 } } } ``` ## Calculating Signatures ### Adding Dependencies Include the following dependency in your Java code: ```xml commons-codec commons-codec 1.15 ``` ### Generating Signatures #### Request Signature Developers need to use their private key to sign a combination of API URL, message body, and other key data using `SHA256_WITH_RSA`. The signature information is transmitted through HTTP headers. Requests without signatures or with invalid signatures will not be executed and will return a 401 Unauthorized response. #### Response Signature For requests with successful signature verification, the platform's private key will be used to sign the response. The signature information is included in the HTTP header, and developers should use the platform's public key for verification. #### Callback Notification Signature When calling the developer's interface, the open platform will sign the callback request using its private key. The signing method is consistent with the response signing method, and developers must verify the callback signature using the platform's public key. #### Constructing the Signature String The signature string consists of five lines, each representing a parameter. Each line ends with \n (newline character, ASCII code 0x0A), including the last line. If a parameter is empty, an empty character followed by \n should still be included. If a parameter itself ends with \n, an additional \n should be appended. ``` https request method\n URL\n Request timestamp\n Request random string\n Request message body\n ``` * 1. Get the HTTP request method (POST) * 1. Get the URL part of the request: the absolute URL without the domain part. If the request has query parameters, the URL should end with '?' and the corresponding query string. Parameters in the URL should be URLEncoded to handle special characters like Chinese characters. * 1. Get the current system timestamp (Unix Timestamp) when initiating the request, accurate to milliseconds. * 1. Generate a 32-bit random string for the request. Recommended algorithm: call a random number function and convert the result to a string. * 1. Get the request body from the request. Example ``` POST\n /api/pay/demo?id=1537\n 1705544961000\n 326425780571035424362645\n {"merch":"123"}\n ``` #### Calculating the Signature Value 1. Use the merchant's private key to sign the signature string with `SHA256_WITH_RSA`; 2. Perform `Base64 encoding + URL encoding` on the signature result to get the signature value. ```java private static final String FORMAT = "%s\n%s\n%s\n%s\n%s"; String content = String.format(FORMAT, Optional.ofNullable(req.getMethod()).orElse(""), Optional.ofNullable(req.getUri()).orElse(""), req.getTimestamp(), Optional.ofNullable(req.getNonce()).orElse(""), Optional.ofNullable(req.getBody()).orElse("")); Signature signature = Signature.getInstance("SHA256withRSA"); byte[] privateKeys = Base64Utils.decodeFromString(privateKey); PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeys); PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(pkcs8EncodedKeySpec); signature.initSign(priKey); signature.update(content.getBytes(StandardCharsets.UTF_8)); byte[] signed = signature.sign(); String result = Base64Utils.encodeToString(signed); String sign = URLEncoder.encode(result); log.info("Generated signature: {}", sign); ``` ### Setting Request Headers 1. Set request headers `x-paykka-appid`, `x-paykka-nonce`, `x-paykka-timestamp`, `x-paykka-sign-alg`, and ensure the parameters are correct 2. Set the signature to the request header `x-paykka-sign`, and ensure that the original signature string parameters and request body `requestBody` are completely consistent ### Request Example ```bash curl 'https://openapi-sandbox.paykka.com/payments' -X POST \ -H 'Content-Type: application/json'\ -H 'Content-Type: application/json' \ -H 'x-paykka-appid: 978594372956732' \ -H 'x-paykka-nonce: 4326048250346354435' \ -H 'x-paykka-sign: Mif3gh48xxxxxxxxx' \ -H 'x-paykka-sign-alg: SHA256_WITH_RSA' \ -H 'x-paykka-timestamp: 1757387467986' \ -d '{"merchant_id": "18356675194960"}' ``` ## Verifying Signatures > ⚠️ **Note**: The public key used for signature verification is the PayKKa public key ```java private static final String FORMAT = "%s\n%s\n%s\n%s\n%s"; String content = String.format(FORMAT, Optional.ofNullable(req.getMethod()).orElse(""), Optional.ofNullable(req.getUri()).orElse(""), resp.getTimestamp(), Optional.ofNullable(resp.getNonce()).orElse(""), Optional.ofNullable(resp.getBody()).orElse("")); Signature signature = Signature.getInstance("SHA256withRSA"); byte[] publicKeys = Base64Utils.decodeFromString(publicKey); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKeys); PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec); signature.initVerify(pubKey); signature.update(content.getBytes(StandardCharsets.UTF_8)); String channelSignature = URLDecoder.decode("signatureDataFromPayKKa", StandardCharsets.UTF_8); boolean verified = signature.verify(Base64Utils.decodeFromString(channelSignature)); ```