导图社区 接入美团第三方开放平台
这是一篇关于接入美团第三方开放平台的思维导图。 平台介绍 平台概述:平台简介、平台角色、平台优势、平台规则 开发指南
编辑于2022-08-15 15:21:53 云南接入第三方开放平台 美团
https://developer.meituan.com/
平台介绍
平台概述
平台简介

基于
美团人工智能
本地生活业务场景
提供
AI核心技术
视觉技术
自然语言处理
语音语义
知识图谱
业务能力
外卖
到店餐饮
配送
餐饮系统
快驴
实现
丰富的场景能力
广泛的商家需求及市场商机
更安全的数据保障和可靠服务
平台角色
服务商
即我行:通过申请可获得开放平台的技术文档、应用程序等相关服务支持和运营服务支持
开发者
即建信金科:服务商中的系统开发人员,对接入平台进行系统开发
应用
ISV服务商基于开放平台所开发的应用程序或软件服务
自用型应用
他用型应用
openAPI
开放平台为服务商应用接入提供可调用的数据接口
SDK
开放平台为服务商应用接入提供的可调用的编程接口
UI SDK
开放平台为服务商应用接入提供的可调用并嵌套的界面应用。
developerID
开发者在开放平台上唯一身份标识,需要注意的是DeveloperID和开发者的商户(门店)是唯一对应的关系。
SignKey
一般配合开发者的DeveloperID一起成对使用,用来验证开发者的身份是否正确。
平台优势
业务优势
业务场景
外卖
配送
到店餐饮
收银
收单
商家资源
美团百万级的门店商家资源
美团的商户能否为我平台所用?
用户资源
4亿用户资源
服务保障
平台运营
开放平台专业运营团队,为商家及服务商提供快速入驻审核、高效运营答疑、定期业务培训等运营保障
稳定性监控
开放平台提供服务商业务稳定性监控,多渠道实时预警消息推送,为商家及服务商业务平稳运营提供技术保障。
品牌扶持
服务商入驻成功后,将获得由开放平台官宣授权的合作授权书,并对优质服务商进行定期流量曝光,为服务商企业品牌注入价值,持续提升市场影响力。
技术保障
开放平台丰富的技术文档,线上答疑,帮助服务商快速接入,高效开发。
增值服务
开放平台丰富的增值业务,让服务商可以有效利用自有流量不断完善多元化营收增长。
平台规则
第三方开发者(服务商)——即我行
开放平台运营管理规则
相关定义
开放平台
url:http://developer.meituan.com
提供
技术文档
应用程序
其他相关服务
服务商
即我行
开发者
即建信金科:服务商中的系统开发人员,对接入平台进行系统开发
应用
ISV服务商基于开放平台所开发的应用程序或软件服务
自用型应用
他用型应用
SDK
开放平台为服务商应用接入提供的可调用的编程接口
UI SDK
开放平台为服务商应用接入提供的可调用并嵌套的界面应用。
SignKey
一般配合开发者的DeveloperID一起成对使用,用来验证开发者的身份是否正确。
openAPI
开放平台为服务商应用接入提供可调用的数据接口
developerID
开发者在开放平台上唯一身份标识,需要注意的是DeveloperID和开发者的商户(门店)是唯一对应的关系。
开放平台目标
服务商
对接美团平台业务能力
提供技术、运营服务
美团商家
提高商家经营能力及效率,满足商家经营中多场景服务需求。
消费者
为消费者提供更优质的服务和体验。
开放平台
提升美团在行业的整体能力与品牌价值,促进生态的繁荣发展。
开放平台整体服务流程
接入指南
注册
开发者的账号密码具有唯一性,开发者需对账号下的一切操作承担全部责任与法律后果
填写申请信息提交相关资质
签订合同并邮寄
保证金支付
成功入驻开放平台获得开放DeveloperId&SignKey,根据接口文档进行开发
提交上线申请并经平台审核
门店绑定
开放平台运营管理规则
资质审核规则(准入)

产品上线审核规则(上线)

上线后运营规则
  
补充条款

