🏭 Supplier API

Управление таможенными счёт-фактурами для возврата НДС продавцам из Турции

Supplier API предназначен для работы с таможенными счёт-фактурами в рамках программы возврата НДС для продавцов из Турции. Этот API позволяет загружать, создавать, обновлять и получать информацию о счёт-фактурах.

📋 Обзор методов

Метод Endpoint Описание
uploadInvoiceFile() POST /v1/invoice/file/upload Загрузка файла счёт-фактуры
createOrUpdateInvoice() POST /v2/invoice/create-or-update Создание/обновление счёт-фактуры
getInvoice() POST /v2/invoice/get Получение информации о счёт-фактуре
deleteInvoice() POST /v1/invoice/delete Удаление ссылки на счёт-фактуру

🎯 Целевая аудитория

Этот API предназначен специально для продавцов из Турции, которые участвуют в программе возврата НДС через таможенные счёт-фактуры.

📄 Поддерживаемые форматы файлов

  • JPEG — для сканированных документов
  • PDF — для цифровых документов
  • Максимальный размер: 10 МБ

💱 Поддерживаемые валюты

Код Валюта
USD Доллар США (по умолчанию)
EUR Евро
TRY Турецкая лира
CNY Китайский юань
RUB Российский рубль
GBP Британский фунт стерлингов

🚀 Быстрый старт

Полный цикл работы с счёт-фактурой

// 1. Загрузка файла счёт-фактуры
const file = await fs.readFile('invoice.pdf');
const base64Content = file.toString('base64');

const uploadResult = await client.supplier.uploadInvoiceFile({
  base64_content: base64Content,
  posting_number: '0001-1234567-0000001'
});

console.log(`📄 Файл загружен: ${uploadResult.url}`);

// 2. Создание записи о счёт-фактуре
const invoice = await client.supplier.createOrUpdateInvoice({
  date: '2024-01-15T10:00:00Z',
  posting_number: '0001-1234567-0000001',
  url: uploadResult.url!,
  number: 'INV-TR-2024-001',
  price: 15000.50,
  price_currency: 'TRY',
  hs_codes: [
    { code: '6203420000' }, // Мужские брюки из хлопка
    { code: '6109100000' }  // Футболки из хлопка
  ]
});

console.log(`✅ Счёт-фактура создана: ${invoice.result ? 'успешно' : 'ошибка'}`);

// 3. Получение информации о счёт-фактуре
const invoiceInfo = await client.supplier.getInvoice({
  posting_number: '0001-1234567-0000001'
});

console.log('📋 Информация о счёт-фактуре:');
console.log(`   Номер: ${invoiceInfo.result?.number}`);
console.log(`   Дата: ${invoiceInfo.result?.date}`);
console.log(`   Сумма: ${invoiceInfo.result?.price} ${invoiceInfo.result?.price_currency}`);
console.log(`   HS-коды: ${invoiceInfo.result?.hs_codes?.length} шт.`);

📊 Детальные примеры использования

1. 📤 Загрузка различных форматов файлов

import * as fs from 'fs/promises';
import * as path from 'path';

class InvoiceFileUploader {
  async uploadInvoiceFile(filePath: string, postingNumber: string) {
    // Проверка формата файла
    const ext = path.extname(filePath).toLowerCase();
    if (!['.pdf', '.jpg', '.jpeg'].includes(ext)) {
      throw new Error('Поддерживаются только PDF и JPEG файлы');
    }
    
    // Проверка размера файла (10 МБ = 10 * 1024 * 1024 байт)
    const stats = await fs.stat(filePath);
    const maxSize = 10 * 1024 * 1024;
    if (stats.size > maxSize) {
      throw new Error(`Размер файла превышает 10 МБ (текущий: ${Math.round(stats.size / 1024 / 1024)} МБ)`);
    }
    
    // Загрузка и кодирование файла
    const fileBuffer = await fs.readFile(filePath);
    const base64Content = fileBuffer.toString('base64');
    
    console.log(`📁 Загружаем файл: ${path.basename(filePath)}`);
    console.log(`   Размер: ${Math.round(stats.size / 1024)} КБ`);
    console.log(`   Формат: ${ext.toUpperCase()}`);
    
    // Отправка на сервер
    const result = await client.supplier.uploadInvoiceFile({
      base64_content: base64Content,
      posting_number: postingNumber
    });
    
    console.log(`✅ Файл успешно загружен`);
    console.log(`   URL: ${result.url}`);
    
    return result;
  }
}

