Cdiscount API产品上架 SubmitProductPackage

Cdiscount API产品上架

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

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

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

评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值