# FastApi Integration Guide

## Background

Welcome to Bitget! As a broker, when integrating with our FastApi, it's important to follow Bitget's integration standards. This documentation provides a step-by-step guide to help you integrate with Bitget's FastApi.

## Integration Preparation

This feature requires applying through your Bitget BD (Business Development) representative. Contact your BD and submit the following materials:

| Info | Description |
|:-----|:-------------|
| name | Platform name |
| contact | Contact method — email/phone number |
| ip | Server IP — common IP addresses used by the broker platform to access Bitget's API |
| logo (AI) | Platform logo — Adobe Illustrator format |
| logo (PNG) | Platform logo — PNG format (64 × 64 pixels resolution) |
| callbackUrl | Callback URL — for broker user info callbacks |
| rsaPublicKey (2048 bit) | Public key — based on the RSA algorithm; format: RSA PKCS8, 2048-bit |

Once the application is approved, your BD will provide the following:

| Parameter name | Parameter type | Description |
|:----------------|:-----------------|:--------------|
| clientId | String | Broker platform ID — unique tag assigned by Bitget |

### Technical Implementation

The following attributes are primarily used to support feature integration.

| Parameter name | Parameter type | Provider | Description |
|:----------------|:-----------------|:-----------|:--------------|
| clientId | String | Bitget | Broker platform ID — provided by your BD after the application is approved |
| callbackUrl | String | Broker user | Callback URL — submitted to Bitget as part of the BD application; used for broker user info callbacks |
| rsaPublicKey (2048 bit) | String | Broker user | Public key — submitted to Bitget as part of the BD application; based on the RSA algorithm, format: RSA PKCS8, 2048-bit |
| rsaPrivateKey (2048 bit) | String | Broker user | Private key — based on the RSA algorithm; format: RSA PKCS8, 2048-bit; kept by the broker, not shared with Bitget |

:::warning{title="Keep your private key secure"}
The `rsaPrivateKey` must be stored securely on the broker side and never shared with Bitget or any third party. Anyone in possession of the private key can decrypt user API key information and forge signatures.
:::

### Code Examples (Java)

Below are examples of signature, RSA encryption and decryption. These examples are designed for Java.

#### Utility Class

Both signature and RSA encryption/decryption are implemented based on this utility class.

```Java title="RsaUtil.java"
package xxx;

import org.springframework.util.Base64Utils;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class RsaUtil {

    // Encrypt with RSA using a public key
    public static String encryptRSA(String plaintext, String publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        return Base64Utils.encodeToString(encryptedBytes);
    }

    // use private key for RSA decryption
    public static String decryptRSA(String encryptedData, String privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKey));
        byte[] encryptedBytes = Base64Utils.decodeFromString(encryptedData);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    /**
     * DecodePublicKey
     *
     * @param key
     * @return
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        byte[] byteKey = org.apache.commons.codec.binary.Base64.decodeBase64(key);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(byteKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePublic(x509EncodedKeySpec);
    }

    /**
     * DecodePrivateKey
     *
     * @param key
     * @return
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        byte[] byteKey = org.apache.commons.codec.binary.Base64.decodeBase64(key);
        PKCS8EncodedKeySpec x509EncodedKeySpec = new PKCS8EncodedKeySpec(byteKey);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(x509EncodedKeySpec);
    }

    public static boolean verify(Map<String, String> map, String publicKey, String sign) throws Exception {
        String srcData = getSignData(map);
        byte[] keyBytes = getPublicKey(publicKey).getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64Utils.decodeFromString(sign));
    }

    /**
     * Signature
     *
     * @param data       data to be signed
     * @param privateKey the private key
     * @return signature
     */
    public static String sign(String data, String privateKey) throws Exception {
        byte[] keyBytes = getPrivateKey(privateKey).getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance("MD5withRSA");
        signature.initSign(key);
        signature.update(data.getBytes());
        return Base64Utils.encodeToString(signature.sign());
    }

    /**
     * Get signature
     *
     * @param params parameter set excluding the secret key
     * @return
     */
    public static String getSignData(Map<String, String> params) {
        StringBuilder sb = new StringBuilder();
        // step1: sort the request parameters
        Set<String> keyset = params.keySet();
        TreeSet<String> sortSet = new TreeSet<String>();
        sortSet.addAll(keyset);
        Iterator<String> it = sortSet.iterator();
        // step2: link parameter key-values, append secret key, get string to encrypt
        while (it.hasNext()) {
            String key = it.next();
            String value = params.get(key);
            sb.append(key).append(value);
        }
        return sb.toString();
    }

    static String publicKey = "*****";

    static String privateKey = "*****";
}
```

#### Signature Usage Example

```Java
public static void main(String[] args) throws Exception {
    // Generate a signature
    Map<String, String> map = new HashMap<>();
    long l = System.currentTimeMillis();
    map.put("timestamp", String.valueOf(l));
    map.put("clientId", "1");
    map.put("clientUserId", "123");
    String sign = RsaUtil.sign(RsaUtil.getSignData(map), RsaUtil.privateKey);
    System.out.println(sign);
}
```

#### Examples of RSA Encryption and Decryption Usage

```Java
public static void main(String[] args) throws Exception {
    String xxStr = "hello world";
    // encrypt
    String encrypted = RsaUtil.encryptRSA(xxStr, RsaUtil.publicKey);
    System.out.println("encrypt by publicKey: " + encrypted);
    // decrypt
    System.out.println("decrypt by privateKey: " + RsaUtil.decryptRSA(encrypted, RsaUtil.privateKey));
}
```

