Cdiscount API产品上架 SubmitProductPackage

本文详细介绍了使用Cdiscount API进行产品上架的过程,包括获取Token、生成ZIP文件、提交产品信息及处理常见错误等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Cdiscount API产品上架

开发前仔细阅读此文档!!!
API 链接地址 https://dev.cdiscount.com/marketplace/?page_id=238

https://portal-marketplaceapi.cdiscount.com/get-started

 

最新API接口地址:https://marketplace.cdiscount.com/zh/api/

 

https://blog.youkuaiyun.com/ts3211/article/details/106186472

https://download.youkuaiyun.com/download/highfiresun/7344067?utm_medium=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-download-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

cdiscount 是一个法国小众电商平台 本公司主做跨境电商,所以需要一个ERP上架功能…

这个上架总的来说要分两步 1:先上传产品基本信息 2:再上传产品库存、价格、物流等其他信息…

step1:获取token

在这里插入图片描述

 

public function getToken($login, $password)

    {

        //相关请求

        $req_url = "https://sts.cdiscount.com/users/httpIssue.svc/?realm=https://wsvc.cdiscount.com/MarketplaceAPIService.svc";

        $api_check = base64_encode($login.":".$password);

        $headers = array("Authorization: Basic ".$api_check,"Content-Type: application/json; charset=utf-8");

        $fetch_token = $this->sendCurlGetRequest($req_url,$headers);

        $token_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

        $token_xml.= $fetch_token;

        $token_output = json_decode(json_encode(simplexml_load_string($token_xml)),true);

        if($token_output){

            return $token_output[0];

        }else{

            return false;

        }

    }

    

//模拟get请求

public function sendCurlGetRequest($url,$headers){

$ch = curl_init($url);

curl_setopt($ch,CURLOPT_URL,$url);

if($headers){

curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);

}

curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);

// https请求 不验证证书和hosts

curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);

curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);

$output = curl_exec($ch);

curl_close($ch);

return $output;

}

 

 

 

step2:生成 zip 文件 ( 经验太少 第一次遇到 传数据要包成zip文件做传输的 ) 并上传

Please find hereafter a sample of the product package :APIMPCdiscount_Sample_Products

 

zip 文件详解

https://dev.cdiscount.com/marketplace/?pagename=productintegration

 

整个zip文件的目录结构是这样的

在这里插入图片描述

 

主要文件 Products.xml (官方详情在这)

https://dev.cdiscount.com/marketplace/?pagename=products-xml

 

生成文件踩过的坑…

 

Product element 中空值字段不要写进文件

ProductImage Element 图片地址 不可写国内地址 接口响应图片超过5秒就会报错 所以要把图片放到外网

zip文件生成目录层不可多 不要把这两个文件夹和文件放入另一个文件夹中!!! ( 这个亏吃大了,总是报错… )

在这里插入图片描述

 

   // 创建临时文件 准备打包压缩 $xml:拼接成的xml文件内容

       $this->createCdiscountDir('Products', $xml, $in_id);

        // 创建ZIP文件

       $zip = new ZipArchive();

       try {

           $zip_dir = "/cidscount/".date('Y-m-d');

           !is_dir($zip_dir) && @mkdir($zip_dir, 0777, true);

           $zip_name = "/cidscount/".date('Y-m-d')."/cidscount".$in_id.".zip";

           $tmp_dir = array('_rels', 'Content', '[Content_Types].xml');

           $this->createZip($tmp_dir, $zip_name);

 

           $this->load->library('CdiscountApi');

           $zip_name = $web_out.':80/'.$zip_name;

           // 调用接口

           $res = $this->SubmitProductPackage($zip_name);

           // 删除临时文件

           foreach ($tmp_dir as $tdk => $tdv) {

               $this->delDirAndFile($tdv);

           }

           die;

       } catch (\Exception $exception) {

           local_log("============= CdiscountSubmit ERROR S ===========");

           local_log($exception->getMessage());

           local_log("============= CdiscountSubmit ERROR E ===========");

           return false;

       }

       

 

 

 

 

 

