顺丰开放平台API PHP SDK demo
in PHP with 0 comment

顺丰开放平台API PHP SDK demo

in PHP with 0 comment

顺丰开放平台Api PHP SDK demo

引用

顺丰开放平台
接口接入规范 V1.1.6

对接前期工作

接入注意事项

Demo

SDK Demo

<?php
/**
 * 顺丰 api 接口使用
 * Date: 2017/10/19
 * Time: 9:36
 * @author zlh <root@rooot.me>
 */

class SfApi {

    /**
     * @var string
     */
    protected $app_id;

    /**
     * @var string
     */
    protected $app_key;

    /**
     * @var string
     */
    public $access_token;

    /**
     * @var string
     */
    public $refresh_token;

    /**
     * @var string
     */
    private $uri;

    /**
     * SfApi constructor.
     * @param string  $app_id
     * @param string $app_key
     * @param bool $online
     * @return void
     */
    public function __construct ($app_id, $app_key, $online = false) {
        $this->app_id = $app_id;
        $this->app_key = $app_key;
        $this->url = $online ? 'https://open-prod.sf-express.com/' : 'https://open-sbox.sf-express.com/';
    }

    /**
     * 快速创建订单
     * @param array $req_body
     * @return bool|string
     */
    public function order ($req_body) {
        $req_body = array(
            'orderId' => $req_body['orderId'],
            'expressType' => $req_body['expressType'],
            'payMethod' => $req_body['payMethod'],
            'custId' => $req_body['custId'],
            'payArea' => isset($req_body['payArea']) ? $req_body['payArea'] : 'SFCM10008035754399',
            //'remark' => $req_body['remark'],
            'consigneeInfo' => array(
                'company' => isset($req_body['consigneeInfo']['company']) ? $req_body['consigneeInfo']['company'] : '个人',
                'contact' => $req_body['consigneeInfo']['contact'],
                'tel' => $req_body['consigneeInfo']['tel'],
                'province' => $req_body['consigneeInfo']['province'],
                'city' => $req_body['consigneeInfo']['city'],
                'county' => $req_body['consigneeInfo']['county'],
                'address' => $req_body['consigneeInfo']['address'],
                'mobile' => $req_body['consigneeInfo']['mobile'],
            ),
            'cargoInfo' => array(
                'cargo' => $req_body['cargoInfo']['cargo'],
            ),
            'addedServices' => $req_body['addedServices'],
        );

        $res = $this->send('order', $req_body);
        $res_tmp = json_decode($res, true);

        if(empty($res_tmp['head']['code'])) {
            return '系统错误!';
        }elseif(substr($res_tmp['head']['code'], -3) != '200') {
            return $res_tmp['head']['message'];
        }else{
            return true;
        }
    }

    /**
     * 订单结果查询
     * @param string $order_id
     * @return json
     */
    public function orderQuery ($order_id) {
        $req_body = array(
            'orderId' => $order_id
        );
        return $this->send('order/query', $req_body);
    }

    /**
     * 根据 运单号/订单号 查询物流路由
     * @param array $req_body
     * @return json
     */
    public function routeQuery ($req_body) {
        $req_body = array(
            'trackingType' => $req_body['trackingType'],
            'trackingNumber' => $req_body['trackingNumber'],
            'methodType' => $req_body['methodType'],
        );
        return $this->send('route/query', $req_body);
    }

    /**
     * 电子运单图片下载
     * @param string $orderId
     * @param bool $isLogo
     * @return json (image参数为BASE64编码的字符串)
     */
    public function wayBillImage ($orderId, $isLogo = false) {
        $req_body = array(
            'orderId' => $orderId,
            'isLogo' => $isLogo ? 1 : 0
        );
        return $this->send('waybill/image', $req_body);
    }

    /**
     * 获取顺丰授权
     * @return json
     */
    public function getToken () {
        return $this->send('security/access_token');
    }

    /**
     * 刷新授权令牌
     * @param string $access_token
     * @param string $refresh_token
     * @return json
     */
    public function refreshToken ($access_token, $refresh_token) {
        $this->access_token = $access_token;
        $this->refresh_token = $refresh_token;
        return $this->send('security/refresh_token');
    }