## Function Implementation

Below is the detailed process for feature implementation — covering the APIs, parameters, and signature generation rules used in the OAuth authorization flow.

The flow has three steps: the broker redirects the user to Bitget's authorization page, Bitget calls back to the broker's interface with the user's API key, and Bitget redirects the user back to the broker's page with the authorization result.

### 1. Broker Redirects to the Bitget Authorization Page

When redirecting from the broker platform to Bitget's authorization page, the broker needs to include the following parameters in the URL.

The authorization page link is `https://www.bitget.com/en/account/oauth`.

| Parameter name | Parameter type | Description |
|:----------------|:-----------------|:--------------|
| clientUserId | String | Broker user ID — customizable by client |
| timestamp | String | Timestamp — Unix millisecond format; must be within 5 minutes before or after the current time, or the request is considered invalid. This timestamp is verified when the user clicks **Authorize** on the Bitget page |
| clientId | String | Broker platform ID |
| sign | String | Signature — format rule: `clientUserId[]timestamp[]clientId[]`. See [Code Examples (Java)](#code-examples-java) for signing details |
| redirectUrl | String | Redirection address — broker platform link automatically redirected to after authorization |
| serialNo | String | Random serial number — 1) the broker must validate the legitimacy of the callback request using this parameter; 2) for security reasons, bind `serialNo` to `clientUserId` and avoid using it across multiple accounts; 3) it's recommended to set an expiration time for `serialNo`, e.g. 10 minutes |

Example URL (make sure to URL-encode all parameters to prevent encoding issues or data loss during transfer):

```Plain Text
https://www.bitget.com/en/account/oauth?clientUserId=123&timestamp=1691117744470&clientId=1&redirectUrl=https://broker-domain/callback/url&sign=F9zeG9oUYzqwN94kSfoFy...&serialNo=123
```

#### Supplementary Information (Optional Configuration)

To help improve user registration conversion, Bitget supports including a referral code in the OAuth redirect URL. When a user is redirected to Bitget via OAuth and clicks **Sign Up**, the referral code included in the URL is automatically detected and pre-filled on the registration page — the user does not need to enter it manually.

Configuration steps:

1. Contact your Broker Operations or BD representative to configure an invitation link (long URL), e.g. `https://www.bitget.com/test?channelCode=abcde&vipCode=1234&languageType=1`.
2. Retrieve the referral code parameter from the invitation link, e.g. `vipCode=1234`.
3. Append this referral code parameter to your Bitget OAuth redirect URL to complete the configuration.

```Plain Text
https://www.bitget.com/account/oauth?clientUserId=123&timestamp=1691117744470&clientId=1&redirectUrl=https://broker-domain/callback/url&sign=F9zeG9oUYzqwN94kSfoFy...&serialNo=123&channelCode=abcde&vipCode=1234
```

### 2. Bitget Callback to Broker Interface

Once the user completes authorization on the page, Bitget will automatically initiate a request to the broker's callback interface.

- Request type: `POST`
- Request URL: to be discussed between both parties
- Content-Type: `application/x-www-form-urlencoded`

| Parameter name | Parameter type | Description |
|:----------------|:-----------------|:--------------|
| openId | String | Encrypted Bitget user ID |
| userId | String | Bitget user ID — returned exclusively for whitelisted users; returned as empty otherwise |
| clientUserId | String | Broker user ID |
| timestamp | String | Timestamp — Unix millisecond format; if the difference from the current time exceeds 5 minutes, the request is considered invalid |
| sign | String | Signature — decrypt with `privateKey` to obtain `serialNo` |
| data | String | Encrypted API key information — decrypt with `privateKey` to obtain the user's `apiKey`, `passphrase`, and `secret` |

#### Additional Information

1. Decrypt `sign` using `privateKey` and verify it matches `serialNo`. If not, the request is invalid.
2. Decrypt `data` using `privateKey` to obtain the API key information in JSON format:

```JSON
{
    "apiKey": "bg_xxxxxxxxxx",
    "passphrase": "***",
    "secret": "xxxxxxxxxxxxxxxxxxxxxxx"
}
```

:::warning{title="Store API key information securely"}
The decrypted `apiKey`, `passphrase`, and `secret` grant trading access to the user's Bitget account. Store them securely (e.g. encrypted at rest) and never expose them in logs, client-side code, or error messages.
:::

After receiving Bitget's request, the broker should respond with:

```JSON
{
    "code": "00000",
    "msg": "success",
    "requestTime": "1671258739383"
}
```

### 3. Bitget Redirects to Broker's Page

Regardless of the authorization result, Bitget will append the following parameters to the URL and redirect to the broker's page.

| Parameter name | Parameter type | Description |
|:----------------|:-----------------|:--------------|
| clientUserId | String | Broker user ID |
| sign | String | Signature — the `serialNo` encrypted with the public key; needs to be decrypted |
| state | String | Authorization status — encrypted with the public key; needs to be decrypted. `SUCCESS` = authorization successful, `FAIL` = authorization failure |

Example:

```HTTP
https://broker-domain/callback/url?sign=sahkduhas1237hkNAQGS&clientUserId=123&state=DHJSAshaj
```

#### Additional Information

1. Decrypt `sign` using `privateKey` and verify it matches `serialNo`. If not, the request is invalid.
2. It's recommended to query `clientUserId` using `serialNo` and verify it matches the `clientUserId` in the current URL. If not, the request is invalid.
