【公告】: 运力合作平台文档已迁移至开放平台门户,本站的文档已不再维护, 欢迎使用开放平台门户 ×

滴滴运力接入平台

欢迎接入滴滴运力接入平台


概述

文档说明

编码方式:

UTF-8

请求格式:

请求采用key-value模式。
header 包含 Content-Type: application/x-www-form-urlencoded

合作方TraceID打通:

合作方需要按如下方式生成 trace_id, 并添加到 每个请求 的: Didi-Header-Rid 字段中, 推荐作为日志记录下来, 方便后期与滴滴排查问题

eg:
请求示例
curl -X "POST" "http://http://:8000/tripcloud/v1/passenger/inner/pEstimatePrice" \
       -H 'Didi-Header-Rid: 43276baa5dca68a000001bbbbbb0fdddd' \
       -H 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' \
       --data-urlencode "business_id=415" \
       --data-urlencode "....."
生成方式如下:
php
demo-php 点击展开
 
class Trace
{
    public static function getServerIp() {
        if (isset($_SERVER)) {
            if(isset($_SERVER['SERVER_ADDR'])) {
                $server_ip = $_SERVER['SERVER_ADDR'];
            } else {
                if(isset($_SERVER['LOCAL_ADDR']))
                    $server_ip = $_SERVER['LOCAL_ADDR'];
            }
        } else {
            $server_ip = getenv('SERVER_ADDR');
        }
        if(!isset($server_ip) || $server_ip == "127.0.0.1" || $server_ip == 0){
            $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
            socket_connect($sock, "8.8.8.8", 53);
            socket_getsockname($sock, $server_ip);
            $_SERVER['SERVER_ADDR'] = $server_ip;
        }
        return isset($server_ip) ? $server_ip : 0;
    }
 
    public static function generateTraceId($appId)
    {
        $ip = self::getServerIp();
        if ($ip == 0) {
            $cmd = "ifconfig | grep -A1 'eth0' | grep 'inet addr:' | awk '{print $2}' | awk -F ':' '{print $2}'";
            $ip = shell_exec($cmd);
            if(empty($ip)){
                $ip = "127.0.0.1";
            }
        }
        $timestamp = time();
        $timeNano = sprintf("%d", microtime(true) * 1e9);
        $ip = explode('.', $ip);
        $pid = getmypid();
        $str = '';
        foreach ($ip as $num) {
            $str .= sprintf("%02x", $num);
        }
        $str .= sprintf("%x", $timestamp & 0xffffffff);
        $str .= sprintf("%04x", $timeNano & 0xffff);
        $str .= sprintf("%04x", $pid & 0xffff);
        $str .= sprintf("%06x", rand(0,(1<<24)-1));
        $str .= sprintf("%02x", $appId % 1000 + 32);
 
        return $str;
    }
}
 
echo Trace::generateTraceId(1002);

                            
go
demo-go 点击展开
package main
 
 
import (
    "bytes"
    "encoding/hex"
    "fmt"
    "math/rand"
    "net"
    "os"
    "time"
)
 
var (
    ip string
)
 
//GenTraceId
func GenTraceId(appId int) string {
    if ip == "" {
        ip = getLocalIP()
    }
    now := time.Now()
    timestamp := uint32(now.Unix())
    timeNano := now.UnixNano()
    pid := os.Getpid()
    b := bytes.Buffer{}
 
    b.WriteString(hex.EncodeToString(net.ParseIP(ip).To4()))
    b.WriteString(fmt.Sprintf("%x", timestamp&0xffffffff))
    b.WriteString(fmt.Sprintf("%04x", timeNano&0xffff))
    b.WriteString(fmt.Sprintf("%04x", pid&0xffff))
    b.WriteString(fmt.Sprintf("%06x", rand.Int31n(1<<24)))
    b.WriteString(fmt.Sprintf("%02x", appId % 1000 + 32))
 
    return b.String()
}
 
func getLocalIP() string {
    ip := "127.0.0.1"
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        return ip
    }
    for _, a := range addrs {
        if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                ip = ipnet.IP.String()
                break
            }
        }
    }
 
    return ip
}
 
func main() {
    print(GenTraceId(1002))
}
                        
java
demo-java 点击展开
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
 
 
public class TraceId {
 
