# Access

# Before Accessing

  • Please ensure that the appropriate permissions are enabled for API access. Unauthorized API access attempts may result in account suspension.
  • Please confirm that the access frequency limit is configured properly. Exceeding the limit will result in a temporary suspension.
  • Please take appropriate measures to prevent reentrancy, as network issues may cause multiple callbacks which result in duplicate callback messages being sent to the accessing party, and this can have an adverse impact on users.
  • Please verify the pushed data to avoid data falsification or loss.
  • Users can only get information about drivers in their fleet.

# Request Access to Relevant Keys

To request access keys and decryption keys, please go to fleet website > Account > open api. Keys will be provided once your request is approved by the reviewer.

# API Portals

Production Environment:

https://open-jelly-ru.didiglobal.com/ 
1

Sandbox Environment:

https://open-jelly-ru-sim.didiglobal.com
1

# Service Calling

Production Environment:

//Driver Balance Management
curl -X POST 
-H "Content-Type: application/json" 
-H "Authorization: bearer {cid}|{access_token}"
-d 'json {
    "fleet_id": 10000000000000,
    "driver_id": 50000000000000,
    "location_country": "RU",
    "lang": "ru-RU"
}' 
'https://open-jelly-ru.didiglobal.com/biz/fleet/open-api/drivers/getBalance'
1
2
3
4
5
6
7
8
9
10
11

Sandbox Environment:

//Driver Balance Management
curl -X POST 
-H "Content-Type: application/json" 
-H "Authorization: bearer {cid}|{access_token}"
-d 'json {
    "fleet_id": 10000000000000,
    "driver_id": 50000000000000,
    "location_country": "RU",
    "lang": "ru-RU"
}' 
'https://open-jelly-ru-sim.didiglobal.com/biz/fleet/open-api/drivers/getBalance'
1
2
3
4
5
6
7
8
9
10
11

Note

domain + "/biz" + api_url is required to call service API Don’t forget "/biz"

# Security Verification

Method: POST

URL: /oauth/token

Feature: Oauth2.0 Verification

Requested Parameters:

Parameter Name Type Description
grant_type string Request mode. Only two modes are supported. i.Client mode: client_credentials ii. Refresh token: refresh_token
scope string Requested permissions list.
_ string Current time, the format is "2006-01-02T15:04:05-0700", valid for 10 minutes. Not checked in test environment
nostr string A random string. 6 characters with a combination of digits and letters (not case sensitive)
refresh_token string refresh_token field received when requesting token(Required for refreshing)

Scope Value Ranges

Scope Value Description
fleet fleet features

Request token:

curl -X POST -H "Content-Type: application/json"
-H "Authorization: Bearer {cid}|{sign}"
-d '{"grant_type":"client_credentials","_":"2016-07-01T10:00:00+0800""nostr":"123abc"}'
'https://open-jelly-ru.didiglobal.com/oauth/token'
1
2
3
4

Refresh token:

curl -X POST -H "Authorization: Bearer {cid}|{sign}" 
-H "Content-Type: application/json" 
-d '{"grant_type":"refresh_token","refresh_token":"43713d0303-49c60a08fe-835c9fc1fe","_":"2016-07-01T11:00:00+0800","nostr":"123abc"}' 
'https://open-jelly-ru.didiglobal.com/oauth/token'
1
2
3
4

Normal Return:

Parameter Name Type Description
access_token string Access token. Must be provided by header when requesting other APIs. Multiple tokens can be active on the platform at once
refresh_token string Refresh token, which is used when the access_token expires. This token can only be valid one time. After refreshing, a new refresh_token will be assigned. The token does not have a fixed expiration date
expires_in_second int access_token’s validity period (seconds)
token_type string token type, bearer/mac. Only bear type is supported.
scope string token access level (same as the requested level)

Note

Frequency limit for requesting a token: Platform <= 10 times/day; Refresh <= 10 times/day. Once this limit is exceeded, the account will be suspended for 24 hours

Returned Result:

http status

  • 200 indicates the request is valid
  • Non-200 indicates a request error

HTTP/1.1 200 OK

Content-Type: application/json;charset=UTF-8

{
"access_token": "didiCB5DEFD8086CE8EAC49CFDCFFA3BF94E66BFC287113521F868B9257E8782D0DB978B81A04133BA5968D9B15B6A4655A9B3B8E7E856ED69BC1FF5848DC784C5080523D74F1AA6B2AC310219BAD3CDE24DF60E61A83B67565B0FC63DE417858FF4",
"refresh_token": "bd01a0d243-e4a056f463-138c7a00fd",
"expires_in_second": 259200,
"token_type": "bearer",
"scope": "calling_car"
}
1
2
3
4
5
6
7

