Skip to content

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 DescriptionType Required
x-paykka-appidUnique identifier of the callerString[1,64]
x-paykka-timestampTimestamp in milliseconds, with an error margin of no more than 5 minutes from the current timeString[1,11]
x-paykka-nonceRandom number for replay prevention, unique for each requestString[10,100]
x-paykka-signRequest signatureString[1,500]
x-paykka-sign-algSignature type, fixed value SHA256_WITH_RSAString[1,10]

Response Header Parameters

Parameter Name DescriptionType Required
x-paykka-timestampTimestampString[1,11]
x-paykka-nonceRandom number for replay prevention, unique for each responseString[10,100]
x-paykka-signResponse signatureString[1,500]

Examples

Request Body

{
    "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

{
  "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
    }
  }
}

API Address

Sandbox API Address

https://openapi-sandbox.paykka.com

Production API Address

https://openapi.paykka.com
https://openapi.eu.paykka.com

Calculating Signatures

Adding Dependencies

Include the following dependency in your Java code:

<dependency>
  <groupId>commons-codec</groupId>
  <artifactId>commons-codec</artifactId>
  <version>1.15</version>
</dependency>

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 open 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 open 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 open 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).

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.

HTTP request method\n
URL\n
Request timestamp\n
Request random string\n
Request message body
    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 such as 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"}

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.
private static final String FORMAT = "%s\n%s\n%s\n%s\n%s";

/**
 * Sign using SHA256withRSA algorithm
 * 
 * @param String privateKey Base64-encoded PKCS8 format private key
 * @param Object req Request object
 * @return string URL-encoded Base64 signature result
 */
public static String signWithSha256Rsa(Object req, String privateKey) {
    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(""));
    log.debug("Signature string: {}", content);

    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);
    return 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

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

private static final String FORMAT = "%s\n%s\n%s\n%s\n%s";

/**
 * Verify signature using SHA256withRSA algorithm
 * 
 * @param String publicKey Base64-encoded X509 format public key
 * @param Object req Request object
 * @param Object resp Response object
 * @return boolean Verification result, true for success, false for failure
 */
public static boolean verifyWithSha256Rsa(Object req, Object resp, String publicKey) {
    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(""));
    log.debug("Signature string: {}", content);

    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));
    return verified;
}