Cómo usar wc_create_order con un producto de suscripción

16 sept 2015, 17:39:11
Vistas: 24.8K
Votos: 5

Quiero crear una nueva orden de forma programática.

Este código funciona bien con productos simples,

    $product = get_product($product_id);
    $order = wc_create_order();
    $order->add_product( $product , 1 );
    $order->calculate_totals();
    // asignar la orden al usuario actual
    update_post_meta($order->id, '_customer_user', get_current_user_id() );
    // completar pago
    $order->payment_complete();

pero cuando lo uso para un producto de suscripción no añade la suscripción, solo añade la orden.

0
Todas las respuestas a la pregunta 3
6
19

Aquí está mi código para crear una suscripción -- me costó muchos intentos y errores resolverlo. ¡Mucha suerte!

function create_test_sub() {

    $email = 'test@test.com';

    $start_date = '2015-01-01 00:00:00';

    $address = array(
        'first_name' => 'Jeremy',
        'last_name'  => 'Test',
        'company'    => '',
        'email'      => $email,
        'phone'      => '777-777-777-777',
        'address_1'  => '31 Main Street',
        'address_2'  => '', 
        'city'       => 'Auckland',
        '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 );

    // He usado un producto con múltiples variaciones

    $parent_product = wc_get_product(22998);

    $args = array(
        'attribute_billing-period' => 'Yearly',
        'attribute_subscription-type' => 'Both'
    );

    $product_variation = $parent_product->get_matching_variation($args);

    $product = wc_get_product($product_variation);  

    // Cada variación también tiene su propia clase de envío

    $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();

    // Tengo alguna lógica para seleccionar qué método de envío usar; tu caso probablemente será diferente, así que determina el método que necesitas y guárdalo en $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;

    // Por lo que veo, necesitas crear primero el pedido y luego la suscripción

    $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", 'Pedido importado', TRUE);

    // Pedido creado, ahora crea la suscripción asociada -- opcional si no estás creando una suscripción, obviamente

    // Cada variación tiene un periodo de suscripción diferente

    $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'>¡Suscripción creada! Haz clic aquí para editar</a>";
}
10 feb 2016 01:07:01
Comentarios

Gracias por esto, pero no logro que funcione. ¿Qué función es wcs_create_subscription()? No está definida, y tampoco lo está wc_create_subscription() si es un error tipográfico.

Pekz0r Pekz0r
24 feb 2016 16:09:55

¡Necesitarás la extensión de Subscriptions para que esa parte funcione! (No olvides que puedes buscar en Google los nombres de las funciones, normalmente son muy fáciles de encontrar porque son muy específicos.)

Jeremy Warne Jeremy Warne
25 feb 2016 04:26:12

Sí, tenía el plugin de Subscriptions, pero parece que tenía una versión anterior (1.5.). Actualicé a la versión 2.0. ahora y el código parece funcionar. ¡Muchas gracias!

Pekz0r Pekz0r
25 feb 2016 11:11:20

¡Excelente caso de uso y ejemplo de wc_create_order + wcs_create_subscription!

Scott Kingsley Clark Scott Kingsley Clark
21 abr 2016 09:34:52

Sigo recibiendo Call to undefined method WP_Error::add_product() ¿alguna idea?

Kravitz Kravitz
9 sept 2019 23:12:22

@Kravitz revisa en qué línea está el error, y haz if (is_wp_error($order) var_dump($order) o la misma línea con $sub en lugar de $order, antes de la llamada a add_product(). Eso te dirá qué está fallando.

Jeremy Warne Jeremy Warne
10 sept 2019 03:03:35
Mostrar los 1 comentarios restantes
1

Aquí hay un ejemplo

     $order_data = array(
        'status'        => 'completed', // Estado del pedido
        'customer_id'   => 1, // ID del cliente
        'customer_note' => '', // Nota del cliente
        'total'         => '', // Total
    );

    $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // Requerido, de lo contrario wc_create_order lanza una excepción
    $order                  = wc_create_order( $order_data );
25 dic 2015 15:55:44
Comentarios

por favor, intenta evitar respuestas que solo incluyan código sin explicar por qué resuelven el problema

Mark Kaplun Mark Kaplun
25 dic 2015 17:06:53
0

Desafortunadamente, debes manejar manualmente la creación de la suscripción mediante código, ya que simplemente agregar un pedido no lo maneja automáticamente.

Aquí está mi función personalizada que construí basándome en todas las respuestas que encontré en SO y revisando el código base de suscripciones.

Probado con

  • 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 = '' ){
    // Primero verifica que todas las funciones y clases requeridas existan
    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', // El estado debe establecerse inicialmente como pendiente para coincidir con el proceso normal de pago
        'billing_period' => WC_Subscriptions_Product::get_period( $product ),
        'billing_interval' => WC_Subscriptions_Product::get_interval( $product )
    ));

    if( is_wp_error( $sub ) ){
        return false;
    }

    // Modelado según WC_Subscriptions_Cart::calculate_subscription_totals()
    $start_date = gmdate( 'Y-m-d H:i:s' );
    // Agrega producto a la suscripción
    $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();

    // Actualiza el estado del pedido con nota personalizada
    $note = ! empty( $note ) ? $note : __( 'Pedido y suscripción agregados programáticamente.' );
    $order->update_status( 'completed', $note, true );

    // También actualiza el estado de la suscripción a activo desde pendiente (y agrega nota)
    $sub->update_status( 'active', $note, true );
    return $sub;
}
14 nov 2019 20:41:15