    public static void main(String[] args) {
        // app_id
        System.out.print(getTraceId(1002));
    }
 
    private static final long ipFactor = getIpFactor();
    private static AtomicLong counter = new AtomicLong();
 
    static {
        counter.set(0);
    }
 
    private static long ip2long(String ip) {
        int ret = 0;
        String ipGroups[] = ip.split("\\.");
        Integer[] ipInt = new Integer[4];
        for (int i = 0; i < ipGroups.length; i++) {
            ipInt[i] = Integer.valueOf(ipGroups[i]);
        }
        ret = ipInt[0] << 24 | ipInt[1] << 16 | ipInt[2] << 8 | ipInt[3];
        return ret;
    }
 
    private static long timeFactor() {
        Date data = new Date();
        long msec = data.getTime();
        return msec;
    }
 
    private static long timeFactorInSecond() {
        return timeFactor() / 1000;
    }
 
    private static long randFactor() {
        long ret = (long) (Math.random() * Math.pow(2, 32));
        return ret;
    }
 
    private static List getLocalIpList() {
        List ipList = new ArrayList();
        try {
            Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();
            NetworkInterface networkInterface;
            Enumeration inetAddresses;
            InetAddress inetAddress;
            String ip;
            while (networkInterfaces.hasMoreElements()) {
                networkInterface = networkInterfaces.nextElement();
                inetAddresses = networkInterface.getInetAddresses();
                while (inetAddresses.hasMoreElements()) {
                    inetAddress = inetAddresses.nextElement();
                    if (inetAddress != null && inetAddress instanceof Inet4Address) { // IPV4
                        ip = inetAddress.getHostAddress();
                        ipList.add(ip);
                    }
                }
            }
        } catch (SocketException e) {
        }
        return ipList;
    }
 
    private static long getIpFactor() {
        List ips = getLocalIpList();
        String ipAddr = "127.0.0.1";
        if (ips == null || ips.size() == 0) {
            return ip2long(ipAddr);
        }
        for (String iptmp : ips) {
            if (iptmp.equals("127.0.0.1")) {
                continue;
            } else {
                ipAddr = iptmp;
                break;
            }
        }
        return ip2long(ipAddr);
    }
 
    public static String getTraceId(int appId) {
        appId = appId % 1000 + 32;
        long timeFactor = timeFactorInSecond();
        long randFactor1 = randFactor();
        long randFactor2 = counter.incrementAndGet();
        long traceIdpart1 = ipFactor << 32 | (timeFactor & 0xffffffff);
        long traceIdpart2 = randFactor1 << 32 | (randFactor2 & 0xffffff) << 8 | appId;
        return handleFormat(Long.toHexString(traceIdpart1), 16) + handleFormat(Long.toHexString(traceIdpart2), 16);
    }
 
    private static String handleFormat(String value, int digit) {
        if (value == null) {
            return "00000000";
        }
        while (value.length() < digit) {
            value = "0" + value;
        }
        return value;
    }
}
                        

返回结果统一约定:

参数名称 类型 是否必选 说明
errno int 服务响应状态,参考附录
errmsg string 服务响应状态说明,参考附录
data object 详情数据


鉴权说明

准入流程

网络协议: HTTPS
请求方式: POST
请求准入方案采用: ip白名单 + 请求加签 , 接入方需要提供请求ip,加密采用非对称RSA密钥对,密钥位数1024位,密钥格式:PKCS#8

注意事项

验签三方不可对字段做强制限制,确保接口滴滴增加或减少字段时,验签逻辑通用可过

url加签

url提交的request中必须带有三个参数: app_id、timestamp和sign
流程图:
字段名 类型 是否必选 说明
app_id string 滴滴分配给合作方的唯一标识
timestamp string 请求发送时的秒级时间戳
sign string 1. 第三方请求滴滴时,第三方生成秘钥对,私钥自己持有,公钥告知滴滴
2. 滴滴同样会告知第三方公钥,用于response解签
3. 请求时对参数列表(除了sign)加签,加签规则见下节:sign生成规则&demo

sign生成规则&demo

sign生成规则
1.将参数列表中除了sign的字段按照key升序排列,类似get的方式,用“=”和“&”拼接成字符串
2.将编码得到的字符串使用私钥加密,密文字符串进行base64编码,得到的结果就是sign的值
sign生成demo