// Использование
const uploader = new InvoiceFileUploader();
const result = await uploader.uploadInvoiceFile('./documents/invoice-tr-001.pdf', '0001-1234567-0000001');

2. 🧾 Создание счёт-фактур с различными валютами

interface InvoiceData {
  postingNumber: string;
  invoiceNumber: string;
  date: Date;
  amount: number;
  currency: 'USD' | 'EUR' | 'TRY' | 'CNY' | 'RUB' | 'GBP';
  hsCodes: string[];
  fileUrl: string;
}

class InvoiceManager {
  async createInvoice(data: InvoiceData) {
    console.log(`🧾 Создание счёт-фактуры ${data.invoiceNumber}`);
    console.log(`   Отправление: ${data.postingNumber}`);
    console.log(`   Сумма: ${data.amount} ${data.currency}`);
    console.log(`   HS-коды: ${data.hsCodes.length} шт.`);
    
    const response = await client.supplier.createOrUpdateInvoice({
      date: data.date.toISOString(),
      posting_number: data.postingNumber,
      url: data.fileUrl,
      number: data.invoiceNumber,
      price: data.amount,
      price_currency: data.currency,
      hs_codes: data.hsCodes.map(code => ({ code }))
    });
    
    if (response.result) {
      console.log(`✅ Счёт-фактура успешно создана`);
    } else {
      console.log(`❌ Ошибка при создании счёт-фактуры`);
    }
    
    return response;
  }
  
  // Пример с турецкой лирой
  async createTurkishInvoice(postingNumber: string, fileUrl: string) {
    return this.createInvoice({
      postingNumber,
      invoiceNumber: `TR-${Date.now()}`,
      date: new Date(),
      amount: 2500.75,
      currency: 'TRY',
      hsCodes: [
        '6203420000', // Мужские брюки
        '6109100000', // Футболки
        '6204620000'  // Женские брюки
      ],
      fileUrl
    });
  }
  
  // Пример с долларом США
  async createUSDInvoice(postingNumber: string, fileUrl: string) {
    return this.createInvoice({
      postingNumber,
      invoiceNumber: `USD-${Date.now()}`,
      date: new Date(),
      amount: 850.00,
      currency: 'USD',
      hsCodes: [
        '6403990000', // Обувь
        '4202920000'  // Сумки
      ],
      fileUrl
    });
  }
}

3. 🔍 Получение и анализ информации о счёт-фактурах

class InvoiceAnalyzer {
  async getInvoiceDetails(postingNumber: string) {
    console.log(`🔍 Запрос информации о счёт-фактуре для отправления: ${postingNumber}`);
    
    const response = await client.supplier.getInvoice({
      posting_number: postingNumber
    });
    
    if (!response.result) {
      console.log('❌ Счёт-фактура не найдена');
      return null;
    }
    
    const invoice = response.result;
    
    console.log('📋 Детали счёт-фактуры:');
    console.log(`   📄 Номер: ${invoice.number || 'Не указан'}`);
    console.log(`   📅 Дата: ${invoice.date ? new Date(invoice.date).toLocaleDateString('ru-RU') : 'Не указана'}`);
    console.log(`   💰 Сумма: ${invoice.price || 0} ${invoice.price_currency || 'USD'}`);
    console.log(`   🔗 URL файла: ${invoice.file_url || 'Недоступен'}`);
    
    if (invoice.hs_codes && invoice.hs_codes.length > 0) {
      console.log(`   📦 HS-коды товаров:`);
      invoice.hs_codes.forEach((hsCode, index) => {
        console.log(`      ${index + 1}. ${hsCode.code} (${this.getHSCodeDescription(hsCode.code)})`);
      });
    } else {
      console.log(`   📦 HS-коды: не указаны`);
    }
    
    return invoice;
  }
  
  // Описания HS-кодов (примеры)
  private getHSCodeDescription(code?: string): string {
    const descriptions: Record<string, string> = {
      '6203420000': 'Мужские брюки из хлопка',
      '6109100000': 'Футболки из хлопка', 
      '6204620000': 'Женские брюки из хлопка',
      '6403990000': 'Обувь прочая',
      '4202920000': 'Сумки и чемоданы'
    };
    
    return descriptions[code || ''] || 'Описание недоступно';
  }
  