// 创建cdiscount目录

       public function createCdiscountDir($type='Products', $xml, $in_id)

       {

           $dir = "Content/";

           $path_arr = array();

           !is_dir($dir) && @mkdir($dir, 0777, true);

   

           // .rels file Structure

           $relsPath = "_rels";

           !is_dir($relsPath) && @mkdir($relsPath, 0777, true);

           $rels_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>

                       <Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">

                       <Relationship Type=\"http://www.cdiscount.com/uri/document\" Target=\"/Content/$type.xml\" Id=\"cidscountRelsFm$in_id\" />

                       </Relationships>";

           file_put_contents($relsPath.'/.rels', $rels_xml);

   

           // [Content_Types].xml

           $ctype_xml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>

                         <Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\">

                         <Default Extension=\"xml\" ContentType=\"text/xml\" />

                         <Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\" />

                         </Types>";

           file_put_contents("[Content_Types].xml", $ctype_xml);

   

           $Path = $dir. "/$type.xml";

           file_put_contents($Path, $xml);

       }

 

   /**

    *

    * createZip - 创建压缩包,for文件夹/文件

    *

    * @param type string-or-array $from

    *        => 字符串 '/path/to/source/file/or/folder/'

    *        => 或者 包含字符串的数组  array('fileA','FolderA','fileB')

    * @param type string $to

    *        => 字符串 '/path/to/output.zip'

    *

    */

   function createZip($from, $to) {

       /* Check zip class */

       if (!class_exists('ZipArchive')) {

           $return = 'Missing ZipArchive module in server.';

           return $return;

       }

 

       /* Check right of write for target zip file */

       $zip = new ZipArchive();

       if (!is_dir(dirname($to))) {

           mkdir(dirname($to), 0755, TRUE);

       }

       if (is_file($to)) {

           if ($zip->open($to, ZIPARCHIVE::OVERWRITE) !== TRUE) {

               $return = "Cannot overwrite: {$to}";

               return $return;

           }

       } else {

           if ($zip->open($to, ZIPARCHIVE::CREATE) !== TRUE) {

               $return = "Could not create archive: {$to}";

               return $return;

           }

       }

 

       /* Check path of source files or folder */

       $source_path_including_dir = array();

       $prefix_relative_path_for_source = '';

       if (is_array($from)) {

           foreach ($from as $path) {

               if (file_exists($path)) {

                   if ($prefix_relative_path_for_source == '') {

                       $prefix_relative_path_for_source = (is_dir($path)) ? realpath($path) : realpath(dirname($path));

                   }

                   $source_path_including_dir[] = $path;

               } else {

                   $return = 'No such file or folder: ' . $path;

                   return $return;

               }

           }

       } elseif (file_exists($from)) {

           $prefix_relative_path_for_source = (is_dir($from)) ? realpath($from) : realpath(dirname($from));

           $source_path_including_dir[] = $from;

       } else {

           $return = 'No such file or folder: ' . $from;

           return $return;

       }

       $prefix_relative_path_for_source = rtrim($prefix_relative_path_for_source, '/') . '/';

 

       /* Get final list of files, no folder */

       $final_list_of_files = array();

       foreach ($source_path_including_dir as $path) {

           if (is_file($path)) {

               /* File */

               $final_list_of_files[] = $path;

           } else {

               /* Folder */

               $list_of_files = $this->recursive_get_files_by_path_of_folder($path);

               foreach ($list_of_files as $one) {

                   $final_list_of_files[] = $one;

               }

           }

       }

       if (!count($final_list_of_files)) {

           $return = 'No valid file or folder used to zip';

           return $return;

       }

 

 

       /* Begin to add to zip file */

       foreach ($final_list_of_files as $one_file) {

           $zip->addFile($one_file, str_replace($prefix_relative_path_for_source, '', $one_file));

       }

       $zip->close();

 

       return $to;

   }

 

 

   /**

    * 获取文件夹下的文件列表,遍历模式

    *

    * @param type $dir

    * @param type $is_tree

    * @return string

    */

   function recursive_get_files_by_path_of_folder($dir, $is_tree = false) {

       $files = array();

       $dir = preg_replace('/[\/]{1}$/i', '', $dir);

       if (is_dir($dir)) {

           if ($handle = opendir($dir)) {

               while (($file = readdir($handle)) !== false) {

                   if ($file != "." && $file != "..") {

                       if (is_dir($dir . "/" . $file)) {

                           $sub_list = $this->recursive_get_files_by_path_of_folder($dir . "/" . $file, $is_tree);

                           if ($is_tree) {

                               $files[$file] = $sub_list;

                           } else {

                               foreach ($sub_list as $one_sub_file) {

                                   $files[] = $one_sub_file;

                               }

                           }

                       } else {

                           $files[] = $dir . "/" . $file;

                       }

                   }

               }

               closedir($handle);

               return $files;

           }

       } else {

           $files[] = $dir;

           return $files;

       }

   }

 

   // 删除目录

   function delDirAndFile($dirName)

   {

       if ( $dirName == '[Content_Types].xml' ){

           unlink("[Content_Types].xml");

           return;

       }

       if ($handle = opendir("$dirName")) {

           while (false !== ($item = readdir($handle))) {

               if ($item != "." && $item != "..") {

                   if (is_dir("$dirName/$item")) {

                       $this->delDirAndFile("$dirName/$item");

                   } else {

                       unlink("$dirName/$item") ;

                   }

               }

           }

           closedir($handle);

           if (rmdir($dirName)) return true ;

       }

   }

 

 

 

 

 

 

 

 

 

