🔧 Bug Fixes: - Fixed product image structure to match Miravia API requirements - Updated MiraviaProduct.php getData() method to wrap images in {"Image": [...]} format - Updated MiraviaCombination.php getData() method to wrap SKU images properly - Resolved error "[4224] The Main image of the product is required" 📋 Changes: - Modified getData() methods to transform flat image arrays to nested structure - Product images: images[] → Images: {"Image": [...]} - SKU images: images[] → Images: {"Image": [...]} - Maintains backward compatibility for empty image arrays 🎯 Impact: - Product uploads will now pass Miravia's image validation - Both product-level and SKU-level images properly formatted - Complies with official Miravia API documentation structure 🤖 Generated with Claude Code (https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
572 lines
16 KiB
PHP
572 lines
16 KiB
PHP
<?php
|
|
|
|
class MiraviaLink
|
|
{
|
|
|
|
protected $api_url = 'https://miravia.wecomm.es';
|
|
protected $api_key = '';
|
|
public $last_error = '';
|
|
public $request_id = '';
|
|
public $insecure_mode = true;
|
|
public $sandbox_mode = false;
|
|
|
|
public function __construct($api_key = '')
|
|
{
|
|
$this->api_key = $api_key;
|
|
if( strpos($api_key, '_f6649cb881216ce050bd0e3') ){
|
|
$this->sandbox_mode = true;
|
|
$this->api_url = 'https://sandbox.miravia.wecomm.es';
|
|
}
|
|
}
|
|
|
|
public function getRegisterUrl($callback_url)
|
|
{
|
|
$url = $this->api_url . '/token';
|
|
$ret = $this->CallAPI($url, 'POST',
|
|
array('callback_url' => $callback_url));
|
|
if($ret === false){
|
|
return '';
|
|
}
|
|
$response = json_decode($ret, true);
|
|
if(!is_array($response)){
|
|
$last_error = $ret;
|
|
return '';
|
|
}
|
|
if(isset($response['error'])){
|
|
$this->last_error = $response['error'];
|
|
}
|
|
|
|
$url = isset($response['url']) ? $response['url'] : '';
|
|
$this->request_id = isset($response['request_id']) ? $response['request_id'] : '';
|
|
|
|
return $url;
|
|
}
|
|
|
|
public function getSellerInfo($notify_endpoint = '')
|
|
{
|
|
$url = $this->api_url . '/seller/info';
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
//$this->last_error = print_r($resp,true);
|
|
return false;
|
|
}
|
|
|
|
// update data
|
|
$url = $this->api_url . '/seller/update';
|
|
if(!empty($notify_endpoint)){
|
|
$data = [ 'notification_url' => $notify_endpoint ];
|
|
}
|
|
$data = json_encode($data);
|
|
$ret = $this->CallAPI($url, 'POST', $data);
|
|
|
|
if(isset($resp['seller'])){
|
|
return $resp['seller'];
|
|
}
|
|
//$this->last_error = print_r($resp,true);
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getWarehouses()
|
|
{
|
|
$url = $this->api_url . '/seller/warehouses';
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
$this->last_error = print_r($resp,true);
|
|
return false;
|
|
}
|
|
|
|
if(isset($resp['module'])){
|
|
return $resp['module'];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getFeedResult($document)
|
|
{
|
|
$url = $this->api_url . '/feed/result/' . $document;
|
|
$ret = $this->CallAPI($url);
|
|
return $ret;
|
|
}
|
|
|
|
public function createProduct($json_product)
|
|
{
|
|
$url = $this->api_url . '/product/create';
|
|
$ret = $this->CallAPI($url, 'POST', $json_product);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else if(!$resp['success']){
|
|
$this->last_error = $resp['message'];
|
|
if(isset($resp['detail'])){
|
|
foreach ($resp['detail'] as $det){
|
|
$this->last_error .= "\r" . $det['field'] . ': ' . $det['message'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function updateProduct($miravia_id, $json_product)
|
|
{
|
|
$url = $this->api_url . '/product/' . $miravia_id . '/update';
|
|
$ret = $this->CallAPI($url, 'POST', $json_product);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else if(!$resp['success']){
|
|
$this->last_error = $resp['message'];
|
|
if(isset($resp['detail'])){
|
|
foreach ($resp['detail'] as $det){
|
|
$this->last_error .= "\r" . $det['field'] . ': ' . $det['message'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function getProductList($page = 1, $pagesize = 50)
|
|
{
|
|
$url = $this->api_url . '/product/list?pagesize=' . $pagesize . '&page=' . $page;
|
|
$ret = $this->CallAPI($url);
|
|
return json_decode($ret, true);
|
|
}
|
|
|
|
public function getProductBySku($sku)
|
|
{
|
|
$url = $this->api_url . '/product/' . $sku . '/find';
|
|
$ret = $this->CallAPI($url);
|
|
return json_decode($ret);
|
|
}
|
|
|
|
public function getProductById($id)
|
|
{
|
|
$url = $this->api_url . '/product/' . $id . '/get';
|
|
$ret = $this->CallAPI($url);
|
|
return json_decode($ret);
|
|
}
|
|
|
|
public function updateStock($json_stock, $use_feed = false)
|
|
{
|
|
if($use_feed){
|
|
$url = $this->api_url . '/feed/stock';
|
|
}else{
|
|
$url = $this->api_url . '/product/update_stock';
|
|
}
|
|
$ret = $this->CallAPI($url, 'POST', $json_stock);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else if(!$resp['success']){
|
|
$this->last_error = $resp['message'];
|
|
if(isset($resp['detail'])){
|
|
foreach ($resp['detail'] as $det){
|
|
$this->last_error .= "\r" . $det['field'] . ': ' . $det['message'];
|
|
}
|
|
}
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
public function disableProducts($itemids)
|
|
{
|
|
if(!is_array($itemids)){
|
|
$itemids = [$itemids];
|
|
}
|
|
$itemids = json_encode([
|
|
'item_ids' => $itemids
|
|
]);
|
|
$url = $this->api_url . '/product/disable';
|
|
$ret = $this->CallAPI($url, 'POST', $itemids);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else {
|
|
return (bool) $resp['success'];
|
|
}
|
|
}
|
|
|
|
public function enableProducts($itemids)
|
|
{
|
|
if(!is_array($itemids)){
|
|
$itemids = [$itemids];
|
|
}
|
|
$itemids = json_encode([
|
|
'item_ids' => $itemids
|
|
]);
|
|
$url = $this->api_url . '/product/enable';
|
|
$ret = $this->CallAPI($url, 'POST', $itemids);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else {
|
|
return (bool) $resp['success'];
|
|
}
|
|
}
|
|
|
|
public function deleteProduct($itemid)
|
|
{
|
|
$url = $this->api_url . '/product/' . $itemid . '/delete';
|
|
$ret = $this->CallAPI($url, 'GET');
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else {
|
|
return (bool) $resp['success'];
|
|
}
|
|
}
|
|
|
|
public function getFeeds()
|
|
{
|
|
$url = $this->api_url . '/feed/list';
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
if(isset($resp['feeds'])){
|
|
return $resp['feeds'];
|
|
}
|
|
//$this->last_error = print_r($resp,true);
|
|
|
|
return false;
|
|
}
|
|
|
|
public function getFeedInfo($id)
|
|
{
|
|
$url = $this->api_url . '/feed/' . $id . '/get' ;
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(isset($resp['feed_result'])) {
|
|
$response = [];
|
|
if(isset($resp['response'])) {
|
|
$response = $resp['response'];
|
|
}
|
|
$resp = $resp['feed_result'];
|
|
$resp['response'] = $response;
|
|
}else{
|
|
if(isset($resp['code'])){
|
|
$this->last_error = $resp['code'] . ': ' .
|
|
@$resp['message'] ?: '';
|
|
}
|
|
}
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
public function cancelFeed($id)
|
|
{
|
|
$url = $this->api_url . '/feed/' . $id . '/cancel' ;
|
|
$ret = $this->CallAPI($url, 'POST');
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(isset($resp['feed_result'])) {
|
|
$resp = $resp['feed_result'];
|
|
}
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
|
|
public function sendFeed($data, $type = 'create')
|
|
{
|
|
$url = $this->api_url . '/feed/' . $type;
|
|
$ret = $this->CallAPI($url, 'POST', $data);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success'])){
|
|
return false;
|
|
}else if(!$resp['success']){
|
|
// $this->last_error = $resp['message'];
|
|
if(isset($resp['detail'])){
|
|
foreach ($resp['detail'] as $det){
|
|
$this->last_error .= "\r" . $det['field'] . ': ' . $det['message'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
/*
|
|
* $fromDate format: YYYY-MM-DD ('Y-m-d')
|
|
*/
|
|
public function getOrders(string $fromDate)
|
|
{
|
|
|
|
if($d = date_parse_from_format('Y-m-d', $fromDate)){
|
|
$fromDate = date('Y-m-d', mktime(0, 0, 0, $d['month'], $d['day'], $d['year']));
|
|
}else{
|
|
$this->last_error= 'Invalid date';
|
|
return false;
|
|
}
|
|
|
|
$url = $this->api_url . '/order/list';
|
|
$url .= '?from_date=' . $fromDate;
|
|
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function getOrder($order_number)
|
|
{
|
|
$url = $this->api_url . '/order/' . $order_number . '/get';
|
|
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function orderConfirmation($order_number, $status)
|
|
{
|
|
$url = $this->api_url . '/order/confirmation/' . $order_number . '/' . $status;
|
|
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function orderPack($id_order, $products)
|
|
{
|
|
$url = $this->api_url . '/order/' . $id_order . '/pack';
|
|
|
|
if(is_array($products)){
|
|
$data = json_encode($products);
|
|
}else{
|
|
$data = $products;
|
|
}
|
|
|
|
$ret = $this->CallAPI($url , 'POST', $data);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function getShippingLabel($package_id)
|
|
{
|
|
$url = $this->api_url . '/order/label/' . $package_id;
|
|
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
|
|
return $resp;
|
|
}
|
|
|
|
public function getDBSCarriers()
|
|
{
|
|
$url = $this->api_url . '/order/dbs/providers';
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(isset($resp['result'])){
|
|
$resp = $resp['result'];
|
|
}
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
return $resp;
|
|
}
|
|
|
|
public function setDBSTracking($packages, $tracking, $provider)
|
|
{
|
|
if(!is_array($packages)){
|
|
$packages = [$packages];
|
|
}
|
|
$payload = [];
|
|
foreach ($packages as $package){
|
|
$payload['packages'][] = [
|
|
'package_id' => $package,
|
|
'tracking_number' => $tracking,
|
|
'shipment_provider_code' => $provider
|
|
];
|
|
}
|
|
|
|
$url = $this->api_url . '/order/dbs/update_tracking';
|
|
$data = json_encode($payload);
|
|
$ret = $this->CallAPI($url, 'POST', $data);
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
$this->last_error = $ret;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function subscribe($seconds, $message='')
|
|
{
|
|
$url = $this->api_url . '/seller/notify/' . (int)$seconds;
|
|
if(!empty($message)){
|
|
$url .= '?message=' . urlencode($message);
|
|
}
|
|
$ret = $this->CallAPI($url);
|
|
if($ret === false){
|
|
return false;
|
|
}
|
|
$resp = json_decode($ret, true);
|
|
if(!isset($resp['success']) || !$resp['success']){
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function purgeImageCache()
|
|
{
|
|
$url = $this->api_url . '/image/cache/purge';
|
|
$ret = $this->CallAPI($url);
|
|
return json_decode($ret, true);
|
|
}
|
|
|
|
protected function CallAPI($url, $method='GET', $data = false)
|
|
{
|
|
if(class_exists('LOG')) {
|
|
LOG::add("DEBUG API: Making {$method} request to: {$url}");
|
|
if($data && strlen($data) < 1000) {
|
|
LOG::add("DEBUG API: Request payload: " . $data);
|
|
} elseif($data) {
|
|
LOG::add("DEBUG API: Request payload size: " . strlen($data) . " bytes");
|
|
}
|
|
}
|
|
|
|
$curl = curl_init();
|
|
|
|
switch ($method)
|
|
{
|
|
case "POST":
|
|
curl_setopt($curl, CURLOPT_POST, 1);
|
|
|
|
if ($data) {
|
|
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
|
|
}
|
|
break;
|
|
case "PUT":
|
|
curl_setopt($curl, CURLOPT_PUT, 1);
|
|
break;
|
|
default:
|
|
if ($data)
|
|
$url = sprintf("%s?%s", $url, http_build_query($data));
|
|
}
|
|
|
|
curl_setopt($curl, CURLOPT_URL, $url);
|
|
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
|
|
|
if($this->insecure_mode) {
|
|
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
|
|
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
|
|
}
|
|
|
|
if(!empty($this->api_key)){
|
|
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
|
|
'Api-Token: ' . $this->api_key
|
|
));
|
|
if(class_exists('LOG')) {
|
|
LOG::add("DEBUG API: Using API token: " . substr($this->api_key, 0, 10) . "...");
|
|
}
|
|
}
|
|
|
|
$start_time = microtime(true);
|
|
$result = curl_exec($curl);
|
|
$end_time = microtime(true);
|
|
$response_time = round(($end_time - $start_time) * 1000, 2);
|
|
|
|
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
|
|
|
|
if(class_exists('LOG')) {
|
|
LOG::add("DEBUG API: Response time: {$response_time}ms, HTTP code: {$http_code}");
|
|
if($result && strlen($result) < 2000) {
|
|
LOG::add("DEBUG API: Response: " . $result);
|
|
} elseif($result) {
|
|
LOG::add("DEBUG API: Response size: " . strlen($result) . " bytes");
|
|
}
|
|
}
|
|
|
|
if($result === false){
|
|
$this->last_error = curl_error($curl);
|
|
if(class_exists('LOG')) {
|
|
LOG::add("DEBUG API: CURL Error: " . $this->last_error);
|
|
}
|
|
}
|
|
curl_close($curl);
|
|
return $result;
|
|
}
|
|
|
|
}
|