  async validateInvoiceData(invoice: any) {
    const issues = [];
    
    if (!invoice.number) {
      issues.push('⚠️ Не указан номер счёт-фактуры');
    }
    
    if (!invoice.date) {
      issues.push('⚠️ Не указана дата счёт-фактуры');
    }
    
    if (!invoice.price || invoice.price <= 0) {
      issues.push('⚠️ Некорректная сумма счёт-фактуры');
    }
    
    if (!invoice.file_url) {
      issues.push('⚠️ Отсутствует ссылка на файл');
    }
    
    if (!invoice.hs_codes || invoice.hs_codes.length === 0) {
      issues.push('⚠️ Не указаны HS-коды товаров');
    }
    
    if (issues.length > 0) {
      console.log('❌ Обнаружены проблемы с данными:');
      issues.forEach(issue => console.log(`   ${issue}`));
      return false;
    }
    
    console.log('✅ Данные счёт-фактуры корректны');
    return true;
  }
}

4. 🗑️ Управление жизненным циклом счёт-фактур

class InvoiceLifecycleManager {
  async deleteInvoice(postingNumber: string, reason?: string) {
    console.log(`🗑️ Удаление ссылки на счёт-фактуру для отправления: ${postingNumber}`);
    if (reason) {
      console.log(`   Причина: ${reason}`);
    }
    
    // Сначала получаем информацию для логирования
    const invoiceInfo = await client.supplier.getInvoice({
      posting_number: postingNumber
    });
    
    if (invoiceInfo.result) {
      console.log(`   📄 Удаляемая счёт-фактура: ${invoiceInfo.result.number}`);
      console.log(`   💰 Сумма: ${invoiceInfo.result.price} ${invoiceInfo.result.price_currency}`);
    }
    
    // Выполняем удаление
    const result = await client.supplier.deleteInvoice({
      posting_number: postingNumber
    });
    
    if (result.result) {
      console.log(`✅ Ссылка на счёт-фактуру успешно удалена`);
    } else {
      console.log(`❌ Ошибка при удалении ссылки на счёт-фактуру`);
    }
    
    return result;
  }
  
  async updateInvoice(postingNumber: string, updates: Partial<InvoiceData>) {
    console.log(`📝 Обновление счёт-фактуры для отправления: ${postingNumber}`);
    
    // Получаем текущую информацию
    const currentInvoice = await client.supplier.getInvoice({
      posting_number: postingNumber
    });
    
    if (!currentInvoice.result) {
      throw new Error('Счёт-фактура не найдена');
    }
    
    // Подготавливаем данные для обновления
    const updateData = {
      date: updates.date?.toISOString() || currentInvoice.result.date!,
      posting_number: postingNumber,
      url: updates.fileUrl || currentInvoice.result.file_url!,
      number: updates.invoiceNumber || currentInvoice.result.number,
      price: updates.amount || currentInvoice.result.price,
      price_currency: updates.currency || currentInvoice.result.price_currency,
      hs_codes: updates.hsCodes?.map(code => ({ code })) || currentInvoice.result.hs_codes || []
    };
    
    console.log(`   Обновляемые поля: ${Object.keys(updates).join(', ')}`);
    
    const result = await client.supplier.createOrUpdateInvoice(updateData);
    
    if (result.result) {
      console.log(`✅ Счёт-фактура успешно обновлена`);
    } else {
      console.log(`❌ Ошибка при обновлении счёт-фактуры`);
    }
    
    return result;
  }
}

5. 📊 Пакетная обработка счёт-фактур

