как использовать wc_create_order с подписочным продуктом
Я хочу программно создать новый заказ.
Этот код хорошо работает с простым продуктом,
$product = get_product($product_id);
$order = wc_create_order();
$order->add_product( $product , 1 );
$order->calculate_totals();
// назначаем заказ текущему пользователю
update_post_meta($order->id, '_customer_user', get_current_user_id() );
// завершение оплаты
$order->payment_complete();
но когда я использую его для подписочного продукта, он не добавляет подписку, а создает только заказ.

Вот мой код для создания подписки — потребовалось много проб и ошибок, чтобы во всем разобраться. Удачи!
function create_test_sub() {
$email = 'test@test.com';
$start_date = '2015-01-01 00:00:00';
$address = array(
'first_name' => 'Джереми',
'last_name' => 'Тест',
'company' => '',
'email' => $email,
'phone' => '777-777-777-777',
'address_1' => '31 Main Street',
'address_2' => '',
'city' => 'Окленд',
'state' => 'AKL',
'postcode' => '12345',
'country' => 'AU'
);
$default_password = wp_generate_password();
if (!$user = get_user_by('login', $email)) $user = wp_create_user( $email, $default_password, $email );
// Я использовал один товар с несколькими вариациями
$parent_product = wc_get_product(22998);
$args = array(
'attribute_billing-period' => 'Ежегодно',
'attribute_subscription-type' => 'Оба'
);
$product_variation = $parent_product->get_matching_variation($args);
$product = wc_get_product($product_variation);
// Каждая вариация также имеет свой собственный класс доставки
$shipping_class = get_term_by('slug', $product->get_shipping_class(), 'product_shipping_class');
WC()->shipping->load_shipping_methods();
$shipping_methods = WC()->shipping->get_shipping_methods();
// У меня есть логика для выбора способа доставки; ваш случай, скорее всего, будет другим, поэтому определите нужный метод и сохраните его в $selected_shipping_method
$selected_shipping_method = $shipping_methods['free_shipping'];
$class_cost = $selected_shipping_method->get_option('class_cost_' . $shipping_class->term_id);
$quantity = 1;
// Насколько я понимаю, сначала нужно создать заказ, затем подписку
$order = wc_create_order(array('customer_id' => $user->id));
$order->add_product( $product, $quantity, $args);
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->add_shipping((object)array (
'id' => $selected_shipping_method->id,
'label' => $selected_shipping_method->title,
'cost' => (float)$class_cost,
'taxes' => array(),
'calc_tax' => 'per_order'
));
$order->calculate_totals();
$order->update_status("completed", 'Импортированный заказ', TRUE);
// Заказ создан, теперь создаем подписку, привязанную к нему — опционально, если вы не создаете подписку
// Каждая вариация имеет разный период подписки
$period = WC_Subscriptions_Product::get_period( $product );
$interval = WC_Subscriptions_Product::get_interval( $product );
$sub = wcs_create_subscription(array('order_id' => $order->id, 'billing_period' => $period, 'billing_interval' => $interval, 'start_date' => $start_date));
$sub->add_product( $product, $quantity, $args);
$sub->set_address( $address, 'billing' );
$sub->set_address( $address, 'shipping' );
$sub->add_shipping((object)array (
'id' => $selected_shipping_method->id,
'label' => $selected_shipping_method->title,
'cost' => (float)$class_cost,
'taxes' => array(),
'calc_tax' => 'per_order'
));
$sub->calculate_totals();
WC_Subscriptions_Manager::activate_subscriptions_for_order($order);
print "<a href='/wp-admin/post.php?post=" . $sub->id . "&action=edit'>Подписка создана! Нажмите здесь для редактирования</a>";
}

Спасибо за это, но у меня не получается заставить это работать. Что это за функция wcs_create_subscription()
? Она не определена, как и wc_create_subscription()
, если это опечатка.

