5

I want to create a new order programatically.

This code works well with simple product,

    $product = get_product($product_id);
    $order = wc_create_order();
    $order->add_product( $product , 1 );
    $order->calculate_totals();
    // assign the order to the current user
    update_post_meta($order->id, '_customer_user', get_current_user_id() );
    // payment_complete
    $order->payment_complete();

but when i use it for subscription product it does not add the subscription, it only add the order.

3 Answers 3

19

Here's my code for creating a subscription -- it took a lot of trial and error to figure it all out. Best of luck!

function create_test_sub() {

    $email = '[email protected]';

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

    // I've used one product with multiple variations

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

    // Each variation also has its own shipping class

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

    // I have some logic for selecting which shipping method to use; your use case will likely be different, so figure out the method you need and store it in $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;

    // As far as I can see, you need to create the order first, then the sub

    $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", 'Imported order', TRUE);

    // Order created, now create sub attached to it -- optional if you're not creating a subscription, obvs

    // Each variation has a different subscription period

    $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'>Sub created! Click here to edit</a>";
}
6
  • Thanks for this, but I can't get it to work. What function is wcs_create_subscription()? It's not defined, and neither is wc_create_subscription() if it's a typo.
    – Pekz0r
    Feb 24, 2016 at 14:09
  • You'll need the Subscriptions extension for that bit to work! (Don't forget you can google function names, they're usually very easy to find because they're so specific.) Feb 25, 2016 at 2:26
  • Yeah, I had the Subscriptions plugin, but it seams like I had an older version(1.5.*). I upgraded to 2.0.* now and the code seams to work. Thanks a lot!
    – Pekz0r
    Feb 25, 2016 at 9:11
  • Cool use-case and example of wc_create_order + wcs_create_subscription! Apr 21, 2016 at 6:34
  • I keep getting Call to undefined method WP_Error::add_product() any idea ?
    – Kravitz
    Sep 9, 2019 at 20:12
0

Here is an example

     $order_data = array(
        'status'        => 'completed',
        'customer_id'   => 1,
        'customer_note' => '',
        'total'         => '',
    );

    $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; // Required, else wc_create_order throws an exception
    $order                  = wc_create_order( $order_data );
1
  • 1
    please try to avoid code only answer without explaining why they solve the issue Dec 25, 2015 at 15:06
0

Unfortunately you must manually handle creating the subscription through code, as just adding an order does not handle it automatically.

Here's my custom function I built based on all the answers I found on SO and digging through subscriptions code base.

Tested with

  • 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 = '' ){
    // First make sure all required functions and classes exist
    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', // Status should be initially set to pending to match how normal checkout process goes
        'billing_period' => WC_Subscriptions_Product::get_period( $product ),
        'billing_interval' => WC_Subscriptions_Product::get_interval( $product )
    ));

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

    // Modeled after WC_Subscriptions_Cart::calculate_subscription_totals()
    $start_date = gmdate( 'Y-m-d H:i:s' );
    // Add product to subscription
    $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();

    // Update order status with custom note
    $note = ! empty( $note ) ? $note : __( 'Programmatically added order and subscription.' );
    $order->update_status( 'completed', $note, true );

    // Also update subscription status to active from pending (and add note)
    $sub->update_status( 'active', $note, true );
    return $sub;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.