Kemp.rest / Examples / Code / Merged order history
KEMP.REST / EXAMPLES / CODE
Merged order history
Written by Kevin Kemp in PHP. JSON REST and WooCommerce account templates, for a proprietary back office .NET points system.
View the codeA customer could have orders in WooCommerce and older or parallel orders in the .NET back office. My Account showed only the local ones. A visit from an ad or an affiliate also dropped its referrer and UTM values before checkout. This file builds one order list and keeps those visit parameters on the order.
Type of code
WordPress and WooCommerce functions. One part replaces the My Account orders screen. Another stores marketing parameters from the landing URL onto the order.
Technology
PHP, WooCommerce account hooks and order objects, the back office JSON REST order API (/V2/Order?EmailOrMemberId=) with a bearer token from Advanced Custom Fields, the WooCommerce orders template, PHP sessions, and order meta. The API host in the sample is api.example.com. The access token is not in the file.
What it does
- Adds the customer phone number to formatted billing and shipping addresses.
- Replaces the My Account orders endpoint. It loads this user’s WooCommerce orders and the same person’s back office orders, ten at a time, using account email, then billing email, then shipping email.
- Where both sides share an order id, the back office shipment packages are attached to the WooCommerce row and the duplicate is dropped. WooCommerce-only orders older than a week stay off the merged list. The combined list is sorted newest first.
- If the back office responds with HTTP 500, the page shows WooCommerce orders only.
- A referrer saved in the session is written onto a new order and shown under the billing address in wp-admin.
- Landing parameters are kept in the PHP session and saved on the thank-you page: referrer, affiliate id, UTM fields, source id, coupon code, and promo code.
Problem it solved
One My Account page showed store orders and back office orders together, with shipment detail on the matching rows. Affiliate and campaign values from the landing URL were still on the order after checkout.
Code
Back to summary · woo_functions.php
<?php
if ( ! defined( 'BACKOFFICE_API_BASE_URL' ) ) {
define('BACKOFFICE_API_BASE_URL','https://api.example.com');
}
add_filter( 'woocommerce_my_account_my_address_formatted_address', function( $args, $customer_id, $name ){
$args['phone'] = get_user_meta( $customer_id, $name . '_phone', true );
return $args;
}, 10, 3 );
add_filter( 'woocommerce_localisation_address_formats', function( $formats ){
foreach ( $formats as $key => &$format ) {
$format .= "\n{phone}";
}
return $formats;
} );
add_filter( 'woocommerce_formatted_address_replacements', function( $replacements, $args ){
$replacements['{phone}'] = $args['phone'];
return $replacements;
}, 10, 2 );
function compare_by_date($a, $b) {
// Convert the date strings to timestamps
$a_date = strtotime($a['date']);
$b_date = strtotime($b['date']);
// Compare the timestamps
if ($a_date == $b_date) {
return 0; // Equal
} else if ($a_date > $b_date) {
return -1; // $a is newer than $b
} else {
return 1; // $a is older than $b
}
}
//---------------------------------------- order History
//add_filter( 'woocommerce_my_account_my_orders_query', 'change_my_orders_query', 10, 1 );
function change_my_orders_query( $query ) {
$date = new DateTime();
$date->modify( '-1 week' );
$query['date_query'] = array(
'after' => $date->format( 'Y-m-d' )
);
return $query;
}
//add_filter( 'woocommerce_my_account_my_orders_columns', 'add_office_order_columns', 10, 1 );
function add_office_order_columns( $columns ) {
$columns['office_order_status'] = __( 'Back Office Order Status', 'textdomain' );
return $columns;
}
//add_action( 'woocommerce_account_orders_endpoint', 'output_office_orders', 10, 0 );
function output_office_orders() {
$current_user_id = get_current_user_id();
$args = array(
'customer_id' => $current_user_id,
'limit' => -1, // to retrieve _all_ orders by this user
);
$customer_orders = wc_get_orders($args);
//$access_token = get_user_meta($current_user_id, 'auth0_access_token',true);
$access_token = get_field('access_token','option');
$user_info = get_userdata($current_user_id);
$email_user = $user_info->user_email;
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$limit = 10;
$api_url = BACKOFFICE_API_BASE_URL.'/V2/Order?EmailOrMemberId='.$email_user.'&Offset=' . $offset . '&Limit=' . $limit;
$response = wp_remote_get($api_url, array(
'headers' => array('Authorization' => 'Bearer ' . $access_token)
));
if ( is_wp_error( $response ) ) {
echo '<p>' . __( 'There was an error fetching the back office orders. Please try again later.', 'textdomain' ) . '</p>';
return;
}
$response_body = wp_remote_retrieve_body($response);
$orders = json_decode( $response_body, true );
if ( ! isset( $orders ) || ! is_array( $orders ) ) {
echo '<p>' . __( 'There was an error parsing the back office orders. Please try again later.', 'textdomain' ) . '</p>';
return;
}
// $orders = json_decode( $response_body, true );
echo '<h2>' . __( 'Back Office Orders', 'textdomain' ) . '</h2>';
echo '<table class="woocommerce-orders-table woocommerce-MyAccount-orders shop_table shop_table_responsive my_account_orders account-orders-table">';
echo '<thead><tr>';
echo '<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-number"><span class="nobr">' . __( 'Order Number', 'textdomain' ) . '</span></th>';
echo '<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-date"><span class="nobr">' . __( 'Order Date', 'textdomain' ) . '</span></th>';
echo '<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-status"><span class="nobr">' . __( 'Order Status', 'textdomain' ) . '</span></th>';
echo '<th class="woocommerce-orders-table__header woocommerce-orders-table__header-order-total"><span class="nobr">' . __( 'Order Total', 'textdomain' ) . '</span></th>';
echo '</tr></thead>';
echo '<tbody>';
foreach ( $orders as $order ) {
echo '<tr class="woocommerce-orders-table__row woocommerce-orders-table__row--status-' . esc_attr( $order['status'] ) . ' order">';
echo '<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-number" data-title="' . esc_attr__( 'Order Number', 'textdomain' ) . '">';
echo '<a href="' . esc_url( $order['url'] ) . '">' . esc_html( $order['orderId'] ) . '</a>';
echo '</td>';
echo '<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-date" data-title="' . esc_attr__( 'Order Date', 'textdomain' ) . '">';
echo '<time datetime="' . esc_attr( $order['orderDate'] ) . '" title="' . esc_attr( $order['orderDate'] ) . '">' . esc_html( $order['orderDate'] ) . '</time>';
echo '</td>';
echo '<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-status" data-title="' . esc_attr__( 'Order Status', 'textdomain' ) . '" style="text-align:left; white-space:nowrap;">';
echo '<mark class="order-status ' . esc_attr( sanitize_html_class( 'status-' . $order['orderStatus'] ) ) . '"><span>' . esc_html( $order['orderStatus'] ) . '</span></mark>';
echo '</td>';
echo '<td class="woocommerce-orders-table__cell woocommerce-orders-table__cell-order-total" data-title="' . esc_attr__( 'Order Total', 'textdomain' ) . '">';
echo '<span class="woocommerce-Price-amount amount">' . esc_html( $order['orderTotal'] ) . '</span>';
echo '</td>';
echo '</tr>';
}
echo '</tbody>';
echo '</table>';
echo '<div class="woocommerce-pagination woocommerce-pagination--without-numbers woocommerce-Pagination">';
echo '<a class="woocommerce-button woocommerce-button--previous woocommerce-Button woocommerce-Button--previous button" href="' . esc_url( add_query_arg( 'offset', max( 0, $offset - $limit ) ) ) . '">' . __( 'Previous', 'textdomain' ) . '</a>';
echo '<a class="woocommerce-button woocommerce-button--next woocommerce-Button woocommerce-Button--next button" href="' . esc_url( add_query_arg( 'offset', $offset + $limit ) ) . '">' . __( 'Next', 'textdomain' ) . '</a>';
echo '</div>';
}
remove_action( 'woocommerce_account_orders_endpoint', 'woocommerce_account_orders' );
add_action( 'woocommerce_account_orders_endpoint', 'backoffice_woocommerce_account_orders' );
function backoffice_woocommerce_account_orders( $current_page ) {
$current_page = empty( $current_page ) ? 1 : absint( $current_page );
$current_user_id = get_current_user_id();
$args = array(
'customer_id' => $current_user_id,
'limit' => -1, // to retrieve _all_ orders by this user
);
$customer_orders = wc_get_orders($args);
//$access_token = get_user_meta($current_user_id, 'auth0_access_token',true);
$access_token = get_field('access_token','option');
$user_info = get_userdata($current_user_id);
$billing_email = get_user_meta($current_user_id, 'billing_email' , true);
$shipping_email = get_user_meta($current_user_id, 'shipping_email' , true);
$email_user = $user_info->user_email;
if($email_user) {
$email_order = $email_user;
}elseif($billing_email) {
$email_order = $billing_email;
}elseif($shipping_email) {
$email_order = $shipping_email;
}
$offset = isset($_GET['offset']) ? $_GET['offset'] : 0;
$limit = 10;
$api_url = BACKOFFICE_API_BASE_URL.'/V2/Order?EmailOrMemberId='.$email_order.'&Offset=' . $offset . '&Limit=' . $limit;
$response = wp_remote_get($api_url, array(
'headers' => array('Authorization' => 'Bearer ' . $access_token)
));
if ( is_wp_error( $response ) ) {
echo '<p>' . __( 'There was an error fetching orders. Please try again later.', 'textdomain' ) . '</p>';
return;
}
$status_code = wp_remote_retrieve_response_code( $response );
if ( $status_code == 500 ) {
$c_orders = array();
foreach ($customer_orders as $customer_order){
$order = wc_get_order( $customer_order );
$order_id = $order->id;
$order_id_prefix = $order->get_order_number();
$c_orders[] = array(
'id' => $order_id,
'order_id_prefix' => $order_id_prefix,
'view_url' => $order->get_view_order_url(),
'item_count' => $order->get_item_count() - $order->get_item_count_refunded(),
'date' => wc_format_datetime( $order->get_date_created() ),
'status' => wc_get_order_status_name( $order->get_status() ),
'total' => $order->get_formatted_order_total(),
'actions' => wc_get_account_orders_actions( $order ),
'packages' => array(),
'order_type' => 'woo',
);
}
}else {
$response_body = wp_remote_retrieve_body($response);
$backoffice_orders = json_decode( $response_body, true );
$one_week_ago = strtotime('-1 week');
$c_orders = array();
foreach ($customer_orders as $customer_order){
$order = wc_get_order( $customer_order );
$order_id = $order->id;
$order_id_prefix = $order->get_order_number();
$c_orders[] = array(
'id' => $order_id,
'order_id_prefix' => $order_id_prefix,
'view_url' => $order->get_view_order_url(),
'item_count' => $order->get_item_count() - $order->get_item_count_refunded(),
'date' => wc_format_datetime( $order->get_date_created() ),
'status' => wc_get_order_status_name( $order->get_status() ),
'total' => $order->get_formatted_order_total(),
'actions' => wc_get_account_orders_actions( $order ),
'packages' => array(),
'order_type' => 'woo',
);
}
$t_orders = array();
if($backoffice_orders != '' && empty($backoffice_orders['errors'])) {
if(count($backoffice_orders)){
foreach ($backoffice_orders as $backoffice_order){
$date = wc_string_to_datetime($backoffice_order['orderDate']);
$t_orders[] = array(
'id' => $backoffice_order['id'],
'office_order_id' => $backoffice_order['orderId'],
'view_url' => '',
'item_count' => count($backoffice_order['products']),
'date' => wc_format_datetime( $date ),
'status' => wc_get_order_status_name( $backoffice_order['orderStatus'] ),
'total' => wc_price(str_replace('$','',$backoffice_order['orderTotal'])),
'actions' => '',
'packages' => $backoffice_order['packages'],
'order_type' => 'office',
);
}
}
if($c_orders && $t_orders){
$filtered_c_orders = array();
foreach ($c_orders as $c_order) {
$c_order_date = strtotime($c_order['date']);
if (!(!in_array($c_order['id'], array_column($t_orders, 'id')) && $c_order_date < $one_week_ago)) {
$filtered_c_orders[] = $c_order;
}
}
$c_orders = $filtered_c_orders;
foreach ($c_orders as $key_c => $order_c) {
foreach ($t_orders as $key_t => $order_t) {
if ($order_c['id'] == $order_t['id']) {
$c_orders[$key_c]['packages'] = $order_t['packages'];
unset($t_orders[$key_t]);
break;
}
}
}
$c_orders = array_values($c_orders);
$t_orders = array_values($t_orders);
}
$orders = array_merge($c_orders,$t_orders);
usort($orders, 'compare_by_date');
}
else {
$orders = $c_orders;
}
}
// $all_orders = array_filter($orders, function($order) use ($oneWeekAgo) {
// return !($order && strtotime($order['date']) < $oneWeekAgo);
// });
wc_get_template(
'myaccount/orders.php',
array(
'current_page' => absint( $current_page ),
'orders' => $orders,
'has_orders' => 0 < $orders,
'wp_button_class' => wc_wp_theme_get_element_class_name( 'button' ) ? ' ' . wc_wp_theme_get_element_class_name( 'button' ) : '',
)
);
}
add_action('woocommerce_new_order', 'trivia_add_referrer_to_order_meta', 10, 1);
function trivia_add_referrer_to_order_meta($order_id) {
$session = WC()->session;
if ( $session ) {
$referrer = $session->get('referrer');
if ($referrer) {
update_post_meta($order_id, '_referrer', $referrer);
}
}
}
add_action('woocommerce_admin_order_data_after_billing_address', 'display_referrer_order_meta', 10, 1);
function display_referrer_order_meta($order) {
$referrer = get_post_meta($order->get_id(), '_referrer', true);
if ($referrer) {
echo '<p><strong>Referrer:</strong> ' . esc_html($referrer) . '</p>';
}
}
// added by Kevin
add_action('init', 'capture_url_parameters');
function capture_url_parameters() {
ob_start();
// Check if all URL parameters are set
session_start([
'read_and_close' => true
]); // Start the session if not already started
// Check if each parameter exists in the URL before storing in session
if(isset($_GET['referrer'])) {
$_SESSION['referrer'] = $_GET['referrer'];
}
if(isset($_GET['affiliateID'])) {
$_SESSION['affiliateID'] = $_GET['affiliateID'];
}
if(isset($_GET['utm_content'])) {
$_SESSION['utm_content'] = $_GET['utm_content'];
}
if(isset($_GET['utm_medium'])) {
$_SESSION['utm_medium'] = $_GET['utm_medium'];
}
if(isset($_GET['utm_source'])) {
$_SESSION['utm_source'] = $_GET['utm_source'];
}
if(isset($_GET['utm_campaign'])) {
$_SESSION['utm_campaign'] = $_GET['utm_campaign'];
}
if(isset($_GET['utm_term'])) {
$_SESSION['utm_term'] = $_GET['utm_term'];
}
if(isset($_GET['sourceID'])) {
$_SESSION['sourceID'] = $_GET['sourceID'];
}
if(isset($_GET['couponcode'])) {
$_SESSION['couponcode'] = $_GET['couponcode'];
}
if(isset($_GET['promocode'])) {
$_SESSION['promocode'] = $_GET['promocode'];
}
}
add_action( 'woocommerce_thankyou', 'custom_utm_fields_hook', 10, 1 );
function custom_utm_fields_hook( $order_id ) {
$order = wc_get_order( $order_id ); // phpcs:ignore.
session_start([
'read_and_close' => true
]);
if(isset($_SESSION['referrer'])) {
update_post_meta( $order_id, 'referrer', $_SESSION['referrer'] );
unset($_SESSION['referrer']);
}
if(isset($_SESSION['affiliateID'])) {
update_post_meta( $order_id, 'affiliateID', $_SESSION['affiliateID'] );
unset($_SESSION['affiliateID']);
}
if(isset($_SESSION['utm_content'])) {
update_post_meta( $order_id, 'utm_content', $_SESSION['utm_content'] );
unset($_SESSION['utm_content']);
}
if(isset($_SESSION['utm_medium'])) {
update_post_meta( $order_id, 'utm_medium', $_SESSION['utm_medium'] );
unset($_SESSION['utm_medium']);
}
if(isset($_SESSION['utm_source'])) {
update_post_meta( $order_id, 'utm_source', $_SESSION['utm_source'] );
unset($_SESSION['utm_source']);
}
if(isset($_SESSION['utm_campaign'])) {
update_post_meta( $order_id, 'utm_campaign', $_SESSION['utm_campaign'] );
unset($_SESSION['utm_campaign']);
}
if(isset($_SESSION['utm_term'])) {
update_post_meta( $order_id, 'utm_term', $_SESSION['utm_term'] );
unset($_SESSION['utm_term']);
}
if(isset($_SESSION['sourceID'])) {
update_post_meta( $order_id, 'sourceID', $_SESSION['sourceID'] );
unset($_SESSION['sourceID']);
}
if(isset($_SESSION['couponcode'])) {
update_post_meta( $order_id, 'couponcode', $_SESSION['couponcode'] );
unset($_SESSION['couponcode']);
}
if(isset($_SESSION['promocode'])) {
update_post_meta( $order_id, 'promocode', $_SESSION['promocode'] );
unset($_SESSION['promocode']);
}
}