Для работы этой части вам понадобится расширение Subscriptions! (Не забывайте, что вы можете погуглить названия функций - их обычно очень легко найти, потому что они очень специфичны.)

Да, у меня был плагин Subscriptions, но, похоже, у меня была старая версия (1.5.). Я обновился до 2.0., и теперь код, кажется, работает. Большое спасибо!

Отличный пример использования wc_create_order + wcs_create_subscription!

У меня постоянно возникает ошибка Call to undefined method WP_Error::add_product()
, есть идеи в чем дело?

Вот пример:
$order_data = array(
'status' => 'completed', // Статус заказа
'customer_id' => 1, // ID покупателя
'customer_note' => '', // Примечание покупателя
'total' => '', // Общая сумма
);
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // Обязательно, иначе wc_create_order выбросит исключение
$order = wc_create_order( $order_data ); // Создание заказа

К сожалению, вам придется вручную обрабатывать создание подписки через код, так как простое добавление заказа не обрабатывает это автоматически.
Вот моя пользовательская функция, которую я создал на основе всех ответов, найденных на Stack Overflow, и изучения кодовой базы подписок.
Протестировано с:
- WordPress 5.2.5
- WooCommerce 3.8.0
- WooCommerce Subscriptions 2.6.1
https://gist.github.com/tripflex/a3123052f36daf18f7cb05391d752223
function give_user_subscription( $product, $user_id, $note = '' ){
// Сначала проверяем существование всех необходимых функций и классов
if( ! function_exists( 'wc_create_order' ) || ! function_exists( 'wcs_create_subscription' ) || ! class_exists( 'WC_Subscriptions_Product' ) ){
return false;
}
$order = wc_create_order( array( 'customer_id' => $user_id ) );
if( is_wp_error( $order ) ){
return false;
}
$user = get_user_by( 'ID', $user_id );
$fname = $user->first_name;
$lname = $user->last_name;
$email = $user->user_email;
$address_1 = get_user_meta( $user_id, 'billing_address_1', true );
$address_2 = get_user_meta( $user_id, 'billing_address_2', true );
$city = get_user_meta( $user_id, 'billing_city', true );
$postcode = get_user_meta( $user_id, 'billing_postcode', true );
$country = get_user_meta( $user_id, 'billing_country', true );
$state = get_user_meta( $user_id, 'billing_state', true );
$address = array(
'first_name' => $fname,
'last_name' => $lname,
'email' => $email,
'address_1' => $address_1,
'address_2' => $address_2,
'city' => $city,
'state' => $state,
'postcode' => $postcode,
'country' => $country,
);
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->add_product( $product, 1 );
$sub = wcs_create_subscription(array(
'order_id' => $order->get_id(),
'status' => 'pending', // Статус должен быть изначально установлен в pending, чтобы соответствовать обычному процессу оформления заказа
'billing_period' => WC_Subscriptions_Product::get_period( $product ),
'billing_interval' => WC_Subscriptions_Product::get_interval( $product )
));
if( is_wp_error( $sub ) ){
return false;
}
// Смоделировано по WC_Subscriptions_Cart::calculate_subscription_totals()
$start_date = gmdate( 'Y-m-d H:i:s' );
// Добавляем продукт в подписку
$sub->add_product( $product, 1 );
$dates = array(
'trial_end' => WC_Subscriptions_Product::get_trial_expiration_date( $product, $start_date ),
'next_payment' => WC_Subscriptions_Product::get_first_renewal_payment_date( $product, $start_date ),
'end' => WC_Subscriptions_Product::get_expiration_date( $product, $start_date ),
);
$sub->update_dates( $dates );
$sub->calculate_totals();
// Обновляем статус заказа с пользовательской заметкой
$note = ! empty( $note ) ? $note : __( 'Заказ и подписка добавлены программно.' );
$order->update_status( 'completed', $note, true );
// Также обновляем статус подписки с pending на active (и добавляем заметку)
$sub->update_status( 'active', $note, true );
return $sub;
}
