Request Signing
1. Authentication method
All API requests must use request-level digital signature authentication.
- Algorithm: ECDSA (ES256, P-256 + SHA-256)
- Private key generation:
openssl ecparam -name prime256v1 -genkey -noout -out priKey.pem - Public key generation:
openssl ec -in priKey.pem -pubout -out pubKey.pem
2. Required HTTP headers
All requests must include the following headers:
| Header | Description |
|---|---|
X-Merchant-Id | Merchant CNPJ (digits only, without ".", ",", "-" or similar symbols) |
X-Timestamp | Request timestamp (Unix seconds) |
X-Nonce | Random string (anti-replay) |
Digest | Request body digest |
Authorization | Signature information |
Authorization.keyId | Signing key identifier used to locate the verification public key |
3. Digest calculation rules
Digest includes the request body in the signed scope.
Format
Digest: SHA-256=<Base64(SHA256(body_bytes))>Rules
- POST / PUT / PATCH: digest the raw HTTP body bytes
- GET / DELETE: digest an empty byte string
- Do not reserialize JSON before computing Digest
4. Signing content (Canonical String)
The signature must cover exactly these 4 items, in fixed order:
(request-target)x-timestampx-noncedigest
(request-target) Format
(request-target): <http-method小写> <path>[?<规范化query>]Example:
(request-target): post /v1/orders
(request-target): get /v1/orders?order_id=123Canonical String formatting rules
Each line must follow this format:
<字段名>:<单个空格><字段值>Use
\n(LF) line breaksNo extra spaces or blank lines are allowed
Example
(request-target): post /v1/orders
x-timestamp: 1738123456
x-nonce: a9f3c1d47e8b9a2c
digest: SHA-256=Base64DigestValue5. Authorization Header
Format
Authorization: Signature keyId="<merchant_id>",alg="ES256",headers="(request-target) x-timestamp x-nonce digest",signature="<Base64签名>"Description
- Sign the Canonical String with the merchant private key
- The platform verifies the signature with the corresponding public key
6. Anti-replay rules
The platform performs the following checks:
- Timestamp check: difference between
X-Timestampand server time must be ≤ **300 seconds - Nonce check:
(merchant_id, nonce)must not repeat within the validity window
7. Critical constraints (must follow)
(request-target)must be included in the signature and cannot be omitted- Header names must be lowercase in the signing string
- There must be exactly one space after the colon (
key: value) - Canonical String must match byte-for-byte
- The
signaturein Authorization is not a standalone Base64 value - Correct verification requires
keyId / alg / headerstogether (request-target)must be included in the signature and cannot be omitted- Digest and Authorization must both be validated
8. Authorization generation flow
Authorization generation has 4 fixed steps.
Step 1:Prepare request body digest (Digest)
Example request body (raw JSON text actually sent):
json
{"order_no":"A10001","amount":100,"currency":"CNY"}Generate Digest:
bodyBytes = UTF8_BYTES('{"order_no":"A10001","amount":100,"currency":"CNY"}')
digestB64 = Base64(SHA256(bodyBytes))
Digest = "SHA-256=" + digestB64Step 2:Build Canonical Signing String
(request-target): post /v1/orders
x-timestamp: 1738123456
x-nonce: a9f3c1d47e8b9a2c
digest: SHA-256=Base64DigestValueCanonical String must match byte-for-byte:
- Exactly one space after the colon
- Use
\n(LF) line breaks- Field order is fixed
Step 3:Generate digital signature (ES256)
hashBytes = SHA256(UTF8_BYTES(signingString))
signature = ECDSA_SIGN_DER(privateKey, hashBytes)
signatureB64 = Base64(signature)Step 4:Generate Authorization
Authorization =
'Signature ' +
'keyId="' + merchantId + '",' +
'alg="ES256",' +
'headers="(request-target) x-timestamp x-nonce digest",' +
'signature="' + signatureB64 + '"'9. Authorization verification flow
After receiving a request, verify using the following flow:
Step 1:Validate Digest
expectedDigest =
"SHA-256=" + Base64(SHA256(HTTP_RAW_BODY_BYTES))
if Digest != expectedDigest:
reject(DIGEST_MISMATCH)Step 2:Rebuild Canonical Signing String
(request-target): post /v1/orders
x-timestamp: <X-Timestamp>
x-nonce: <X-Nonce>
digest: <Digest>Step 3:Verify Authorization
publicKey = LOOKUP_PUBLIC_KEY(keyId)
hashBytes = SHA256(UTF8_BYTES(signingString))
ok = ECDSA_VERIFY_DER(publicKey, hashBytes, Base64Decode(signature))
if ok != true:
reject(SIGNATURE_INVALID)