class BatchInvoiceProcessor {
  async processMultipleInvoices(invoices: InvoiceData[]) {
    console.log(`🔄 Пакетная обработка ${invoices.length} счёт-фактур`);
    
    const results = [];
    const batchSize = 3; // Обрабатываем по 3 счёт-фактуры одновременно
    
    for (let i = 0; i < invoices.length; i += batchSize) {
      const batch = invoices.slice(i, i + batchSize);
      console.log(`📦 Обработка пакета ${Math.floor(i / batchSize) + 1}/${Math.ceil(invoices.length / batchSize)}`);
      
      // Обрабатываем пакет параллельно
      const batchPromises = batch.map(async (invoiceData, index) => {
        try {
          console.log(`   ⏳ [${i + index + 1}] Обработка ${invoiceData.invoiceNumber}`);
          
          const result = await client.supplier.createOrUpdateInvoice({
            date: invoiceData.date.toISOString(),
            posting_number: invoiceData.postingNumber,
            url: invoiceData.fileUrl,
            number: invoiceData.invoiceNumber,
            price: invoiceData.amount,
            price_currency: invoiceData.currency,
            hs_codes: invoiceData.hsCodes.map(code => ({ code }))
          });
          
          console.log(`   ✅ [${i + index + 1}] ${invoiceData.invoiceNumber} - успешно`);
          return { success: true, invoiceNumber: invoiceData.invoiceNumber, result };
          
        } catch (error) {
          console.log(`   ❌ [${i + index + 1}] ${invoiceData.invoiceNumber} - ошибка: ${error}`);
          return { success: false, invoiceNumber: invoiceData.invoiceNumber, error: String(error) };
        }
      });
      
      const batchResults = await Promise.all(batchPromises);
      results.push(...batchResults);
      
      // Пауза между пакетами для соблюдения лимитов API
      if (i + batchSize < invoices.length) {
        console.log(`   ⏸️ Пауза 1 секунда перед следующим пакетом...`);
        await new Promise(resolve => setTimeout(resolve, 1000));
      }
    }
    
    // Сводка результатов
    const successful = results.filter(r => r.success).length;
    const failed = results.filter(r => !r.success).length;
    
    console.log(`\n📊 Итоги пакетной обработки:`);
    console.log(`   ✅ Успешно: ${successful}`);
    console.log(`   ❌ Ошибок: ${failed}`);
    console.log(`   📈 Процент успеха: ${Math.round(successful / results.length * 100)}%`);
    
    if (failed > 0) {
      console.log(`\n❌ Неудачные операции:`);
      results.filter(r => !r.success).forEach(r => {
        console.log(`   - ${r.invoiceNumber}: ${r.error}`);
      });
    }
    
    return results;
  }
}

🏗️ TypeScript типы

Основные интерфейсы

// Запрос загрузки файла
interface SupplierInvoiceFileUploadRequest {
  base64_content: string;  // обязательное поле
  posting_number: string;  // обязательное поле
}

// Ответ загрузки файла
interface SupplierInvoiceFileUploadResponse {
  url?: string;  // ссылка на загруженный файл
}

// HS-код товара
interface SupplierHsCode {
  code?: string;
}

// Запрос создания/обновления счёт-фактуры
interface SupplierInvoiceCreateOrUpdateRequest {
  date: string;           // обязательное поле (ISO 8601)
  posting_number: string; // обязательное поле
  url: string;           // обязательное поле (ссылка из upload)
  number?: string;       // номер счёт-фактуры (макс. 50 символов)
  price?: number;        // сумма (до 2 знаков после точки)
  price_currency?: 'USD' | 'EUR' | 'TRY' | 'CNY' | 'RUB' | 'GBP';
  hs_codes?: SupplierHsCode[];
}

// Ответ создания/обновления
interface SupplierInvoiceCreateOrUpdateResponse {
  result?: boolean;  // результат операции
}

// Запрос получения информации
interface SupplierInvoiceGetRequest {
  posting_number: string;  // обязательное поле
}

// Информация о счёт-фактуре
interface SupplierInvoiceInfo {
  date?: string;           // дата загрузки
  file_url?: string;       // ссылка на файл
  hs_codes?: SupplierResponseHsCode[];
  number?: string;         // номер счёт-фактуры
  price?: number;          // сумма
  price_currency?: 'USD' | 'EUR' | 'TRY' | 'CNY' | 'RUB' | 'GBP';
}

// Ответ получения информации
interface SupplierInvoiceGetResponse {
  result?: SupplierInvoiceInfo;
}

// Запрос удаления
interface SupplierInvoiceDeleteRequest {
  posting_number: string;  // обязательное поле
}

// Ответ удаления
interface SupplierInvoiceDeleteResponse {
  result?: boolean;  // результат операции
}

⚠️ Важные особенности

Ограничения файлов

  • Форматы: только JPEG и PDF
  • Размер: максимум 10 МБ
  • Кодирование: Base64 для передачи

Нумерация счёт-фактур

  • Максимальная длина: 50 символов
  • Может содержать буквы и цифры
  • Рекомендуется использовать уникальные номера

Валюты и цены

  • По умолчанию используется USD
  • Разделитель дробной части: точка
  • Максимум 2 знака после точки
  • Пример: 199.99