美团外卖订单小票打印规范
保证金协议规则
第三方应用开发安全规范
通用准则
设立安全角色
至少保证组织中有1名人员作为安全角色
补丁和漏洞管理
必须及时安装和安全性相关补丁
关键和重要的更新补丁需要在十天内部署
中等优先级的更新补丁需要在三十天内部署
低等优先级的更新及补丁可以在九十天内部署
避免弱口令
同时满足
长度≥8位
包含数字、字母、特殊符号
避免键盘连续字符
避免口令包含用户名
通信加密
SSL
防通信伪造
对于HTTP应用,通过参数签名验签
规范细则
以Java为默认语言
主机安全
主机应该及时打安全补丁,避免攻击者通过相关利用程序攻击。
如果使用云厂商服务,请关注其是否提供补丁服务。
通信安全
应用通信使用HTTPS协议,避免通信被劫持、窃听、中间人攻击。
SSLTLS版本应当禁用存在诸多安全风险的版本,包括SSLv1、SSLv2、SSLv3、TLSv1.0;如果可行,禁用TLSv1.1。
中间件安全
tomcat
防止管理页面弱口令
Tomcat默认携带Manager应用,用于管理部署、卸载等,如果使用管理页面,需要设置强密码
代码块
|--bin
|--conf
|--webapps
| |--manager
| |--ROOT
| |--examples
关闭指令增强
如果需要通过本地Tomcat服务的网络端口来关闭Tomcat,关闭指令必须设置为强复杂度并且难以猜测。
编辑文件CATALINA_HOME/conf/server.xml,设置关闭指令
<Server port="8005" shutdown="这里设置成复杂的指令,防止被猜测">
通常该功能不是必需的,如果您确认不需要使用此功能,请禁用之
<Server port="-1" shutdown="SHUTDOWN">
禁用不必要的HTTP方法
Tomcat服务器提供默认http方法包括GET、HEAD、POST、PUT、DELETE、OPTIONS。
在配置前,需要了解产品是否会使用到这些HTTP方法,如果用不到,则必须禁用。
在tomcat目录下的web.xml中配置
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
在Tomcat的web.xml 文件中配置org.apache.catalina.servlets.DefaultServlet的初始化参数
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
禁用WebDAV
WebDAV(Web-based Distributed Authoring and Versioning)是基于 HTTP 1.1 的一个通信协议。它为 HTTP 1.1 添加了一些扩展,使得应用程序可以直接将文件写到 Web Server 上。WebDAV存在一定的安全问题。
Tomcat支持WebDAV的,但是默认不启用。 请确保以下配置在web.xml中不存在或者为注释状态
<servlet>
<servlet-name>webdav</servlet-name>
<servlet-class>org.apache.catalina.servlets.WebdavServlet</servlet-class>
</servlet>
禁用列目录
允许列目录会造成信息泄露;另一方面,由于生成具有数千个文件目录的列表会占用大量的CPU资源,将会导致DoS攻击。
生产环境必须禁用列目录,设置DefaultServlet设置list为false
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
删除示例程序
Tomcat默认携带示例程序,提供样例,通常在生产环境中,您不会使用该示例程序
删除示例程序(删除webappsexamples目录)
|--bin
|--conf
|--webapps
| |--manager
| |--ROOT
| |--examples
JBoss
jmx-console增加密码限制
如果您不需要使用jmx-console,优先删除其对应的war($JBOSS_HOME/[server]/all/deploy 和 $JBOSS_HOME/[server]/default/deploy下的 jmx-console.war )。
如果您确实使用,请启用认证,且使用强复杂度口令。
防止反序列化造成远程命令执行
JBoss中的http-invoker.sar暴露的/invoker/readonly 接口可以接受序列化数据,接口执行反序列化时未恰当检查。通过精心构造恶意的序列化数据,可以远程执行命令。通常应用不需要http-invoker.sar 组件,如果您确认不需要,可删除此组件。
Weblogic
防止反序列化造成远程命令执行
近年来,Weblogic陆续被披露存在高危漏洞,其中最常见的是反序列化,通过精心构造恶意的序列化数据,可以远程执行命令,入侵系统。如果您的应用使用了Weblogic,请密切关注其官方的安全公告,并及时更新安全补丁。
Mongodb
防止Mongodb未授权访问
Mongodb默认监听27017端口,未启用认证,攻击者如果网络可达的话,可能会访问您的Mongodb,查询、删除、修改您的缓存数据。
强烈不建议Mongodb服务发布到互联网上
绑定监听IP
// 当在启动mongodb的时候,使用-bind_ip指定监听IP,数据库实例将只监听192.168.0.1的请求
-bind_ip 192.168.0.1
启动基于角色的登录认证
Redis
防止Redis未授权访问
Redis默认监听6379端口,未启用认证,攻击者如果网络可达的话,可能会访问您的Redis,查询、删除、修改您的缓存数据。
强烈不建议Redis服务发布到互联网上
配置Redis仅监听本地地址
# 修改redis.conf配置文件
bind 127.0.0.1 192.168.13.12 #如只监听来自127.0.0.1 192.168.13.12的请求
配置访问密码
# 修改redis.conf配置文件
requirepass @4KI&1*)Ljq19494jd
隐藏重要命令
// 将 config flushdb flushall 设置为空,即禁用该命令;
// 也可重命名为为一些复杂的、难以猜测的名字
rename-command CONFIG ""
rename-command flushall ""
rename-command flushdb ""
rename-command shutdown shotdown_test
Elasticsearch
防止Elasticsearch未授权访问
Elasticsearch 默认端口是 9200,绑定的是本机 127.0.0.1 ,默认参数是安全的。但是如果您修改参数,将Elasticsearch 暴露在互联网,将会带来巨大的安全隐患。如果您确定您需要将Elasticsearch 暴露在互联网,请启用认证:
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-network.html#common-network-settings
Jekins
对管理控制台启用访问控制
通常,管理人员不需要直接在互联网上进行管理,仅需要根据业务自身需求,对管理控制台访问源IP、端口进行限制,防止被攻击者访问管理控制台。关于Jekins的更多加固,请参考:
https://jenkins.io/doc/book/system-administration/security/?spm=a2c4g.11186623.2.19.797a4f6aWwVg1N
服务安全
防止SSH弱口令
防止MySQL弱口令
防止SQL Server弱口令
防止FTP弱口令
禁用FTP匿名登录
应用安全
越权防御
您需要确保受限资源仅允许被资源所有者(或所有者授权的主体)访问。
防止水平越权
需要杜绝诸如“用户张三查看李四的订单“、”商户王老板修改商户赵老板的用户信息“。
通常需要配合认证信息来完成检查,比如用户张三登录之后查看订单
//1. 在控制层中检查
// 根据订单Id查询订单信息
OrderVo order = OrderService.selectByPrimaryId(id);
// 检查订单所属人与当前登录用户一致
assert(order.getUid() == request.getAttribute("user-context").getUid());
//2. 在DAO中限制
// 根据订单Id、登录用户id查询订单信息
Integer uid = request.getAttribute("user-context").getUid();
OrderVo order = OrderService.selectByPrimaryIdAndUid(id, uid);
防止垂直越权
需要杜绝诸如“营业员查看财务报表“(假定“营业员”不具备“财务”、“经理”、”老板“才具备的权限)通常需要设计角色,不同角色具备相应的权限,然后给不同账户分配相应角色。您需要在接口层面进行限制角色,并且确保不会遗漏接口。如通过注解在接口层检查角色权限
@ResponseBody
@Auth(roles=["finance", "manager", "boss"])
@RequestMapping(value = "/financeReport", method = RequestMethod.POST)
public ResponseResult financeReport (@RequestBody FinanceSearchDto financeSearchDto) {
// 业务代码
}
防止SQL注入
SQL注入可能会造成数据泄露、数据被篡改、服务器被入侵等一系列严重后果。造成SQL注入主要有两个原因
SQL接收未经净化的参数
参数使用拼接方式
建议使用参数化绑定避免拼接
JDBC
PreparedStatement ps = (java.sql.PreparedStatement) conn.prepareStatement(sql);
ps.setObject(1, request.getParameter("param"));
String sql = "SELECT * FROM `example` WHERE `param`=?";
ResultSet rs = ps.executeQuery();
conn.execqueryResultSet(sql);
//使用参数绑定,避免SQL注入
String sql = "SELECT * FROM `example` WHERE `param`='" + request.getParameter("param") + "'";
JdbcConnection conn = new JdbcConnection();
// 使用拼接接受客户端参数,存在SQL注入
mybatis
<!--使用拼接接受客户端参数,存在SQL注入-->
<select id="selectByName" resultMap="xxx">
SELECT `xx`, `yy`
FROM `example`
WHERE `param`=${param}
</select>
<!--使用参数绑定,避免SQL注入-->
<select id="selectByName" resultMap="xxx">
SELECT `xx`, `yy`
FROM `example`
WHERE `param`=#{param}
</select>
避免跨站脚本攻击
跨站脚本攻击(Cross-Site Scripting,XSS),指恶意脚本被注入到可信的网站中,恶意脚本在浏览器中渲染执行。攻击者可利用XSS漏洞获取用户Cookie,传播蠕虫,篡改页面或进行钓鱼等。防御XSS通常是通过转义,针对PHP
/*未处理参数输出,存在XSS*/
<?php
$value=$_GET['param'];
echo $vaule;
?>
/*HTML转义,防止XSS*/
<?php
$value=$_GET['param'];
echo htmlspecialchars($vaule);
?>
避免服务端请求伪造
服务端请求伪造(Server-Side Request Forgery, SSRF)应用程序可能存在这样的场景:获取客户端传递的URL参数,然后在服务端发起网络请求下载URL指向的资源。SSRF的危害是跨越了网络边界,从边界外访问边界内的资源。您应当避免没有对URL参数检查就在服务器发起请求,建议做如下检查
限制URL参数中的协议(大部分情况应用仅使用HTTP(s) 就够了)
检查URL中的host,如果可行,使用白名单列表检查host是否匹配;如果无法使用白名单,请检查其解析的IP地址是否是内网地址,您应当避免外部请求内网地址
避免命令注入
应用程序可能存在这样的场景:获取客户端传递的参数,将参数组合到待执行的系统命令中
public class DoStuff {
public string executeCommand(String userName){
try {
String myUid = userName;
Runtime rt = Runtime.getRuntime();
rt.exec("doStuff.exe " + ”-“ + myUid); // 执行doStuff.exe程序
}catch(Exception e){
e.printStackTrace();
}
// 如果myUid为 "zhangsan && shutdown -s" 将会执行关机
}
}
应当避免没有检查参数就将其带入到系统命令中,建议做如下检查
如果可行,使用白名单检查参数是否匹配
避免参数值包含"&", "&&", "|", "||"
避免任意文件上传
检查上传文件后缀名,确保其在白名单列表中,并且保证所保存的文件后缀名在白名单列表中
设置保存文件的目录不具备可执行权限
随机化命名保存文件名
避免使用不安全的第三方组件
在您的应用中,可能会包含成百上千的第三方组件,应当避免使用不安全的组件(或其不安全的版本),特别包含严重和高危漏洞的组件。对于Java和Python的第三方组件评估,您可借助如下开源工具:https://github.com/SAP/vulnerability-assessment-tool
避免XML 外部实体引用
XML 外部实体引用(XML External Entities,XXE)是与解析XML消息相关的漏洞。XML允许外部实体引用
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "dev/random" >]><foo>&xxe;</foo>
关于XXE更详细的介绍参考:https://www.owasp.org/images/5/5d/XML_Exteral_Entity_Attack.pdf https://security.tencent.com/index.php/blog/msg/69在您的业务场景中,也许需要接受客户端的XML消息,然后解析进一步处理业务(如微信公众号中部分与微信服务器的接口)。通常您所要处理的XML消息不需要使用到外部实体引用,因此在使用相关组件解析XML前禁用外部实体引用是非常推荐的做法。禁用外部实体引用往往仅需要简单的几行配置,不同的XML解析器做法不一,具体参考:https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.md
保护Cookie安全
设置Cookie HttpOnly
HttpOnly是设置Cookie时可以设置的一个属性,如果没有设置这个属性,该Cookie值允许被页面的脚本读取。Servlet示例
//没有设置HttpOnly
response.setHeader("SET-COOKIE","token=" + token);
//设置HttpOnly
response.setHeader("SET-COOKIE","token=" + token + ";HttpOnly");
设置Cookie Secure
Secure是设置Cookie时可以设置的一个属性,如果您的应用网站时HTTPS的,可以设置此属性。这样浏览器就只会在访问协议为HTTPS的地址才会发送该Cookie
//没有设置Secure
response.setHeader("SET-COOKIE","token=" + token + ";HttpOnly");
//设置Secure
response.setHeader("SET-COOKIE","token=" + token + ";HttpOnly;Secure");
也可以在Web容器中设置,如Tomcat7可以在/conf/web.xml 中设置
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
合理处理跨域
浏览器严格控制跨域,因为跨域可能会造成严重的信息泄露。然而,跨域访问的业务需求可能总是会存在。当您处理跨域时,务必保证允许被跨域访问的域是在白名单中的。例如,使用CORS(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS)处理跨域,使用Filter处理
String origin = httpServletRequest.getHeader("Origin");
if (Constants.ALLOWED_DOMAIN_List.contains(origin)) {
httpServletResponse.setHeader("Access-Control-Allow-Origin", origin);
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
//...Ohter Header
}
if("OPTIONS".equalsIgnoreCase(httpServletRequest.getMethod())) {
OutputStream out = httpServletResponse.getOutputStream();
out.write("ok".getBytes());
out.flush();
}else{
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
避免源码泄露
避免将您的源码存储在GitHub、云盘中等,源码泄露将会成为影响应用安全的威胁。
避免您的备份文件被下载,如果您要备份文件,确保备份文件不可从互联网下载。
废弃的接口及时下线
如果久接口已经废弃,请及时下线处理。因为当接口出现安全问题时,您可能只兼顾新版本,如果旧版本仍然开放,可能会成为被入侵的风险点。当进行接口下线处理时,仅使用@Deprecated注解标注时不够的,您需要进一步移除路由、或者删除源码
规范行为管理规范
服务商系统安全规范
SLA协议
美团商家账号通协议
开放平台API计费管理规则
服务协议
保密协议
开发指南
第三方开发者(服务商)——即我行
入驻流程
入驻流程
登陆账号
点击申请加入
填写企业资质信息
实名认证
签订电子合同
支付保证金
成为开发者
服务商准入规则

服务商签约所需资料
基本材料
除基本企业信息与申请人信息外
如电子合同签约人为法人,还需提供法定代表人姓名、法定代表人身份证号、法人身份证件照片(正、反面照片)、法定代表人手机号、公司简介(100字以内)。
如电子合同签约人为被授权人,还需提供被授权人姓名、被授权人身份证号、被授权人身份证照片(正反面)、公司简介(100字以内)、法定代表人姓名、法定代表人身份证号(或护照号)、法代表人身份证件照片(正、反面照片)或法人护照首页及第二页照片、业务授权书。
信息安全材料
为营造规范、有序、安全的开放平台环境,提升合作伙伴提供的应用安全性,从而保障开发者及用户的合法权益,同时作为保证金缴纳金额的判定依据,您需提交部分信息安全证明材料。开放平台将给您充足的时间进行材料办理,入驻阶段可暂时无需提交相关安全资质,您可在开发阶段完成安全资质的办理,待您开发完成上线时,必须补交安全材料;若您未能提供相关安全材料,开放平台有权不予审核上线
安全材料名称及材料申请方式

签订电子合同
1. 实名认证
资质申请提交后,需签约人先完成实名认证信息的确认
2. 运营审核
待运营审核通过后,将下发签约短信;
3. 签订电子合同
通过登录签约链接,按照提示进行操作,即可完成电子合同的签订,并进入保证金支付环节。
保证金说明
保证金作用
为保障开放平台服务商向用户提供优质、稳定的服务,并有效履行《开放平台服务协议》下相关约定,因此与开放平台合作服务商需缴纳保证金。保证金支付金额以服务商所属企业为准,如您有多个开发者ID无需再次履行相应的保证金缴纳义务。
保证金金额
开放平台升级调整后会根据服务商入驻申请材料等因素进行评估后,自动生成保证金金额,具体请以签约后缴纳金额为准。若无违规情况,开放平台将于合作终止后的15个工作日内,退还保证金至您的支付账户。有关违规情况的保证金扣除明细,可查看《服务商违规管理行为管理规范》,开放平台会在后续进行公示。
保证金付款
为了避免大额支付失败问题,建议您使用企业网银进行支付。
保证金票据
保证金支付后,您可邮件申请收据,邮件地址:it_meituan.open@meituan.com,具体申请方式会在保证金缴纳环节有相应展示。
服务商入驻咨询
邮件咨询
邮件地址
it_meituan.open@meituan.com
邮件主题
入驻问题咨询
企业名称
邮件内容
请您写明您的企业名称、联系人、联系方式,并清晰描述申请过程中所遇到的疑问,我们收到邮件后会在2个工作日内为您解答。
电话咨询
联系电话:400-0615-100
问题处理时间:工作日上午10:30-下午18:00
接口调用约定
请求说明
开放平台业务接口,目前只支持HTTP POST一种请求方式。
指定Body Content-Type为:application/x-www-form-urlencoded
Post请求时,所有参数均通过表单传递,请勿将请求参数放到Query参数中。
参数说明
对于POST请求,请求参数全部放在Body里,不要把参数放在Query里。 对于POST请求,请求参数全部放在Body里,不要把参数放在Query里。 对于POST请求,请求参数全部放在Body里,不要把参数放在Query里。
系统参数
请求所有接口均需要携带的参数,用于开发者身份认证和数据安全校验。系统参数包括

业务参数
业务参数是指调用具体某个接口时需要的和业务相关的参数,通常不同的接口有不同的业务参数。
业务参数统一使用JSON字符串表示,并将业务此JSON字符串作为指定参数"biz"的值。
假如一个接口需要两个参数:id和name,传参的方式可参考
appAuthToken:V2-5fc970ded1f0f933594ee68bf8da6eaa06a44a340e5b23832f944a716379f006aeddd4598541271ece7f91d185b64080
charset:UTF-8
timestamp:1618975600
version:2
developerId:1031123
businessId:18
sign:96e2cedadeb7401acf48e46672673f6512cc460e
biz:{"id":1,"name":"hello"}
Postman请求示例

响应格式
所有接口响应Content-Type均为:application/json,即响应均为json格式。接口响应有统一的格式
成功格式
{ "code": "OP_SUCCESS", "data": {} }
code为OP_SUCCESS表示请求成功,其他情况都为请求失败。
接口请求成功时data字段表示接口响应的业务数据。
失败格式
{ "code": "xx_xxx", "msg": "缺少系统参数", "traceId": 5362971231773781183 }
code定义参见4,错误码定义。msg字段为错误描述信息。
traceId为本次调用产生的流水号,接口调用异常,可以提供此流水号用于排查问题。
错误码定义
见平台返回状态码
业务ID列表
1
团购
2
外卖
3
闪惠
4
会员
7
预定
13
评价
16
外卖非接单
17
茶饮版
18
餐饮系统-智能版
19
配送-合同版
20
餐饮系统
21
渠道分销
22
到店广告
23
美团外卖增值平台
24
AI服务
26
小黄卡
27
快驴
28
AI服务-智能对话机器人
29
语音原子能力
31
配送-零售版
33
顾客点餐自助核销
34
门票整合营销
36
智能外呼机器人
常见错误FAQ
问题
请求提醒缺少系统参数。
回复
检查系统参数是否正确。
对于Post请求所有参数都放在Body里!不可以放在Query里。
签名规则
功能说明
调用每个接口都需要携带签名,服务端会根据请求参数,对签名进行校验,签名不合法的请求将会被拒绝
服务商侧&开放平台侧校验每次请求参数的数据完整性
服务商侧&开放平台侧验证请求者的身份
签名计算
开放平台提供了 Java,Php,Node JS, C#, Python语言的签名代码。请直接copy代码调用获取Sign
获取sign代码
String signKey = "123";//你的signkey,注意替换 Map<String,String> param = new HashMap(); //下方的参数为每次调用api需要的参数 param.put("timestamp","1577771730"); param.put("developerId","123123"); param.put("biz","{\"ticket\":\"10346762252\",\"number\":2}"); param.put("version","2"); param.put("charset","UTF-8"); param.put("appAuthToken","74d301d082f366b730043dc0a55b403ac0f7ac9e63c93cedd3ab9759a06a1bbf3fc953de835c"); String sign=SignUtil.getSign(signKey,param);//获得sign ceb760af62431e20fd2e4aa1bbf21d2c17012dc0
请求示例
POST /order/queryById HTTP/1.1 Host: api-open-cater.meituan.com Content-Type: application/x-www-form-urlencoded;charset=utf-8 appAuthToken=74d301d082f366b730043dc0a55b403ac0f7ac9e63c93cedd3ab9759a06a1bbf3fc953de835c& charset=UTF-8& timestamp=1577771730& sign=ceb760af62431e20fd2e4aa1bbf21d2c17012dc0 & //传入sign developerId=123123& biz={"ticket":"10346762252","number":2}& version=2
常见语言代码示例
java
public class SignUtil { private SignUtil() {} public static String getSign(String signKey, Map<String, String> params) { try { String sortedStr = getSortedParamStr(params); String paraStr = signKey + sortedStr; return createSign(paraStr); } catch (UnsupportedEncodingException e) { log.warn("getSign UnsupportedEncodingException ", e); } return StringUtils.EMPTY; } /** * 构造自然排序请求参数 * * @param params 请求 * @return 字符串 */ private static String getSortedParamStr(Map<String, String> params) throws UnsupportedEncodingException { Set<String> sortedParams = new TreeSet<>(params.keySet()); StringBuilder strB = new StringBuilder(); // 排除sign和空值参数 for (String key : sortedParams) { if ("sign".equalsIgnoreCase(key)) { continue; } String value = params.get(key); if (StringUtils.isNotEmpty(value)) { strB.append(key).append(value); } } return strB.toString(); } /** * 生成新sign * * @param str 字符串 * @return String */ private static String createSign(String str) { if (str == null || str.length() == 0) { return null; } char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char[] buf = new char[j * 2]; int k = 0; int i = 0; while (i < j) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; i++; } return new String(buf); } catch (Exception e) { log.warn("create sign was failed", e); return null; } } }
php
<?php function get_sign($sign_key, $data) { if ($data == null) { return null; } ksort($data); $result_str = ""; foreach ($data as $key => $val) { if ( $key != null && $key != "" && $key != "sign" ) { $result_str = $result_str . $key . $val; } } $result_str = $sign_key . $result_str; $ret = bin2hex(sha1($result_str, true)); return $ret; } $data = array("biz" => "{'req':100}", "charset" => "utf-8", "appAuthToken" => "123", "timestamp" => "1577771730", "version" => "1"); echo get_sign("123456", $data); ?>
C#
class SignService { static void Main(string[] args) { Hashtable ht = new Hashtable(); ht.Add("biz","{'req':100}"); ht.Add("charset","utf-8"); ht.Add("appAuthToken","123"); ht.Add("timestamp","1606820155"); ht.Add("version","1"); SignService signService = new SignService();
python
def get_sign(sign_key, data): list_data = list(data.keys()) string_sign = sign_key sort_list = sorted(list_data) for item in sort_list: if item == 'sign': continue string_sign += str(item) string_sign += str(data[item]) sign = hashlib.sha1(string_sign.encode('utf-8')).hexdigest()
node.js
const crypto = require('crypto') class SignUtil { constructor() { this.shasum = crypto.createHash('sha1'); } sortedParamAsStr(map) { if (map == null || !(map instanceof Object)) { return ""; } let sortedMap = new Map([...map.entries()].sort()); let resultStr = ""; sortedMap.forEach(function(value, key, map) { if (key != "sign" && value != null && value != "") { resultStr += key; resultStr += value; } }); return resultStr; } createSign(map, signKey) { let sortedParamStr = this.sortedParamAsStr(map); let paraStr = signKey + sortedParamStr; this.shasum.update(paraStr); return this.shasum.digest('hex'); } } module.exports = new SignUtil();
其他编程语言
签名算法原理
以执行验券接口 /xhcard/singleverify/verifyTicket为例,假如开发着的signKey是 2d11ab8e,请求参数:
appAuthToken:74d301d082f366b730043dc0a55b403ac0f7ac9e63c93cedd3ab9759a06a1bbf3fc953de835c
charset:UTF-8
timestamp:1577771730
developerId:100567
biz: {"ticket":"10346762252","number":2}
version: 2
步骤
将请求参数中除 sign 外的多个键值对,根据键按照字典序排序,并按照 "key1value1key2value2..." 的格式拼成一个字符串。请求参数拼接后的结果为:appAuthToken74d301d082f366b730043dc0a55b403ac0f7ac9e63c93cedd3ab9759a06a1bbf3fc953de835cbiz{"ticket":"10346762252","number":2}charsetUTF-8developerId100567timestamp1577771730version2
将 signKey 拼接在 1 中排序后的字符串前面得到待签名字符串: 2d11ab8eappAuthToken74d301d082f366b730043dc0a55b403ac0f7ac9e63c93cedd3ab9759a06a1bbf3fc953de835cbiz{"ticket":"10346762252","number":2}charsetUTF-8developerId100567timestamp1577771730version2
使用 sha1 算法加密待加密字符串并转为小写即为 sign: e25c4832b4cf72073d9d6a41e0b2d29c24d236e9
将 sign 添加到请求参数中
注意
对于biz这类复杂参数,不论value内部是否包含多个字段,均把value看作一个完整字符串来处理,不需要对内部字段进行拆分和排序。
请求参数中有中文时,中文需要经过 url 编码,但计算签名时不需要
计算 sha1 签名时,需要以 utf-8 的编码转换 byte 流,否则可能导致含中文参数的签名计算不正确
公共参数请求

公共响应参数

平台返回状态码
返回状态码(调用接口时,未使用biz传参)
平台返回状态码

业务返回状态码
 
平台返回状态码(调用接口时,使用biz传参)

鉴权失败处理
如果响应code为 OP_UNIAUTH_FAILED,说明请求鉴权失败,鉴权失败的原因在响应结果的data字段中,请检查响应结果描述

授权说明
授权接入
子主题
接入流程完成后,您将获得appAuthToken,每个门店都有一个唯一的appAuthToken用于请求门店数据。
见必接通用接口-门店映射
外卖/团购门店映射
外卖/团购门店映射UISDK简介
开放平台提供的UISDK本质为若干Web页面,提供门店绑定、验券、闪惠订单确认、券查询等功能。 在使用UISDK各功能页面前,需要通过UISDK提供的门店绑定环节获取token,生成的token由第三方保存,作为使用UISDK的凭证。
使用环境
要第三方能在其客户端以Webview、Iframe等方式打开外网Web页面就可使用UI SDK, UI SDK的base URL为:https://open-erp.meituan.com

接入参数
UI SDK会分析URL中的query string,如传入的参数不符合要求会报传入参数错误。
门店映射接入参数
详见:必接通用接口-门店映射-UISDK接入
功能页面传入参数
门店绑定成功后开放平台会生成一个认证token,并通过门店映射回调地址的方式将token传递给服务商
使用功能页,通过query string 传入的参数如下: 
消息通知
Android
如使用Android系统自带的webview打开web page,可调用addJavascriptInterface方法向webview打开的页面中注入Java方法。 约定的方法名为:UISDKMessageHandler,UISDK调用方式:window.Android.UISDKMessageHandler(event,value);

IOS
IOS8以上版本使用WKWebView展示web page,可使用addScriptMessageHandler方法向web page暴露方法。 将名为UISDKMessageHandler的方法加入到messageHandlers中,UI SDK端即可使用:window.webkit.messageHandlers.UISDKMessageHandler.postMessage(event:value);向服务商端推送消息,通知方法参数说明跟Android方法相同。
iframe
如果以iframe的方式引入UI SDK,UI SDK则以postMessage的方式发送消息,postMessage({event,value}, origin);
最佳展示效果
为获得最佳展示效果,checkcoupon页和checkpigeon页建议展示尺寸为:width:424px,height:476px;
说明
一店一appAuthToken,只有当该店所有业务都解绑,再次绑定任何一个业务时,该店appAuthToken才会更新; 如果该店有多个业务,但只解绑其中部分业务,当再次绑定时,该店appAuthToken保持不变
第三方业务授权
概述
授权流程
快速接入
查询授权信息
解除授权
业务ID(businessId)列表
授权错误码
回调接口
付费接口列表
测试账号使用指导
对接前说明
必接通用接口
工具型服务
商家账号通