Диагностика проблемы дублирующихся лицензий в подписках WooCommerce
При использовании подписок WooCommerce совместно с системой лицензирования плагинов часто возникает ситуация, когда одна и та же лицензия выдается несколько раз одному пользователю. Это приводит к конфликтам при активации плагина и ошибкам обновлений. Основные признаки проблемы:
- В панели администратора WooCommerce в заказах несколько одинаковых лицензионных ключей на одного пользователя.
- При попытке обновить плагин лицензия определяется как уже используемая.
- Отчеты по использованию лицензий показывают избыточное количество активаций с одной лицензии.
Проблема чаще всего связана с тем, как WooCommerce Subscriptions обрабатывает повторяющиеся заказы и их метаданные, а также с отсутствием уникализации ключей при автоматическом продлении.
Причина возникновения дублирующихся лицензий
WooCommerce Subscriptions создает новый заказ при каждом автоматическом продлении. Если генерация лицензионного ключа привязана к заказу, а не к подписке, то при каждом продлении создается новая лицензия. Также часто отсутствует проверка на существование активной лицензии для пользователя или подписки.
Типичные ошибки в коде генерации ключей:
function generate_license_key( $order_id ) {
// Генерация ключа без проверки существующих
$key = strtoupper( wp_generate_password( 16, false ) );
update_post_meta( $order_id, '_license_key', $key );
return $key;
}Этот код всегда создает новый ключ для каждого заказа без учета подписки.
Пошаговое решение: уникализация лицензий по подписке
Чтобы избежать дублирования, нужно связать лицензию с подпиской, а не с каждым заказом. Для этого необходимо:
- Получить ID подписки из заказа.
- Проверить, есть ли уже лицензия, связанная с этой подпиской.
- Если лицензия есть — вернуть ее, если нет — создать новую.
Пример исправленного кода:
function get_or_create_license_key_by_subscription( $order_id ) {
if ( ! class_exists( 'WC_Subscriptions_Manager' ) ) {
return false; // Subscriptions не активен
}
// Получаем подписки, связанные с заказом
$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) );
if ( empty( $subscriptions ) ) {
return false;
}
foreach ( $subscriptions as $subscription ) {
$subscription_id = $subscription->get_id();
$license_key = get_post_meta( $subscription_id, '_license_key', true );
if ( $license_key ) {
return $license_key; // Лицензия уже есть
} else {
$license_key = strtoupper( wp_generate_password( 16, false ) );
update_post_meta( $subscription_id, '_license_key', $license_key );
return $license_key;
}
}
return false;
}Этот код гарантирует, что лицензия создается один раз для подписки, а не для каждого заказа.
Как внедрить решение в процесс покупки и обновлений
Вызов функции get_or_create_license_key_by_subscription необходимо интегрировать в хуки, связанные с обработкой заказа и активацией лицензии, например:
add_action( 'woocommerce_order_status_completed', 'process_license_on_subscription_order', 20, 1 );
function process_license_on_subscription_order( $order_id ) {
$license_key = get_or_create_license_key_by_subscription( $order_id );
if ( $license_key ) {
// Сохраняем или отправляем лицензию пользователю
// Например, добавим в email или мета пользователя
}
}Проверка результата после внедрения
- Создайте новую подписку и проверьте, что сгенерирован один лицензионный ключ, привязанный к подписке (проверьте мета _license_key у поста-субскрипшена).
- При автоматическом продлении заказа убедитесь, что ключ не меняется и не создается дубликат.
- Проверьте в админке WooCommerce, что в заказах нет повторяющихся ключей.
- Протестируйте активацию плагина с этим ключом, чтобы убедиться, что он работает корректно.
Частые ошибки и как их исправить
- Генерация ключей на каждый заказ: исправить, привязав ключ к подписке, как описано выше.
- Отсутствие проверки класса WC_Subscriptions: нужен
class_exists, чтобы избежать ошибок, если плагин подписок отключен. - Ключи хранятся в мета заказов, а не подписок: храните ключи в мета подписок для единой точки контроля.
- Отсутствие обработки автопродления: используйте хук
woocommerce_order_status_completed, который срабатывает и при обновлениях заказов подписки.
Практические советы по безопасности и производительности
- Не храните лицензионные ключи в открытом виде в пользовательских метаданных — используйте защиту доступа к метаданным или шифрование, если это необходимо.
- Кэшируйте результаты функции получения ключа, если она вызывается часто.
- Внедрите логирование генерации ключей для отладки и аудита.
- Если используете REST API для проверки лицензий, ограничьте доступ с помощью nonce и прав пользователя.
Сравнение вариантов хранения лицензий
| Вариант | Где хранится ключ | Плюсы | Минусы |
|---|---|---|---|
| В мета заказов | postmeta заказов | Просто реализовать | Дубликаты ключей при продлении |
| В мета подписок | postmeta подписок (WC_Subscription) | Уникальность ключа, удобство управления | Нужна интеграция с WC Subscriptions API |
| Отдельная таблица базы | Собственная таблица лицензий | Максимальный контроль, масштабируемость | Сложнее реализовать, требует миграций |