HS-коды товаров

  • Используются для таможенного оформления
  • 10-значные коды международной номенклатуры
  • Обязательны для корректного возврата НДС

🔧 Лучшие практики

1. Обработка ошибок

async function safeInvoiceOperation<T>(
  operation: () => Promise<T>,
  operationName: string
): Promise<T | null> {
  try {
    console.log(`⏳ Выполнение: ${operationName}`);
    const result = await operation();
    console.log(`✅ Завершено: ${operationName}`);
    return result;
  } catch (error) {
    console.error(`❌ Ошибка в ${operationName}:`, error);
    
    // Логирование для различных типов ошибок
    if (error instanceof Error) {
      if (error.message.includes('413')) {
        console.error('   Файл слишком большой (больше 10 МБ)');
      } else if (error.message.includes('422')) {
        console.error('   Некорректные данные в запросе');
      } else if (error.message.includes('429')) {
        console.error('   Превышен лимит запросов, повторите позже');
      }
    }
    
    return null;
  }
}

2. Валидация данных

function validateInvoiceData(data: InvoiceData): string[] {
  const errors: string[] = [];
  
  // Проверка номера отправления
  if (!data.postingNumber.match(/^\d{4}-\d{7}-\d{7}$/)) {
    errors.push('Некорректный формат номера отправления');
  }
  
  // Проверка суммы
  if (data.amount <= 0) {
    errors.push('Сумма должна быть больше 0');
  }
  
  if (data.amount > 999999.99) {
    errors.push('Сумма не может превышать 999,999.99');
  }
  
  // Проверка HS-кодов
  data.hsCodes.forEach((code, index) => {
    if (!code.match(/^\d{10}$/)) {
      errors.push(`HS-код ${index + 1} должен содержать ровно 10 цифр`);
    }
  });
  
  // Проверка номера счёт-фактуры
  if (data.invoiceNumber.length > 50) {
    errors.push('Номер счёт-фактуры не может превышать 50 символов');
  }
  
  return errors;
}

3. Мониторинг и логирование

class InvoiceAuditLogger {
  private logs: Array<{
    timestamp: Date;
    operation: string;
    postingNumber: string;
    status: 'success' | 'error';
    details?: any;
  }> = [];
  
  log(operation: string, postingNumber: string, status: 'success' | 'error', details?: any) {
    const logEntry = {
      timestamp: new Date(),
      operation,
      postingNumber,
      status,
      details
    };
    
    this.logs.push(logEntry);
    
    // Консольное логирование
    const timestamp = logEntry.timestamp.toISOString();
    const statusIcon = status === 'success' ? '' : '';
    console.log(`${statusIcon} [${timestamp}] ${operation} - ${postingNumber}`);
    
    if (details) {
      console.log(`   Детали:`, details);
    }
  }
  
  generateReport() {
    const successful = this.logs.filter(l => l.status === 'success').length;
    const failed = this.logs.filter(l => l.status === 'error').length;
    
    return {
      total: this.logs.length,
      successful,
      failed,
      successRate: this.logs.length > 0 ? successful / this.logs.length : 0,
      recentLogs: this.logs.slice(-10) // последние 10 записей
    };
  }
}

🤝 Связанные API

Supplier API часто используется совместно с другими API для комплексной работы с международными отправлениями:

  • Product API — Управление товарами для экспорта
  • FBS API — Международные FBS отправления
  • Finance API — Финансовые операции и возвраты НДС
  • Returns API — Возвраты международных отправлений

❓ FAQ

Q: Кто может использовать Supplier API?
A: API предназначен специально для продавцов из Турции, участвующих в программе возврата НДС.

Q: Какие файлы можно загружать?
A: Только PDF и JPEG файлы размером до 10 МБ.

Q: Можно ли обновить уже созданную счёт-фактуру?
A: Да, используйте тот же метод createOrUpdateInvoice с теми же данными отправления.

Q: Что такое HS-коды и зачем они нужны?
A: HS-коды — это международные коды товаров для таможенного оформления, необходимые для корректного возврата НДС.

Q: В каких валютах можно создавать счёт-фактуры?
A: Поддерживаются USD, EUR, TRY, CNY, RUB, GBP. По умолчанию — USD.

📞 Поддержка

Нашли ошибку или хотите улучшить документацию?

Полезные ресурсы:


🏠 Главная документация 📚 Все категории