【公告】: 运力合作平台文档已迁移至开放平台门户,本站的文档已不再维护,
欢迎使用开放平台门户
×
滴滴运力接入平台
欢迎接入滴滴运力接入平台
概述
文档说明
编码方式:
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 ListgetLocalIpList() { 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同学) |