    /**
     * 忘记 access_token 查询
     * @return json
     */
    public function queryToken () {
        return $this->send('security/access_token/query');
    }

    /**
     * 根据请求的接口创建相应的 Uri
     * @param string $resource 要请求的resource(前后不带'/')
     * @return string
     */
    protected function buildUri ($resource) {
        $query = '/sf_appid/' . $this->app_id . '/sf_appkey/' . $this->app_key;

        if ($resource == 'security/refresh_token') {
            $query = "/access_token/$this->access_token/refresh_token/$this->refresh_token" . $query;
        }elseif (substr($resource, 0, 8) != 'security') {
            $query = '/access_token/' . $this->access_token . $query;
        }

        $uri = $this->url . $this->transType()[$resource]['type'] . '/v1.0/' . $resource . $query;

        return $uri;
    }

    /**
     * 向顺丰接口发送请求
     * SSL POST JSON
     * @param string $resource 要请求的resource(前后不带'/')
     * @param array $body 请求报文(参数)
     * @return json
     */
    protected function send ($resource, $body = array()) {

        $this->uri = $this->buildUri($resource);

        $req_data = array(
            'head' => array(
                'transType' => $this->transType()[$resource]['transType'],
                'transMessageId' => date('Ymd') . substr_replace(time(), mt_rand(10,99),0,2),
            )
        );

        if(!empty($body)) {
            $req_data['body'] = $body;
        }

        $header = array(
            "Content-Type: application/json",
        );

        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, $this->uri);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);  // 从证书中检查SSL加密算法是否存在
        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($req_data));

        $response = curl_exec($ch);

        curl_close($ch);

        return $response;
    }

    /**
     * 顺丰开放平台各接口对应的资源类型
     * @return array
     */
    private function transType () {
        return array(
            'order' => array('transType' => 200, 'type' => 'rest'),
            'order/query' => array('transType' => 203, 'type' => 'rest'),
            'filter' => array('transType' => 204, 'type' => 'rest'),
            'route/query' => array('transType' => 501, 'type' => 'rest'),
            'route/inc/query' => array('transType' => 504, 'type' => 'rest'),
            'waybill/image' => array('transType' => 205, 'type' => 'rest'),
            'product/basic/query' => array('transType' => 250, 'type' => 'rest'),
            'product/additional/query' => array('transType' => 251, 'type' => 'rest'),
            'security/access_token/query' => array('transType' => 300, 'type' => 'public'),
            'security/access_token' => array('transType' => 301, 'type' => 'public'),
            'security/refresh_token' => array('transType' => 302, 'type' => 'public'),
        );
        /**
            快速下单 /order/ 200
            订单查询 /order/query/ 203
            订单筛选 /filter/ 204
            路由查询 /route/query/ 501
            路由增量查询 /route/inc/query/ 504
            电子运单图片下载 /waybill/image/ 205
            基础服务查询 /product/basic/query/ 250
            附加服务查询 /product/additional/query/ 251
            申请访问令牌 /security/access_token/ 301
            查询访问令牌 /security/access_token/query/ 300
            刷新访问令牌 /security/refresh_token/ 302
         */
    }

}

使用

  1. 引入该类文件
  2. 并实例化,参数分别为 app_id, app_secret, 是否用于生产环节 (true|false),默认为 false
  3. 检查 access_token 并设置。
  4. 调用对应方法即可,详细使用请参考方法注释和顺丰接口文档。

例子

// 实例化对象并传入参数
$sf = new SfApi('your app_id', 'your app_secret', false);

// 获取 access_token
$token_res = $sf->getToken();
var_dump($token_res);

// 为对象设置 access_token 属性
$sf->access_token = json_decode($token_res, true)['body']['accessToken'];