秘钥:
-----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMHpxwUNH3UQcv50 rKMHCcJFwfJQBqCumy1+hoVr7wYbt0XRvQJ2v/oOCKXFpD5Nb1kJhIKN+aOryVgh V+af2Y06ZirxIHaRt4UbrW4OqqBgJtjzaJ8yo/SoMY83A7+ctjKfUYeWgQTAaoij 3cw+CbweLV5c6AGgRxvrDTEyW8dhAgMBAAECgYAJbUYBltu6oywT9rQV0NfGnAGL uBw6X4KnuYjsn4ylLV/Bgyq/HerDSz9cX7lWVglduLq6ZhCGxmkpYaWWTpsSzsRl W0yb1PkJmbboSWW0aQ87Rm/Tvk/dZ5cGSWOzC/sXOgQonUbPU0la7fwM3yMIQoXY +k8jNBpWdtGCIRIByQJBAPf/c0ZLvYyG8QWTIDYQjbYPeA/YsJsaUMLCpx2Y5yp5 ndOttFRX3IeGOPYcMXwNSJ7+nJJD8BV7ngmVnKZ7bpcCQQDIK5Ga7JOcLkhVs0sW 9JBu+zqB7Uk3oSAp27ZopSVJunm1WgMIWpr03BkXvdbtD/HYXT2FNTj5t4Tb2D9T x7DHAkEAqcMF5/Lk+BNPXd+OxzOhriT8rOxKSIJFEm0o9Iu8gkjqDwLzVGEopuTs jRxTi3WUZrIn/7/d0vbiAfGWYChSVQJAQRTJVpGsvI7fvd15gJErlKniL/QyZf/h MTraZ9Op9/rFL42AhurOjuYw0mNKyfDxNOO76N+REr/0VnZMwLSgaQJBALsPFVqX fy90CE/RFjuqGm3NS9vOmJDkhlzVDMT+JunrH+BLqpQJvqkkM/gXebQDgUhrp4Ab Vyqog8xcJMhx/Es= -----END PRIVATE KEY-----

公钥:
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDB6ccFDR91EHL+dKyjBwnCRcHy UAagrpstfoaFa+8GG7dF0b0Cdr/6DgilxaQ+TW9ZCYSCjfmjq8lYIVfmn9mNOmYq 8SB2kbeFG61uDqqgYCbY82ifMqP0qDGPNwO/nLYyn1GHloEEwGqIo93MPgm8Hi1e XOgBoEcb6w0xMlvHYQIDAQAB -----END PUBLIC KEY-----

参数列表: b=2; a=1; app_id=1; timestamp=1552274633

1.组织参数列表得:
     a=1&app_id=1&b=2& timestamp=1552274633
2.使用私钥加密,base64后得:
    F3vEQPFPIGWr+nBca/lYgJEUfybL6+zIn7XFcTjM2QY4aJzEo9AxdoXDxhsD7MvjO9
HLQq5OwSYTITe5SBUXU5h3KVlCsQX6v9pFlSGCDuDtIsX4kFkYMHjGjTUWywTWbFd4BAW06FKhUpfSWvA1pkpaqGcsXs1i2oKN4mNduY8=
3.最终请求参数为:
    "a=1&b=2&app_id=1& timestamp=1552274633&sign=F3vEQPFPIGWr%2bnBca%2flYgJEUfybL
6%2bzIn7XFcTjM2QY4aJzEo9AxdoXDxhsD7MvjO9HLQq5OwSYTITe5SBUXU5h3KVlCsQX6v9pFlSGCDuDtIsX4kFkYMHjG
jTUWywTWbFd4BAW06FKhUpfSWvA1pkpaqGcsXs1i2oKN4mNduY8%3d"

加签验签代码实现(php)

/**
* 根据私钥生成签名
* @param array $assocArr
* @param $rsaPriKeyStr
* @return string
* @throws \Exception
*/
public function genSignWithRsa(array $assocArr, $rsaPriKeyStr)
{
    $sign = '';
    if (empty($rsaPriKeyStr)) {
        return $sign;
    }
    if (!function_exists('openssl_pkey_get_private') || !function_exists('openssl_sign')) {
        throw new \Exception("openssl扩展不存在");
     }

    $priKey = openssl_pkey_get_private($rsaPriKeyStr);

    if (isset($assocArr['sign'])) {
         unset($assocArr['sign']);
    }

    ksort($assocArr); //按字母升序排序
    $parts = array();
    foreach ($assocArr as $k => $v) {
        $parts[] = $k . '=' . $v;
     }
    $str = implode('&', $parts);
    openssl_sign($str, $sign, $priKey);
    openssl_free_key($priKey);
    return base64_encode($sign);
}