Description:

  1. Request data format must be in json
  2. header should contain the accessing party’s id and signature. Authorization: bearer {cid}|{sign}. bearer represents the token type. cid is the assigned accessing party id, which is connected to the signature sign with "|"
  3. Signature algorithm

php version

function generalCreateSign( $aData = array(), $sKey = '' )
{
    if ( empty( $aData ) || ! is_string( $sKey ) ) { //Signature data cannot be blank, and key must be a string
        return FALSE;
    }

    ksort( $aData ); //Signature data is sorted by key value in the dictionary order
    $str = '';
    foreach ( $aData as $k => $v ) {
        if ( empty( $v ) && $v !== 0 ) { //Empty key value is not used in signature
            continue;
        }

        if ( is_array( $v ) ) {
            $v = json_encode( $v );
        }
        $str .= trim( $k ) . '=' . trim( $v ) . '&'; //Key1=Value1&Key2=Value2...
    }
    $str = trim( $str, '&' ); //Remove & characters at two ends of the string
    return md5( md5( $str ) . $sKey ); //2 times md5, key is the signature assigned to the accessing party's key
}

golang version

func generalCreateSign(req GetTokenReq, clientSecret string) string {
	reqData := map[string]string{}
	keys := []string{}

	t := reflect.TypeOf(req)
	v := reflect.ValueOf(req)
	for i := 0; i < t.NumField(); i++ {
		tag := t.Field(i).Tag.Get("json")
		value := v.Field(i).String() // req All fields are string type
		reqData[tag] = value
		keys = append(keys, tag)
	}

	// Sort by dictionary order
	sort.Strings(keys)

	sign := ""
	for _, k := range keys {
		if reqData[k] == "" {
			continue
		}

		// k1=v1&k2=v2...
		sign += strings.TrimSpace(k) + "=" + strings.TrimSpace(reqData[k]) + "&"
	}
	sign = strings.Trim(sign, "&")

	withSecret := fmt.Sprintf("%x", md5.Sum([]byte(sign))) + clientSecret
	return fmt.Sprintf("%x", md5.Sum([]byte(withSecret)))
}

c# version

public static string GenerateMD5(string txt)
{
	byte[] buffer = Encoding.Default.GetBytes(txt);
	byte[] newBuffer = MD5.Create().ComputeHash(buffer);
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < newBuffer.Length; i++)
	{
		sb.Append(newBuffer[i].ToString("x2"));
	}
	return sb.ToString();
}

public static string GeneralCreateSign(IDictionary parameters, string secret)
{
	IDictionary sortedParams = new SortedDictionary(parameters);
	IEnumerator> iterator = sortedParams.GetEnumerator();

	StringBuilder strBuilder = new StringBuilder();
	while (iterator.MoveNext())
	{
		string key = iterator.Current.Key;
		string value = iterator.Current.Value;
		if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
		{
			strBuilder.Append(key).Append("=").Append(value).Append("&");
		}
	}

	string finalString = strBuilder.ToString().Trim('&');

	string result = GenerateMD5(GenerateMD5(finalString) + secret);

	return result;
}

java version

public static String Md5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    byte s[] = MessageDigest.getInstance("md5").digest(str.getBytes("UTF8"));
    String Md5String = "";
    for (int i = 0; i < s.length; i++) {
        Md5String += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00).substring(6);
    }
    return Md5String;

}

public static Map sortMapByKey(Map map) {
    if (map == null || map.isEmpty()) {
        return null;
    }
    Map sortMap = new TreeMap(new Comparator() {
        public int compare(String obj1, String obj2) {
            return obj1.compareTo(obj2);//Sort by ascending
        }
    });
    sortMap.putAll(map);
    return sortMap;
}

public static String CreateSign(Map params, String key) throws Exception {

    if (key.isEmpty() || params.isEmpty()){
        throw new Exception("sign key is empty or param is empty");
    }
    // Sort
    params = sortMapByKey(params);
    Iterator> it = params.entrySet().iterator();

    String sign = "";
    while (it.hasNext()){
        Map.Entry entry =  it.next();
        String k = entry.getKey();
        String v = entry.getValue();

        if (!v.isEmpty()){
            sign+=k.trim()+"="+v.trim()+"&";
        }
    }
    // Remove & at the end
    sign = sign.substring(0,sign.length()-1);
    // md5
    return Md5(Md5(sign)+key) ;
}