// 下单
$req_body = array(
    'orderId' => 'SF201710091507536283',
    'expressType' => 1,
    'payMethod' => 3,
    'custId' => 'your custId',
    'payArea' => '010EU',
    'remark' => '备注',
    'consigneeInfo' => array(
        'company' => '个人',
        'contact' => '测试对象',
        'tel' => '17600000000',
        'province' => '北京市',
        'city' => '北京市',
        'county' => '朝阳区',
        'address' => '十里河',
        'mobile' => '17600000000',
    ),
    'cargoInfo' => array(
        'cargo' => '产品名',
    ),
    // 代收货款需填,标准快递则不需要如下参数
    'addedServices' => array(
        array(
            'name' => 'COD',
            'value' => '698',
        ),
        array(
            'name' => 'CUSTID',
            'value' => 'your custId'
        )
    )
);
$order_res= $sf->order($req_body);
var_dump($order_res);

// 获取运单图片
$image_res = $sf->wayBillImage('SF201710091507536283');
$image_res = json_decode($image_res, true);
file_put_contents('./SF201710091507536283.jpg', base64_decode($image_res['body']['images'][0]));

echo '<img src="./SF201710091507536283.jpg" />';

运单图片

示例

sf2

图中马赛克为手打

运单图片处理

处理图片使用 GDimagemagick 等处理图片的库。
一般来说需要隐藏客户的收件信息,下附上图片处理相关函数。

/** 图片局部打马赛克 
* @param  String  $source 原图 
* @param  Stirng  $dest   生成的图片 
* @param  int     $x1     起点横坐标 
* @param  int     $y1     起点纵坐标 
* @param  int     $x2     终点横坐标 
* @param  int     $y2     终点纵坐标 
* @param  int     $deep   深度,数字越大越模糊 
* @param  bool    $fill   是否只填充白色
* @return boolean 
*/  
function imageMosaics($source, $dest, $x1, $y1, $x2, $y2, $deep, $fill = false){  
  
    // 判断原图是否存在  
    if(!file_exists($source)){  
        return false;  
    }  
  
    // 获取原图信息  
    list($owidth, $oheight, $otype) = getimagesize($source);  
  
    // 判断区域是否超出图片  
    if($x1>$owidth || $x1<0 || $x2>$owidth || $x2<0 || $y1>$oheight || $y1<0 || $y2>$oheight || $y2<0){  
        return false;  
    }  
  
    switch($otype){  
        case 1: $source_img = imagecreatefromgif($source); break;  
        case 2: $source_img = imagecreatefromjpeg($source); break;  
        case 3: $source_img = imagecreatefrompng($source); break;  
        default:  
            return false;  
    }
  
    // 打马赛克  
    if($fill === false) {
        for($x=$x1; $x<$x2; $x=$x+$deep){  
            for($y=$y1; $y<$y2; $y=$y+$deep){  
                $color = imagecolorat($source_img, $x+round($deep/2), $y+round($deep/2));
                imagefilledrectangle($source_img, $x, $y, $x+$deep, $y+$deep, $color);  
            }  
        } 
    }else{
        $im = imagecreatetruecolor(200, 200);
        $fill = imagecolorallocate($im, 255, 255, 255);
        imagefilledrectangle($source_img, $x1, $y1, $x2, $y2, $fill); 
    }
  
    // 生成图片  
    switch($otype){  
        case 1: imagegif($source_img, $dest); break;  
        case 2: imagejpeg($source_img, $dest); break;  
        case 3: imagepng($source_img, $dest); break;  
    }  
  
    return is_file($dest)? true : false;  
  
}
// 使用
$source = $dest = './201710244108826825.jpg';  
// 5,5  1125,100 区域填充白色(应对带有顺丰logo的打印纸)
$flag = imageMosaics($source, $dest, 5, 5, 1125, 100, 4, true);  
// (120)215, 250   810,400 区域马赛克
$flag = imageMosaics($source, $dest, 215, 250, 810, 400, 7);  
// (110)210,1207  678,1314 区域马赛克
$flag = imageMosaics($source, $dest, 210, 1207, 678, 1314, 7);  
echo '<img width="400px" src="'.$source.'">';
  

该函数为以前网络上找到并改造的,未找到原作者。如有侵权请联系我删除。

处理后示例

sf1

图中 logo 处和收件人处为函数生成的马赛克、填充白色,其余区域为手打马赛克。

其他

代码写的比较仓促,不过一般的与顺丰对接也就这些基本功能,还有一些接口本文没有用到的,可以参考上面 SDK 中对应函数的注释和接口文档。

注:转载请注明出处。

Comments are closed.