Cancellation API - Управление заявками на отмену заказов
Cancellation API - Управление заявками на отмену заказов
Cancellation API предоставляет функционал для управления заявками на отмену rFBS-заказов, включая их просмотр, подтверждение и отклонение.
Обзор API
Количество методов: 7 (4 устаревших v1 + 3 актуальных v2)
Основные функции: Управление заявками на отмену rFBS-заказов
Статус версий: v1 методы устарели и будут отключены 3 августа 2025 года
⚠️ Важно: Все методы v1 устарели и будут отключены 3 августа 2025 года. Используйте только методы v2.
Методы API
Актуальные методы (v2)
1. Получение списка заявок на отмену (v2)
Метод: getConditionalCancellationListV2()
Эндпоинт: POST /v2/conditional-cancellation/list
Получает список заявок на отмену rFBS-заказов с cursor-based пагинацией и фильтрацией.
Параметры запроса
interface CancellationGetListV2Request {
limit: number; // Количество заявок (максимум 1000)
last_id?: number; // ID последней заявки для пагинации
filters?: {
state?: 'ON_APPROVAL' | 'APPROVED' | 'REJECTED';
cancellation_initiator?: ('CLIENT' | 'SYSTEM')[];
posting_number?: string[]; // Номера отправлений
};
with?: {
counter?: boolean; // Включить счётчик заявок на рассмотрении
};
}
Пример использования
import { OzonSellerApiClient } from '@spacechemical/ozon-seller-api';
const client = new OzonSellerApiClient({
apiKey: 'your-api-key',
clientId: 'your-client-id'
});
// Получить заявки на отмену, ожидающие рассмотрения
const pendingCancellations = await client.cancellation.getConditionalCancellationListV2({
limit: 100,
filters: {
state: 'ON_APPROVAL',
cancellation_initiator: ['CLIENT']
},
with: {
counter: true
}
});
// Обработать заявки
pendingCancellations.result?.forEach(cancellation => {
console.log(`Заявка ${cancellation.cancellation_id}: ${cancellation.posting_number}`);
console.log(`Статус: ${cancellation.state?.state}`);
console.log(`Инициатор: ${cancellation.cancellation_initiator}`);
console.log(`Причина: ${cancellation.cancellation_reason?.name}`);
console.log(`Создана: ${cancellation.cancelled_at}`);
if (cancellation.auto_approve_date) {
console.log(`Автоподтверждение: ${cancellation.auto_approve_date}`);
}
});
console.log(`Заявок на рассмотрении: ${pendingCancellations.counter}`);
// Загрузить следующую страницу
if (pendingCancellations.last_id) {
const nextPage = await client.cancellation.getConditionalCancellationListV2({
limit: 100,
last_id: pendingCancellations.last_id,
filters: { state: 'ON_APPROVAL' }
});
}
2. Подтверждение заявки на отмену (v2)
Метод: approveConditionalCancellationV2()
Эндпоинт: POST /v2/conditional-cancellation/approve
Подтверждает заявку на отмену в статусе ON_APPROVAL. Заказ будет отменён, деньги вернутся покупателю.
Параметры запроса
interface CancellationApproveV2Request {
cancellation_id: number; // ID заявки на отмену
comment: string; // Комментарий к решению
}
Пример использования
// Подтвердить заявку на отмену
await client.cancellation.approveConditionalCancellationV2({
cancellation_id: 12345,
comment: 'Заявка обоснована. Товар действительно повреждён при доставке.'
});
console.log('Заявка подтверждена. Заказ отменён, деньги вернутся покупателю.');
3. Отклонение заявки на отмену (v2)
Метод: rejectConditionalCancellationV2()
Эндпоинт: POST /v2/conditional-cancellation/reject
Отклоняет заявку на отмену в статусе ON_APPROVAL. Заказ останется в прежнем статусе и будет доставлен покупателю.
Параметры запроса
interface CancellationRejectV2Request {
cancellation_id: number; // ID заявки на отмену
comment: string; // Комментарий к решению
}
Пример использования
// Отклонить заявку на отмену
await client.cancellation.rejectConditionalCancellationV2({
cancellation_id: 12345,
comment: 'Товар уже отправлен курьером и будет доставлен в ближайшее время.'
});
console.log('Заявка отклонена. Заказ будет доставлен покупателю.');
Устаревшие методы (v1)
⚠️ Внимание: Все методы v1 будут отключены 3 августа 2025 года. Переходите на v2.
4. Получение списка заявок (v1) - DEPRECATED
Метод: getConditionalCancellationList()
Эндпоинт: POST /v1/conditional-cancellation/list
5. Получение информации о заявке (v1) - DEPRECATED
Метод: getConditionalCancellation()
Эндпоинт: POST /v1/conditional-cancellation/get
6. Подтверждение заявки (v1) - DEPRECATED
Метод: approveConditionalCancellation()
Эндпоинт: POST /v1/conditional-cancellation/approve
7. Отклонение заявки (v1) - DEPRECATED
Метод: rejectConditionalCancellation()
Эндпоинт: POST /v1/conditional-cancellation/reject
Практические сценарии использования
1. Мониторинг заявок на отмену
class CancellationMonitor {
constructor(private client: OzonSellerApiClient) {}
// Получить все заявки, требующие рассмотрения
async getPendingCancellations() {
const cancellations = await this.client.cancellation.getConditionalCancellationListV2({
limit: 1000,
filters: {
state: 'ON_APPROVAL'
},
with: {
counter: true
}
});
return {
items: cancellations.result || [],
totalCount: cancellations.counter || 0
};
}
// Анализ причин отмен
async analyzeCancellationReasons() {
const { items } = await this.getPendingCancellations();
const reasonStats = items.reduce((acc, cancellation) => {
const reason = cancellation.cancellation_reason?.name || 'Неизвестная причина';
acc[reason] = (acc[reason] || 0) + 1;
return acc;
}, {} as Record<string, number>);
console.log('Статистика причин отмен:');
Object.entries(reasonStats).forEach(([reason, count]) => {
console.log(`${reason}: ${count} заявок`);
});
return reasonStats;
}
// Заявки с истекающим сроком автоподтверждения
async getExpiringCancellations(hoursBeforeExpiry: number = 24) {
const { items } = await this.getPendingCancellations();
const expiryThreshold = new Date();
expiryThreshold.setHours(expiryThreshold.getHours() + hoursBeforeExpiry);
return items.filter(cancellation => {
if (!cancellation.auto_approve_date) return false;
const autoApproveDate = new Date(cancellation.auto_approve_date);
return autoApproveDate <= expiryThreshold;
});
}
}
const monitor = new CancellationMonitor(client);
// Проверить заявки, требующие внимания
const expiringCancellations = await monitor.getExpiringCancellations(12); // 12 часов до автоподтверждения
if (expiringCancellations.length > 0) {
console.log(`Внимание! ${expiringCancellations.length} заявок будут автоматически подтверждены в ближайшие 12 часов.`);
expiringCancellations.forEach(cancellation => {
console.log(`Заявка ${cancellation.cancellation_id} (${cancellation.posting_number}) - автоподтверждение: ${cancellation.auto_approve_date}`);
});
}
2. Автоматизация обработки заявок
class CancellationProcessor {
constructor(private client: OzonSellerApiClient) {}
// Автоматическое принятие решений на основе правил
async processAutomaticDecisions() {
const { items } = await this.getPendingCancellations();
const results = {
approved: 0,
rejected: 0,
requiresManualReview: 0
};
for (const cancellation of items) {
const decision = this.evaluateCancellation(cancellation);
if (decision.action === 'approve') {
try {
await this.client.cancellation.approveConditionalCancellationV2({
cancellation_id: cancellation.cancellation_id!,
comment: decision.comment
});
results.approved++;
console.log(`Автоматически подтверждена заявка ${cancellation.cancellation_id}`);
} catch (error) {
console.error(`Ошибка при подтверждении заявки ${cancellation.cancellation_id}:`, error);
}
} else if (decision.action === 'reject') {
try {
await this.client.cancellation.rejectConditionalCancellationV2({
cancellation_id: cancellation.cancellation_id!,
comment: decision.comment
});
results.rejected++;
console.log(`Автоматически отклонена заявка ${cancellation.cancellation_id}`);
} catch (error) {
console.error(`Ошибка при отклонении заявки ${cancellation.cancellation_id}:`, error);
}
} else {
results.requiresManualReview++;
console.log(`Заявка ${cancellation.cancellation_id} требует ручного рассмотрения: ${decision.comment}`);
}
}
return results;
}
private evaluateCancellation(cancellation: any): {
action: 'approve' | 'reject' | 'manual';
comment: string;
} {
const reason = cancellation.cancellation_reason?.name || '';
const initiator = cancellation.cancellation_initiator;
const autoApproveDate = cancellation.auto_approve_date ? new Date(cancellation.auto_approve_date) : null;
// Правило 1: Автоматически подтверждать системные отмены
if (initiator === 'SYSTEM') {
return {
action: 'approve',
comment: 'Автоматическое подтверждение системной отмены'
};
}
// Правило 2: Автоматически подтверждать отмены по качеству товара
if (reason.includes('повреждён') || reason.includes('дефект') || reason.includes('брак')) {
return {
action: 'approve',
comment: 'Подтверждено: проблемы с качеством товара'
};
}
// Правило 3: Отклонять отмены после отправки
if (reason.includes('передумал') || reason.includes('не подходит размер')) {
return {
action: 'reject',
comment: 'Отклонено: товар уже отправлен, изменение решения после отправки'
};
}
// Правило 4: Заявки с истекающим сроком требуют ручного рассмотрения
if (autoApproveDate && autoApproveDate <= new Date(Date.now() + 2 * 60 * 60 * 1000)) { // 2 часа
return {
action: 'manual',
comment: 'Требует срочного ручного рассмотрения - автоподтверждение через 2 часа'
};
}
return {
action: 'manual',
comment: 'Требует индивидуального рассмотрения'
};
}
private async getPendingCancellations() {
const cancellations = await this.client.cancellation.getConditionalCancellationListV2({
limit: 1000,
filters: { state: 'ON_APPROVAL' }
});
return {
items: cancellations.result || []
};
}
}
const processor = new CancellationProcessor(client);
const results = await processor.processAutomaticDecisions();
console.log(`Результаты автоматической обработки:`);
console.log(`Подтверждено: ${results.approved}`);
console.log(`Отклонено: ${results.rejected}`);
console.log(`Требует ручного рассмотрения: ${results.requiresManualReview}`);
3. Пакетная обработка с пагинацией
async function processAllCancellations(
client: OzonSellerApiClient,
processor: (cancellations: any[]) => Promise<void>
) {
let lastId: number | undefined;
let totalProcessed = 0;
const batchSize = 100;
do {
try {
const response = await client.cancellation.getConditionalCancellationListV2({
limit: batchSize,
last_id: lastId,
filters: { state: 'ON_APPROVAL' }
});
const cancellations = response.result || [];
if (cancellations.length === 0) {
break;
}
await processor(cancellations);
totalProcessed += cancellations.length;
lastId = response.last_id;
console.log(`Обработано: ${totalProcessed} заявок`);
// Пауза между запросами для соблюдения rate limits
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error('Ошибка при обработке заявок:', error);
break;
}
} while (lastId);
console.log(`Всего обработано: ${totalProcessed} заявок`);
}
// Использование
await processAllCancellations(client, async (cancellations) => {
for (const cancellation of cancellations) {
// Логика обработки каждой заявки
console.log(`Обрабатывается заявка ${cancellation.cancellation_id}: ${cancellation.posting_number}`);
}
});
Обработка ошибок
Типичные ошибки и их обработка
try {
await client.cancellation.approveConditionalCancellationV2({
cancellation_id: 12345,
comment: 'Подтверждаем отмену'
});
} catch (error) {
if (error.response?.status === 400) {
const errorData = error.response.data;
switch (errorData.code) {
case 'CANCELLATION_NOT_FOUND':
console.error('Заявка на отмену не найдена');
break;
case 'CANCELLATION_ALREADY_PROCESSED':
console.error('Заявка уже обработана');
break;
case 'CANCELLATION_NOT_ON_APPROVAL':
console.error('Заявка не находится в статусе ожидания рассмотрения');
break;
case 'INVALID_COMMENT':
console.error('Некорректный комментарий');
break;
default:
console.error('Неизвестная ошибка:', errorData.message);
}
} else if (error.response?.status === 429) {
console.error('Превышен лимит запросов. Повторите попытку позже.');
} else {
console.error('Произошла ошибка:', error.message);
}
}
Retry-механизм для обработки временных ошибок
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3,
delay: number = 1000
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
if (attempt === maxRetries) {
throw lastError;
}
// Экспоненциальная задержка
const retryDelay = delay * Math.pow(2, attempt - 1);
console.log(`Попытка ${attempt} не удалась. Повтор через ${retryDelay}мс...`);
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
throw lastError!;
}
// Использование с retry
await withRetry(async () => {
return client.cancellation.getConditionalCancellationListV2({
limit: 100,
filters: { state: 'ON_APPROVAL' }
});
}, 3, 1000);
Лучшие практики
1. Мониторинг и алертинг
class CancellationAlerting {
constructor(private client: OzonSellerApiClient) {}
// Проверка критических ситуаций
async checkCriticalSituations() {
const alerts = [];
// Много заявок на рассмотрении
const pendingCount = await this.getPendingCount();
if (pendingCount > 50) {
alerts.push({
type: 'HIGH_PENDING_COUNT',
message: `Высокое количество заявок на рассмотрении: ${pendingCount}`,
severity: 'warning'
});
}
// Заявки с истекающим сроком
const expiringCount = await this.getExpiringCount();
if (expiringCount > 0) {
alerts.push({
type: 'EXPIRING_CANCELLATIONS',
message: `${expiringCount} заявок автоматически подтвердятся в ближайшие 2 часа`,
severity: 'critical'
});
}
return alerts;
}
private async getPendingCount(): Promise<number> {
const response = await this.client.cancellation.getConditionalCancellationListV2({
limit: 1,
filters: { state: 'ON_APPROVAL' },
with: { counter: true }
});
return response.counter || 0;
}
private async getExpiringCount(): Promise<number> {
const response = await this.client.cancellation.getConditionalCancellationListV2({
limit: 1000,
filters: { state: 'ON_APPROVAL' }
});
const now = new Date();
const twoHoursLater = new Date(now.getTime() + 2 * 60 * 60 * 1000);
return (response.result || []).filter(cancellation => {
if (!cancellation.auto_approve_date) return false;
return new Date(cancellation.auto_approve_date) <= twoHoursLater;
}).length;
}
}
2. Логирование и аудит
interface CancellationAuditLog {
timestamp: string;
cancellation_id: number;
posting_number: string;
action: 'approve' | 'reject';
comment: string;
processor: 'automatic' | 'manual';
user_id?: string;
}
class CancellationAuditor {
private auditLogs: CancellationAuditLog[] = [];
async logDecision(
cancellationId: number,
postingNumber: string,
action: 'approve' | 'reject',
comment: string,
processor: 'automatic' | 'manual',
userId?: string
) {
const log: CancellationAuditLog = {
timestamp: new Date().toISOString(),
cancellation_id: cancellationId,
posting_number: postingNumber,
action,
comment,
processor,
user_id: userId
};
this.auditLogs.push(log);
// Отправка в систему логирования
console.log('Audit Log:', JSON.stringify(log));
}
generateReport(startDate: Date, endDate: Date) {
const filteredLogs = this.auditLogs.filter(log => {
const logDate = new Date(log.timestamp);
return logDate >= startDate && logDate <= endDate;
});
return {
total: filteredLogs.length,
approved: filteredLogs.filter(log => log.action === 'approve').length,
rejected: filteredLogs.filter(log => log.action === 'reject').length,
automatic: filteredLogs.filter(log => log.processor === 'automatic').length,
manual: filteredLogs.filter(log => log.processor === 'manual').length,
logs: filteredLogs
};
}
}
Связанные API: Return API (обработка возвратов), Returns API (управление возвратами), Order API (управление заказами), FBS API (логистика)