// 产品上架 shopid 是为了获取token 上面有获取token方法 自己结合实际情况对此段代码进行修改

    public function SubmitProductPackage($shopid, $zip_name, $in_id)

    {

        $sql = "SELECT * FROM shop WHERE id = $shopid ";

        $rs = $this->db->query($sql)->row_array();

        $time = time();

        if($time>$rs['access_timeout']){

            $token = $this->getToken($rs['appkey'], $rs['appsecret'], $rs['id']);

        }else{

            $token = $rs['access_token'];

        }

 

        $xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

        $xml.= '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

                    <s:Body>

                        <SubmitProductPackage xmlns="http://www.cdiscount.com">

                            '.$this->returnXMLHead($token).'

                            <productPackageRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">

                                <ZipFileFullPath>'.$zip_name.'</ZipFileFullPath>

                            </productPackageRequest>

                        </SubmitProductPackage>

                    </s:Body>

                </s:Envelope>';

 

        $output = $this->sendCurlPostRequest($xml, 'SubmitProductPackage');

 

        $PackageId = $output['Body']['SubmitProductPackageResponse']['SubmitProductPackageResult']['PackageId'];

        if ( $PackageId && $PackageId != '-1' ){

            $this->db->update('cdiscount.db_submit_product_list', array('ProductPackageId'=>$PackageId, 'status'=>1), 'id='.$in_id);

            return true;

        }else{

            $this->db->update('cdiscount.db_submit_product_list', array('ProductPackageId'=>-1, 'ProductStatus'=>$output['Body']['SubmitProductPackageResponse']['SubmitProductPackageResult']['ErrorMessage']), 'id='.$in_id);

            return $output['Body']['SubmitProductPackageResponse']['SubmitProductPackageResult']['ErrorMessage'];

        }

    }

 

 

public function returnXMLHead($token)

    {

        $head = "<headerMessage xmlns:a=\"http://schemas.datacontract.org/2004/07/Cdiscount.Framework.Core.Communication.Messages\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">

                    <a:Context>

                        <a:CatalogID>1</a:CatalogID >

                        <a:CustomerPoolID>1</a:CustomerPoolID >

                        <a:SiteID>100</a:SiteID >

                    </a:Context >

                    <a:Localization>

                        <a:Country>CN</a:Country >

                        <a:Currency>Eur</a:Currency >

                        <a:DecimalPosition>2</a:DecimalPosition >

                        <a:Language>En</a:Language >

                    </a:Localization>

                    <a:Security>

                        <a:DomainRightsList i:nil = \"true\" />

                        <a:IssuerID i:nil = \"true\" />

                        <a:SessionID i:nil = \"true\" />

                        <a:SubjectLocality i:nil = \"true\" />

                        <a:TokenId >$token</a:TokenId >

                        <a:UserName i:nil = \"true\" />

                    </a:Security>

                    <a:Version>1.0</a:Version >

                </headerMessage>";

        return $head;

    }

 

//模拟post请求

    public function sendCurlPostRequest($curlData, $api)

    {

        $url='https://wsvc.cdiscount.com/MarketplaceAPIService.svc';

        $curl = curl_init();

        curl_setopt ($curl, CURLOPT_URL, $url);

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

        curl_setopt($curl,CURLOPT_TIMEOUT,120);

        curl_setopt($curl,CURLOPT_ENCODING,'gzip');

        curl_setopt($curl,CURLOPT_HTTPHEADER,array (

            'Accept-Encoding: gzip,deflate',

            'Content-Type: text/xml;charset=UTF-8',

            'SOAPAction:"http://www.cdiscount.com/IMarketplaceAPIService/'.$api.'"'

        ));

        curl_setopt ($curl, CURLOPT_POST, 1);

        curl_setopt ($curl, CURLOPT_POSTFIELDS, $curlData);

        // https请求 不验证证书和hosts

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

        $result = curl_exec($curl);

        //$http_error = curl_error($curl);

        //$http_status = curl_getinfo($curl);

        //print_r($http_status);exit;

        if ( $result == false ){

            local_log("============== sendCurlPostRequest ERROR S===============");

            local_log(curl_error($curl));

            local_log("============== sendCurlPostRequest ERROR E===============");

            die('CURL ERROR');

        }

        curl_close ($curl);

 

        //echo $result;exit;

        //print_r($result);exit;

        //解析xml文件 并作相关处理

        $soap_xml = "";

        $soap_xml.= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";

        $soap_xml.= $result;

        $response = str_replace('</s:','</',$soap_xml);

        $response = str_replace('<s:','<',$response);

        //$response = strtr($soap_xml, ['</s:' => '</', '<s:' => '<']);

        //$response = strtr($soap_xml, ['</s:' => '</', '<s:' => '<']);

        $output = json_decode(json_encode(simplexml_load_string($response)),true);

        return $output;

    }

 