/**
* @desc 公钥校验签名
* @param array $assocArr
* @param $rsaPubKeyStr
* @return bool
* @throws /Exception
*/
public function checkSignWithRsa(array $assocArr, $rsaPubKeyStr)
{
    if (!isset($assocArr['sign']) || empty($assocArr) || empty($rsaPubKeyStr)) {
     return false;
    }
    if (!function_exists('openssl_pkey_get_public') || !function_exists('openssl_verify')) {
        throw new \Exception("openssl扩展不存在");
    }

    $sign = $assocArr['sign'];
    unset($assocArr['sign']);

    if (empty($assocArr)) {
        return false;
    }
    ksort($assocArr); //按字母升序排序
    $parts = array();
    foreach ($assocArr as $k => $v) {
        if (is_array($v)) {
            $v = json_encode($v);
        }

        $parts[] = $k . '=' . $v;
    }
    $str = implode('&', $parts);
    $sign = base64_decode($sign);
    $pubKey = openssl_pkey_get_public($rsaPubKeyStr);
    $result = (bool)openssl_verify($str, $sign, $pubKey);
    openssl_free_key($pubKey);
    return $result;
}
                

加签验签代码实现(JAVA)

java版本,请使用 SHA1WithRSA 进行加签和验签。


附录1

运力类型:

合作方可能提供多种产品类型(product_type)的服务,如快车,专车。
每种产品类型下包含一个或多个运力类型(ride_type),如专车的舒适型,商务型和豪华型。
产品类型 (product_type) 运力类型(ride_type) 描述
express-car express 快车-经济
express-car superior 快车-优享
private-car compact 专车-舒适
private-car premium 专车-商务
private-car luxury 专车-豪华
taxi taxi 出租车
discounts short-distance 短途特惠

订单状态:

状态码 名称 说明
100 正在派单 正在派发订单,等待司机接单
101 司机接单 司机接到订单
102 司机到达 司机到达目的地,未开始计费
103 开始计费 开始行程,开始计费
104 订单完成 司机结束计费,乘客未支付
105 订单完成 司机结束计费,乘客支付完成
106 乘客取消 乘客取消
107 取消待支付 取消产生判责费用
108 客服关闭 客服关闭,第三方取消
109 未能完成服务状态,已改派 未能完成服务状态,司机取消(暂不支持改派)
110 该订单已被其他平台受理 -
111 第三方无司机应答 派单一定时间后,无司机应答

支付渠道:

渠道名称 渠道id 说明
微信支付 127
阿里支付 128
银联支付 129
手QQ支付 130
财付通网关 131
QQ钱包支付 132
微信代扣 133
阿里代扣 134
一网通银行卡支付 135
一网通银行卡支付-代扣 136
微信预授权支付 137
阿里预授权支付 138
百度钱包支付 139
百度钱包代扣 140
连连支付 141
连连代扣 142
阿里PC支付 143
QQ钱包代扣 144
银联信用卡消费 146
银联信用卡预授权 147

错误码:

errno errmsg 说明
0 success 业务返回成功
10100 服务器错误 服务器错误
10101 网络超时 网络超时
10102 签名错误 签名错误
10103 参数错误 参数错误
10104 获取配置错误 获取配置错误
10105 请求过期 请求过期
12001 订单已被其他平台受理 订单已被其他平台受理
12002 订单流转非法 订单流转非法
13001 抢单失败 抢单失败
13002 抢单失败-订单已取消 抢单失败-订单已取消
13003 抢单失败-订单已被抢 抢单失败-订单已被抢
13004 抢单失败-播单超时 抢单失败-播单超时
13010 抢单成功-被自己抢 抢单成功-被自己抢
14001 订单未完成 订单未完成
14002 订单已支付 订单已支付
14003 订单完单失败 订单完单失败
596201 未开通该区 未开通该区
- - 其他待补充(找RD同学)

城市列表: