This is a useful way to provide custom purchase funnels on remote site(s) that fulfill on the main site, particularly for subscription management. Noting that Woo Subscriptions has some complex ways of processing coupons and to expect some finagling to get discounts to work.
Remote site code snippet. Include read/write Woo REST API keys to the main site:
add_action( 'woocommerce_thankyou', function( $order_id ) {
// Settings
$url = 'https://main-site.com/wp-json/wc-ccom/v1/orderin';
$auth = 'ck_##########:cs_##########';
// Assemble Payload
$order = wc_get_order( $order_id );
$data = (object) [
'created_via' => $_SERVER['HTTP_HOST'],
'order_number' => $order->get_id(),
'order_number_formatted' => 'PREFIX-' . $order->get_id(),
'billing_address_1' => $order->get_billing_address_1(),
'billing_address_2' => $order->get_billing_address_2(),
'billing_city' => $order->get_billing_city(),
'billing_company' => $order->get_billing_company(),
'billing_country' => $order->get_billing_country(),
'billing_email' => $order->get_billing_email(),
'billing_first_name' => $order->get_billing_first_name(),
'billing_last_name' => $order->get_billing_last_name(),
'billing_phone' => $order->get_billing_phone(),
'billing_postcode' => $order->get_billing_postcode(),
'billing_state' => $order->get_billing_state(),
'shipping_address_1' => $order->get_shipping_address_1(),
'shipping_address_2' => $order->get_shipping_address_2(),
'shipping_city' => $order->get_shipping_city(),
'shipping_company' => $order->get_shipping_company(),
'shipping_country' => $order->get_shipping_country(),
'shipping_first_name' => $order->get_shipping_first_name(),
'shipping_last_name' => $order->get_shipping_last_name(),
'shipping_postcode' => $order->get_shipping_postcode(),
'shipping_state' => $order->get_shipping_state(),
'line_items' => [],
];
// Add Line Items To Payload
foreach( $order->get_items() as $item_id => $item ) {
$data->line_items[] = (object) [
'name' => $item->get_name(),
'quantity' => $item->get_quantity(),
'subtotal' => $item->get_subtotal(),
'total' => $item->get_total(),
];
}
// Transmit
$args = [
'headers' => [
'Authorization' => 'Basic ' . $auth,
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'body' => json_encode( $data ),
'timeout' => 30,
];
$response = wp_remote_post( $url, $args );
// Handle Success
$response_body = json_decode( wp_remote_retrieve_body( $response ) );
if( wp_remote_retrieve_response_code( $response ) === 200 ) {
if( isset( $response_body->redirect_url ) ) {
wp_redirect( $response_body->redirect_url );
exit;
}
}
// Handle Error
if( isset( $response_body->error_message ) ) {
printf( '<p><strong style="color:red;">%s</strong></p>', $response_body->error_message );
return;
}
} );
The following code snippets sit on the main site receiving the order:
Support custom order numbering:
add_filter( 'woocommerce_order_number', function( $order_id, $order ) {
$order = wc_get_order( $order_id );
if( ! $order ) {
return $order_id;
}
return $order->get_meta( '_order_number_formatted' )
? $order->get_meta( '_order_number_formatted' )
: $order_id;
}, 10, 2 );
REST API order receiver:
// Settings
function wc_ccom_orderin_get_pid( $title ) {
$product_ids = [
'Product Title' => 123456,
];
return isset( $product_ids[$title] )
? $product_ids[$title] : false;
}
// REST API Route
add_action( 'rest_api_init', function() {
register_rest_route(
'wc-ccom/v1',
'/orderin',
[
'methods' => 'POST',
'callback' => 'wc_ccom_orderin',
'permission_callback' => current_user_can( 'manage_woocommerce' ),
]
);
} );
// REST API Function
function wc_ccom_orderin( $request ) {
// Parameters
$params = (object) $request->get_params();
// Start Responder
$response = new WP_REST_Response();
// Log Request
$logger = wc_get_logger();
$logger->info( print_r( $params, true ), [ 'source' => 'wc_ccom_orderin' ] );
// Check Preexistence
$args = [
'meta_key' => '_order_number',
'meta_value' => $params->order_number,
'meta_compare' => '=',
'return' => 'ids',
];
$orders = wc_get_orders( $args );
if( $orders ) {
$response->set_status( 400 );
$response->set_data( (object) [
'error_message' => sprintf(
'Order number %d preexists as %d',
$params->order_number,
$orders[0]
),
] );
return $response;
}
// Get Customer ID And Login
$user = get_user_by( 'email', $params->billing_email );
if( $user ) {
$user_id = $user->ID;
} else {
$user_id = wp_create_user(
$params->billing_email,
wp_generate_password(),
$params->billing_email
);
}
// Billing Address
$address_billing = [
'address_1' => $params->billing_address_1,
'address_2' => $params->billing_address_2,
'city' => $params->billing_city,
'company' => $params->billing_company,
'country' => $params->billing_country,
'email' => $params->billing_email,
'first_name' => $params->billing_first_name,
'last_name' => $params->billing_last_name,
'phone' => $params->billing_phone,
'postcode' => $params->billing_postcode,
'state' => $params->billing_state,
];
// Shipping Address
$address_shipping = [
'address_1' => $params->shipping_address_1,
'address_2' => $params->shipping_address_2,
'city' => $params->shipping_city,
'company' => $params->shipping_company,
'country' => $params->shipping_country,
'first_name' => $params->shipping_first_name,
'last_name' => $params->shipping_last_name,
'postcode' => $params->shipping_postcode,
'state' => $params->shipping_state,
];
// Create Pending Order For Customer
$order = wc_create_order();
$order->set_customer_user_agent( $params->created_via );
$order->set_status( 'pending' );
$order->set_customer_id( $user_id );
$order->update_meta_data( '_order_number', $params->order_number );
$order->update_meta_data( '_order_number_formatted', $params->order_number_formatted );
$order->set_address( $address_billing, 'billing' );
$order->set_address( $address_shipping, 'shipping' );
// Loop Line Items
$subtotal = 0;
foreach( $params->line_items as $item ) {
$item = (object) $item;
// Map Product To This Site
$product_id = wc_ccom_orderin_get_pid( $item->name );
$product = $product_id ? wc_get_product( $product_id ) : false;
// Handle Errors
if( ! $product_id || ! $product ) {
$response->set_status( 400 );
$response->set_data( (object) [
'error_message' => 'Unable to match product by title: '
. $item->name
] );
return $response;
}
// Create Subscription
$args = [
'customer_id' => $user_id,
'order_id' => $order->get_id(),
'status' => 'pending',
'billing_period' => WC_Subscriptions_Product::get_period( $product ),
'billing_interval' => WC_Subscriptions_Product::get_interval( $product )
];
$subscription = wcs_create_subscription( $args );
// Handle Errors
if( is_wp_error( $subscription ) ) {
$response->set_status( 400 );
$response->set_data( (object) [ 'error_message' => $subscription->get_error_message() ] );
return $response;
}
// Set Addresses
$subscription->set_address( $address_billing, 'billing' );
$subscription->set_address( $address_shipping, 'shipping' );
// Set Totals
// Adding Prices Here Isn't Obeyed By Subscriptions Order Pay Page
$subtotal += (float) $item->total;
$order->add_product( $product, $item->quantity, [ 'total' => $item->total ] );
$subscription->add_product( $product, $item->quantity );
// Set Discount (Negative Fee) For Ongoing Discounts
/*
$differential = (float) -1 * ( $product->get_price() - $item->subtotal );
if( $differential ) {
$fee = new WC_Order_Item_Fee();
$fee->set_name( 'Discount on ' . $item->name );
$fee->set_amount( $differential );
$fee->set_tax_status( 'taxable' );
$fee->set_total( $differential );
$order->add_item( $fee );
$subscription->add_item( $fee );
}
*/
// Set Dates
$start_date = gmdate( 'Y-m-d H:i:s' );
$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
),
);
$subscription->update_dates( $dates );
$subscription->calculate_totals();
$subscription->save();
}
// Set Parent Order Coupon (One-Time Or Ongoing)
$coupon_code = '';
if( $coupon_code ) {
$order->apply_coupon( $coupon_code );
}
// Save Parent Order
$order->calculate_totals();
$order->save();
// Order Note
$order->add_order_note( 'This order was created by remote site API call.' );
// Log Them In
wp_set_current_user( $user_id, $user_login );
wp_set_auth_cookie( $user_id );
//do_action( 'wp_login', $user_login );
// API Reply
$response->set_status( 200 );
$response->set_data( (object) [ 'redirect_url' => $order->get_checkout_payment_url() ] );
return $response;
}
Send order complete signal to remote site upon parent order paid. Include read/write Woo REST API keys to the main site:
add_action( 'woocommerce_thankyou', function( $order_id ) {
// Settings
$url = 'https://remote-site.com/wp-json/wc/v3/orders/';
$auth = 'ck_##########:cs_##########';
// Get Order Source ID
$order = wc_get_order( $order_id );
$order_number = $order->get_meta( '_order_number', true );
$order_number_formatted = $order->get_meta( '_order_number_formatted', true );
if( substr( $order_number_formatted, 0, 6 ) !== 'PREFIX' ) {
return;
}
// Transmit
$args = [
'headers' => [
'Authorization' => 'Basic ' . base64_encode( $auth ),
'Accept' => 'application/json',
'Content-Type' => 'application/json',
],
'body' => json_encode( [ 'status' => 'completed' ] ),
'method' => 'PUT',
'timeout' => 30,
];
$response = wp_remote_request( $url . $order_number, $args );
} );