应用程序接口(API:application programming interface)是一组定义、程序及协议的集合,通过 API 接口实现网络多节点间的相互通信.
本API文档对两种机制做描述:
API:
主动发起:
资源提供方,提供接口供接入方调用资源
ShopEx开放平台(ShopEx Open Platform,简称SOP)ShopEx服务的开放平台,基于基础服务、数据和流程。提供互连互通服务。通过平台,连接一切。 ShopEx是面向电子商务企业的数据和服务的综合性平台,为互联网电子商务企业应用提供应用接入、应用分销、整合方案、服务接入等一整套服务的开放性平台,通过接入ShopEx开放平台,电子商务企业可轻松与第三方开放应用数据整合、成本控制、资源共享等完整的解决方案。 其主要内容包括:以OpenAPI形式开放的ShopEx电子商务基础服务、ShopEx自有的开放式应用平台、对第三方应用平台的开放式基础支持。
接入方为资源使用方
接入方发起(以下三种联通方式):
Ecstore发起:
前提:需要在ecstore后台绑定接入方,同时要再 app/${app_id}(默认b2c)/apiv_mapper.xml中绑定双方API版本映射关系
在Ecstore2.0中针对API设计了版本机制。目前最新的API版本是2.0,之前的API版本设为1.0。接入方接入Ecstore时需要指定API版本号,若不指定,默认使用最新版本。
app/${app_id}/apiv_mapper.xml 版本关系映射表(接入方的API版本和Ecstore API版本关系对应表) app/b2c/lib/apiv/ API机制主目录 exchange/ 路由器目录 extends/ 基类目录 interface/ 类接口目录 apis/ 新增API实现目录
matrix对接API需要接入方通过ShopEx开放平台联通
参数 | 类型 | 是否必须 | 描述 |
---|---|---|---|
method | String | Y | API接口名称 |
v | String | Y | 矩阵API协议版本,可选值:1.0 |
timestamp | String | Y | 时间戳,格式为yyyy-MM-dd hh:mm:ss,例如:2008-01-25 20:23:30。ShopEx API服务端允许客户端请求时间误差为10分钟 |
format | String | Y | 可选,指定响应格式。默认JSON ,目前支持格式为JSON |
sign | String | Y | 对调用API时所有输入参数(包括应用级参数)进行签名结果 |
certi_id | Number | N | 分配给应用的证书ID |
from_node_id | String | N | API调用的来源节点ID,对于第三方开发者来说,此id即为申请应用颁发的APP KEY |
from_api_v | String | N | 请求方版本号 |
to_node_id | String | N | API调用的目的节点ID(除了基础服务类api,其他接口必传此参数) |
to_api_v | String | N | 接收方版本号 |
callback_url | String | N | 响应回调地址,带http路径的标准URL。(调用异步接口此参数必填) |
{ "res":"", "rsp":"succ", "data": { "tid":"000001" } }
参数名称 | 描述 |
---|---|
Rsp | 请求是否正确 , succ 为成功 , fail 为失败 |
Res | 返回的消息字符串.请求正确时为空,失败时为错误消息 |
Data | 返回请求的数据结果集 |
<?php
function get_sign($params,$token){
return strtoupper(md5(strtoupper(md5(assemble($params))).$token));
}
function assemble($params)
{
if(!is_array($params)) return null;
ksort($params,SORT_STRING);
$sign = '';
foreach($params AS $key=>$val){
$sign .= $key . (is_array($val) ? assemble($val) : $val);
}
return $sign;
}
?>
token的来源:
token是在域名绑定shopex_id时由商派中心生成的一串验签码,此验签码存放在config/certi.php文件中,和证书一起。
调用方式:
base_certificate::token();
<service id="api-response_1.0_b2c.order"> <class>b2c_api_ocs_1_0_order</class> </service> <service id="api-response_2.0_b2c.order"> <class>b2c_apiv_apis_20_order</class> </service>
<service id="api-response_2.0_b2c.order"> <class>b2c_apiv_apis_response20_order</class> </service>
第一个参数为接入方请求的用户级参数,第二个参数是API控制对象
<?php
public function search( $params, &$service )
{
//校验参数
if( !( $start_time = $params['start_time'] ) )
$service->send_user_error('7001', '开始时间不能为空!');
if( ($start_time = strtotime(trim($start_time))) === false || $start_time == -1 )
$service->send_user_error('7002', '开始时间不合法!');
if( !( $end_time = $params['end_time'] ) )
$service->send_user_error('7003', '结束时间不能为空!');
if( ($end_time = strtotime(trim($end_time))) === false || $end_time == -1 )
$service->send_user_error('7004', '结束时间不合法!');
$obj_orders = &app::get('b2c')->model('orders');
……
}
直联API同Matrix机制相同,唯一不同在于跳过了ShopEx开放平台。接入方直接发送请求到Ecstore。
参数 | 类型 | 是否必须 | 描述 |
---|---|---|---|
direct | string | Y | 设置为true |
method | String | Y | 指定调用api的service和mehtod. 例如:method设为b2c.payment.create 那么service:api.b2c.payment, method:create |
sign | String | Y | 签名,参看签名算法 |
date | String | Y | 时间戳,格式为yyyy-MM-dd hh:mm:ss,例如:2008-01-25 20:23:30 |
format | String | N | 可选,指定响应格式。默认json |
{ "res":"", "rsp":"succ", "data": { "tid":"000001" } }
参数名称 | 描述 |
---|---|
Rsp | 请求是否正确 , succ 为成功 , fail 为失败 |
Res | 返回的消息字符串.请求正确时为空,失败时为错误消息 |
Data | 返回请求的数据结果集 |
Ecstore资源发生变更时会对外主动发起请求。当前已有的发起点包括:
前提:需要在ecstore后台绑定接入方,同时要再 app/${app_id}(默认b2c)/apiv_mapper.xml中绑定双方API版本映射关系
<service id="api-request_2.0_ecos.ome_ordercreate"> <class>b2c_apiv_apis_request20_ome_order</class> </service>
<service id="api-request_2.0_ecos.ome_ordercreate"> <class>b2c_apiv_apis_request20_ome_order</class> </service>
<?php
class b2c_apiv_apis_20_ome_order extends b2c_apiv_extends_request
{
var $method = 'store.trade.add';
var $callback = array();
var $title = '订单新增';
var $timeout = 1;
var $async = true;
public function get_params($sdf)
{
$order_id = $sdf['order_id'];
$order_detail = kernel::single('b2c_order_full')->get($order_id);
return $order_detail;
}
}
<service id="api-request_out_ordercreate"> <class>b2c_apiv_apis_out_order</class> </service>
<service id="api-request_out_ordercreate"> <class>b2c_apiv_apis_out_order</class> </service>
<?php
class b2c_apiv_apis_out_order implements b2c_apiv_interface_requestout
{
public function init($sdf)
{
$url = 'https://www.baidu.com';
$core_http = kernel::single('base_httpclient');
$response = $core_http->set_timeout(10)->post($url,$sdf,array(
'Content-Encoding' => 'gzip',
));
if($response===HTTP_TIME_OUT){
$headers = $core_http->responseHeader;
kernel::log('Request timeout, process-id is '.$headers['process-id']);
return false;
}else{
}
}
}
开放API, 是很轻量级的API. 系统不支持签名验证, 也没有做异常处理. 因此可以按照实际业务需要定制开发签名验证和异常处理.
http://{$mydomain}/index.php/openapi/{$openapi_key}/{$openapi_method}/{$key_1}/{$value_1}/{$key_2}/{$value_2}
如果服务器设置过rewrite
http://{$mydomain}/openapi/{$openapi_key}/{$openapi_method}/{$key_1}/{$value_1}/{$key_2}/{$value_2}
$myadmin: 域名 $openapi_key: open api的唯一标识 $openapi_method: 调用方法 $key_1: 参数1 $value_1: 参数1的值 $key_2: 参数2 $value_2: 参数2的值
通过POST/GET进行请求
小技巧: 1. 在系统中可以直接用工具类base_httpclient 来实现. 2. openapi的调用api可以用kernel::openapi_url()生成. 例如: $http = new base_httpclient; $url = kernel::openapi_url('openapi.queue','worker',array('task_id'=>$task_id)); $http->post($url,$_POST);
无
用户传参主要有以下两种方式
openapi是通过service机制进行注册的, 默认的service box以"openapi."作为前缀, 来看一下我们系统目前所提供的所有openapi.
bryant@forsky %> ./cmd dev:show services | grep -i "^openapi" openapi.rpc_callback base_rpc_service openapi.check base_rpc_check openapi.queue base_service_queue openapi.pam_callback pam_callback openapi.ectools_payment ectools_payment_api openapi.b2c.callback.shoprelation b2c_api_callback_shoprelation
左侧: $openapi_key open api的唯一标识 右侧: $openapi_class_name 注册在$openapi_key上的相应openapi处理类
补充知识: 如果需要使用 ./cmd dev:show services, 需要预装dev app
api类的存放位置: app/{$app_id}/lib/openapi/{$openapi_class_name}.php
系统实例化api类的时候会将对应的app对象({$app_id})作为参数传进来, 也可以通过调用app:get($app_id)来获取需要的app对象:
<?php
class {$openapi_class_name}
{
/**
* app object
*/
public $app;
/**
* 构造方法
* @param object app
*/
public function __construct($app)
{
$this->app = app::get('ectools');
$this->app_b2c = $app;
}
api类方法的写法:
第一个参数: 将url传过来的参数转化成数组.array({$key1} => {$value1},{$key2} => {$vaule2),...)
返回值: 可通过echo/print_r等直接输出. 也可以和通信方约定传输格式及错误处理
<?php
class {$openapi_class_name}
{
/**
* app object
*/
public $app;
/**
* 构造方法
* @param object app
*/
public function __construct($app)
{
$this->app = app::get('ectools');
$this->app_b2c = $app;
}
public function create($params)
{
...
echo 'succ';
}
}
签名验证主要目的是为了保证多节点通信的多方之间的信任关系
建议方法:
AC算法:
<?php function get_sign($params,$token){ return strtoupper(md5(strtoupper(md5(assemble($params))).$token)); } function assemble($params) { if(!is_array($params)) return null; ksort($params,SORT_STRING); $sign = ''; foreach($params AS $key=>$val){ $sign .= $key . (is_array($val) ? assemble($val) : $val); } return $sign; } ?>
例如:json 失败:{"rsp":"fail","res":"4003","data":"sign error"} 成功:{"rsp":"succ","res":"","data":"....."} 请求失败的时候 res 会返回相关错误信息 请求成功的时候 res 一般为空 data项返回相关数据信息(详情要看相关api接口文档)
openapi基类: myapp_openapi
<?php class myapp_openapi { public function __construct() { set_error_handler(array('myapp_openapi', 'error_handler')); } static function error_handler($errno, $errstr, $errfile, $errline ){ switch ($errno) { case E_ERROR: case E_USER_ERROR: throw new ErrorException($errstr, 0, $errno, $errfile, $errline); break; case E_STRICT: case E_USER_WARNING: case E_USER_NOTICE: default: //do nothing break; } } function log($message) { error_log($message, 0); } function verify($params) { //验证是否合法 } //返回成功 function send_user_succ($result){ $result_json = array( 'rsp'=>'succ', 'data'=>$result, } echo json_encode($result_json); exit; } //返回失败 function send_user_error($code, $data){ $res = array( 'rsp' => 'fail', 'res' => $code, 'data' => $data, ); echo json_encode($res); exit; } }业务api: myapp_openapi_login
<?php class myapp_openapi_login extends myapp_openapi { public function __construct($app){ $this->app = $app; parent::__construct(); } /登陆 function login($params) { if (parent::verify($parames)) { //如果成功则记录日志 $this->log("join user"); }else{ //如果失败则返回错误信息 $this->send_user_error('4003', 'user login fail'); } } }