跳至主要内容

Signature Algorithm

Overview

All Agreement Payment APIs use the same authentication mechanism as QR Payment: HMAC_SHA256 or RSA_SHA256 signature via standard Bybit API headers.


Signature Algorithm

AlgorithmKey TypeSignature HeaderDescription
HMAC_SHA256API Secret (symmetric)X-BAPI-SIGN (hex)System-generated API key
RSA_SHA256RSA private key (asymmetric)X-BAPI-SIGN (base64)Self-generated API key

Reference: See Bybit API authentication examples for complete sample code.


Request Signature

Signature Flow

1. Construct String to Sign

# POST request:
String to sign = timestamp + api_key + recv_window + raw_request_body

# GET request:
String to sign = timestamp + api_key + recv_window + queryString

Example (POST):

1736233200000xxxxxxxxxxxxxxxxxx5000{"merchant_id":"M123456789","user_id":"U_123456789",...}

Example (GET):

1736233200000xxxxxxxxxxxxxxxxxx5000merchant_id=M123456789&user_id=U_123456789&agreement_type=CYCLE&agreement_no=AGR202601070001

2. Calculate Signature

# HMAC_SHA256 (System-generated key):
Signature = HEX(HMAC_SHA256(String to sign, api_secret))

# RSA_SHA256 (Self-generated key):
Signature = Base64(RSA_SHA256_Sign(String to sign, merchant_private_key))

3. Set Request Headers

X-BAPI-API-KEY: xxxxxxxxxxxxxxxxxx
X-BAPI-TIMESTAMP: 1736233200000
X-BAPI-SIGN: {calculated signature}
X-BAPI-RECV-WINDOW: 5000

Code Examples

Java (HMAC_SHA256)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.HexFormat;

public class SignatureUtil {

public static String signHmac(String timestamp, String apiKey,
String recvWindow, String body,
String apiSecret) throws Exception {
String content = timestamp + apiKey + recvWindow + body;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(apiSecret.getBytes("UTF-8"), "HmacSHA256"));
byte[] hash = mac.doFinal(content.getBytes("UTF-8"));
return HexFormat.of().formatHex(hash);
}
}

Python (HMAC_SHA256)

import hmac, hashlib

def sign_request(timestamp, api_key, recv_window, body, api_secret):
content = f"{timestamp}{api_key}{recv_window}{body}"
return hmac.new(
api_secret.encode('utf-8'),
content.encode('utf-8'),
hashlib.sha256
).hexdigest()

cURL Example

#!/bin/bash

API_HOST="https://api2.bybit.com"
API_PATH="/v5/bybitpay/agreement/pay"
API_KEY="xxxxxxxxxxxxxxxxxx"
API_SECRET="your_api_secret"
RECV_WINDOW="5000"

REQUEST_BODY='{"merchant_id":"M123456789","user_id":"U_123456789","agreement_type":"CYCLE","agreement_no":"AGR202601070001","out_trade_no":"ORDER20260107001","scene_code":"SUBSCRIPTION","amount":{"total":"2350","currency":"USDT","currency_type":"CRYPTO","chain":"TRC20"},"order_info":{"order_title":"Monthly subscription"},"notify_url":"https://merchant.com/notify/pay"}'

TIMESTAMP=$(date +%s000)
SIGN_CONTENT="${TIMESTAMP}${API_KEY}${RECV_WINDOW}${REQUEST_BODY}"
SIGNATURE=$(echo -n "$SIGN_CONTENT" | openssl dgst -sha256 -hmac "$API_SECRET" | awk '{print $2}')

curl -X POST "${API_HOST}${API_PATH}" \
-H "Content-Type: application/json" \
-H "X-BAPI-API-KEY: ${API_KEY}" \
-H "X-BAPI-TIMESTAMP: ${TIMESTAMP}" \
-H "X-BAPI-SIGN: ${SIGNATURE}" \
-H "X-BAPI-RECV-WINDOW: ${RECV_WINDOW}" \
-d "${REQUEST_BODY}"

Webhook Signature Verification

Verification Steps

  1. Get sign and signType from request body
  2. Remove sign and signType to get content to verify
  3. Use platform public key to verify signature (RSA2/SHA256withRSA)
  4. Verify timestamp from X-Timestamp header (within 5 minutes)

Java Example

public class WebhookVerifier {

public boolean verifyWebhook(String body, String timestamp,
String platformPublicKey) throws Exception {
// 1. Check timestamp
long now = System.currentTimeMillis();
long ts = Long.parseLong(timestamp);
if (Math.abs(now - ts) > 5 * 60 * 1000) {
return false;
}

// 2. Extract signature
JSONObject notify = JSONObject.parseObject(body);
String sign = notify.getString("sign");

// 3. Remove sign and signType
notify.remove("sign");
notify.remove("signType");
String contentToVerify = notify.toJSONString();

// 4. Verify RSA2 signature
return verifyRSA2(contentToVerify, sign, platformPublicKey);
}

private boolean verifyRSA2(String content, String signBase64,
String publicKeyStr) throws Exception {
String keyContent = publicKeyStr
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s+", "");

byte[] keyBytes = Base64.getDecoder().decode(keyContent);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);

Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
signature.update(content.getBytes("UTF-8"));
return signature.verify(Base64.getDecoder().decode(signBase64));
}
}

Key Management

Key TypePurposeCustodianFormat
Merchant Private KeySign API requestsMerchant (confidential)PEM (PKCS1/PKCS8)
Merchant Public KeyPlatform verifies requestsPlatformPEM (X.509)
Platform Private KeySign webhooksPlatformPEM
Platform Public KeyVerify webhooksMerchantPEM (X.509)

Private Key Formats

# PKCS1 format
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----

# PKCS8 format (recommended)
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkq...
-----END PRIVATE KEY-----

Security Requirements

  1. Store private keys encrypted; never store in plaintext
  2. Rotate keys periodically (recommended annually)
  3. Contact platform immediately if key is compromised
  4. Use different keys for test and production environments
  5. PKCS8 format is recommended for better compatibility