保存接口返回 PackageId

 

step3:获取产品导入的进度状态 GetProductPackageSubmissionResult

cdiscount 后台可查看上传进度

在这里插入图片描述

接口文档 https://dev.cdiscount.com/marketplace/?page_id=240

 

当返回信息状态为 Integrated 时 你就已经成功一半了!

 

step4:上传产品价格、库存、物流等其他信息… SubmitOfferPackage ( 奇葩公司 这都分两步走)

接口地址 https://dev.cdiscount.com/marketplace/?page_id=91

Detailed Description of the Offers.xml files

具体可参考文档及step2-step3

 

这里注意的是 上架的时候看这个

在这里插入图片描述

 

报错信息

// Not enough quota available (1 requested and 0 available)

// 一般这个只有在调用 SubmitProductPackage 方法时才会出现 返回这个的时候一般要间隔1-2小时才能再次调用此方法 不然会一直返回这个

{"Body":{"SubmitProductPackageResponse":{"SubmitProductPackageResult":{"ErrorMessage":"Not enough quota available (1 requested and 0 available).","OperationSuccess":"false","ErrorList":{"Error":{"ErrorType":"Quota","Message":"Not enough quota available (1 requested and 0 available)."}},"SellerLogin":"apinewcos16-api","TokenId":"98345a53105d45123123123213213219bd","NumberOfErrors":"0","PackageId":"0","PackageIntegrationStatus":[],"ProductLogList":[]}}}}

 

 

 

// Le fichier d'int\u00e9gration des produits soumis ('http:\\w81kukm95gd.xicp.io:55\fuman\cidscount\2020-05-13\cidscount44.zip') a \u00e9t\u00e9 int\u00e9gr\u00e9 dans un pr\u00e9c\u00e9dent appel ! Il n'est pas possible de soumettre plusieurs fois le m\u00eame fichier.

// 像这个 如果你确定不是上传了同一个文件 那指定是你的ZIP文件写错了 我的就是这个卡了好久 就是zip目录层的问题

{"Body":{"SubmitProductPackageResponse":{"SubmitProductPackageResult":{"ErrorMessage":"Le fichier d'int\u00e9gration des produits soumis ('http:\/\/w81kukm95gd.xicp.io:55\/fuman\/cidscount\/2020-05-13\/cidscount44.zip') a \u00e9t\u00e9 int\u00e9gr\u00e9 dans un pr\u00e9c\u00e9dent appel ! Il n'est pas possible de soumettre plusieurs fois le m\u00eame fichier.","OperationSuccess":"false","ErrorList":{"Error":{"ErrorType":"FileAlreadySubmitted","Message":"Le fichier d'int\u00e9gration des produits soumis ('http:\/\/w81kukm95gd.xicp.io:55\/fuman\/cidscount\/2020-05-13\/cidscount44.zip') a \u00e9t\u00e9 int\u00e9gr\u00e9 dans un pr\u00e9c\u00e9dent appel ! Il n'est pas possible de soumettre plusieurs fois le m\u00eame fichier."}},"SellerLogin":"apinewcos16-api","TokenId":"98345a53105d45d681559078e86219bd","NumberOfErrors":"0","PackageId":"-1","PackageIntegrationStatus":[],"ProductLogList":[]}}}}

 

 

总结

歪果仁的思维到底是和国内有点差别… 这个接口对接过程中会返回各种报错信息(大神玩家请忽略这句话) 总归一点一点解决了…

 

有其他问题欢迎留言 。。。 看到必回…

 

2021-01-30 新增

报错小结

Not enough quota available (1 requested and 0 available).

 

接口配额限制 等待一小时左右基本就好了 或者等待上一条处理信息结束(有一次我被卡了一天 发工单过去他们说是缓存问题…)

 

Produit : Les informations renseignées dans vos libellés et descriptifs ne sont pas assez claires pour déterminer la catégorie à associer. Si vous ne détectez pas d’erreur, vous pouvez nous contacter via la rubrique « Aide » de votre espace vendeur

 

通过在产品的标题(ShortLabel)和/或说明(Description)中添加产品的主类别,可以纠正错误。

 

https://beezup.zendesk.com/hc/fr/articles/215434843-Cdiscount-questions-fr%C3%A9quentes

 

这个连接是我谷歌到的一个网址 里面有很多报错信息的解释 可以看看

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值