I'm using a custom WooCommerce checkout.js file for a plugin I'm creating but I'm having an issue whereby the billing address is getting saved as the shipping address when the order is placed.
Here is the custom 'checkout.js' code:
/* global wc_checkout_params */
jQuery( function( $ ) {
  // wc_checkout_params is required to continue, ensure the object exists
  if ( typeof wc_checkout_params === 'undefined' ) {
    return false;
  }
  console.log('Start');
  $.blockUI.defaults.overlayCSS.cursor = 'default';
  var wc_checkout_form = {
    updateTimer: false,
    dirtyInput: false,
    xhr: false,
    $order_review: $( '#order_review' ),
    $checkout_form: $( 'form.checkout' ),
    init: function() {
      console.log('Init');
      $( document.body ).bind( 'updated_checkout', this.updated_checkout );
      $( document.body ).bind( 'update_checkout', this.update_checkout );
      $( document.body ).bind( 'init_checkout', this.init_checkout );
      $( document.body ).bind( 'country_to_state_changed', this.upgrade_state_fields );
      $( document.body ).on( 'change', 'select.country_to_state, input.country_to_state', this.upgrade_state_fields );
      $( '.back-to-checkout' ).bind( 'click', this.close_drawer );
      // Payment methods
      this.$checkout_form.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
      if ( $( document.body ).hasClass( 'woocommerce-order-pay' ) ) {
        this.$order_review.on( 'click', 'input[name="payment_method"]', this.payment_method_selected );
      }
      $( 'button.details-next-button' ).bind( 'click', this.prevent_checkout );
      // Form submission
      $( document.body ).on( 'click', '#place_order', this.submit );
      this.$checkout_form.on( 'submit', this.submit );
      // Inline validation
      this.$checkout_form.on( 'blur change', '.input-text, select, input:checkbox', this.validate_field );
      // Manual trigger
      this.$checkout_form.on( 'update', this.trigger_update_checkout );
      // Inputs/selects which update totals
      this.$checkout_form.on( 'change', 'select.shipping_method, input[name^="shipping_method"], #ship-to-different-address input, .update_totals_on_change select, .update_totals_on_change input[type="radio"]', this.trigger_update_checkout );
      this.$checkout_form.on( 'change', '.address-field select', this.input_changed );
      this.$checkout_form.on( 'change', '.address-field input.input-text, .update_totals_on_change input.input-text', this.maybe_input_changed );
      this.$checkout_form.on( 'change keydown', '.address-field input.input-text, .update_totals_on_change input.input-text', this.queue_update_checkout );
      // Address fields
      this.$checkout_form.on( 'change', '#ship-to-different-address input.mdl-switch__input', this.ship_to_different_address );
      this.$checkout_form.on( 'change', '#order_notes input.mdl-switch__input', this.order_notes );
      // Trigger events
      this.$checkout_form.find( '#ship-to-different-address input' ).change();
      this.init_payment_methods();
      // Update on page load
      if ( wc_checkout_params.is_checkout === '1' ) {
        $( document.body ).trigger( 'init_checkout' );
      }
      if ( wc_checkout_params.option_guest_checkout === 'yes' ) {
        $( 'input#createaccount' ).change( this.toggle_create_account ).change();
      } else {
        $( 'div.create-account' ).show();
      }
jQuery('.woocommerce-message').remove();
    },
    init_payment_methods: function( selectedPaymentMethod ) {
      console.log('Init payment_methods');
      var $payment_methods = $( '.woocommerce-checkout' ).find( 'input[name="payment_method"]' );
      // If there is one method, we can hide the radio input
      if ( 1 === $payment_methods.length ) {
        $payment_methods.eq(0).hide();
      }
      // If there was a previously selected method, check that one.
      if ( selectedPaymentMethod ) {
        $( '#' + selectedPaymentMethod ).prop( 'checked', true );
      }
      // If there are none selected, select the first.
      if ( 0 === $payment_methods.filter( ':checked' ).length ) {
        $payment_methods.eq(0).prop( 'checked', true );
      }
      // Trigger click event for selected method
      $payment_methods.filter( ':checked' ).eq(0).trigger( 'click' );
      wc_checkout_form.convert_payment_textinputs();
    },
    close_drawer: function() {
      console.log('close_drawer');
      jQuery('.mdl-layout__obfuscator.is-visible').click();
    },
    updated_checkout: function() {
      console.log('updated_checkout');
        jQuery('.mdl-layout__drawer .shipping input[type=radio]').each(function() {
          var name = $(this).attr('name');
          var id = $(this).attr('id');
          $(this).attr('name','review_' + name);
          $(this).attr('id','review_' + id);
        });
        componentHandler.upgradeDom();
        window.setTimeout(function() {
          jQuery('.mdl-textfield').each(function() {
            if (!jQuery(this).is('.validate-required')) {
              jQuery(this).removeClass('is-invalid');
            }
            if (jQuery(this).find('.mdl-textfield__input').val() != '') {
              jQuery(this).addClass('is-dirty');
            }
          });
        },10);
      if (jQuery('.woocommerce-error').length > 0) {
        var message ='';
        jQuery('.woocommerce-error').find('li').each(function() {
          message+= jQuery(this).text();
        });
        jQuery('.woocommerce-error').remove();
        var snackbarContainer = document.querySelector('#error-snackbar');
        var data = {
          message: message,
          timeout: 5000
        };
        snackbarContainer.MaterialSnackbar.showSnackbar(data);
      }
      jQuery('.woocommerce-message').remove();
    },
    convert_payment_textinputs: function() {
      console.log('convert_payment_textinputs');
      jQuery('.payment_box').find('input[type=text],input[type=tel],input[type=email],input[type=date],input[type=password]').each(function() {
        if (jQuery(this).parent().is('[data-upgraded]') || jQuery(this).parent().find('input[type=text],input[type=tel],input[type=email],input[type=date],input[type=password]').length > 1) {
          return;
        }
        jQuery(this).parent().addClass('mdl-textfield mdl-js-textfield mdl-textfield--floating-label');
        jQuery(this).addClass('mdl-textfield__input');
        jQuery(this).parent().find('label').addClass('mdl-textfield__label');
        componentHandler.upgradeElement(jQuery(this).parent()[0]);
      });
    },
    upgrade_state_fields: function() {
      console.log('upgrade_state_fields');
        $('#billing_state,#shipping_state').addClass('mdl-textfield__input');
      $('#billing_state,#shipping_state,#billing_country, #shipping_country').each(function() {
        jQuery(this).parent().removeAttr('data-upgraded');
        jQuery(this).find('option[value=""]').text('');
        componentHandler.upgradeElement(jQuery(this).parent()[0]);
      });
      window.setTimeout(function() {
      jQuery('.mdl-textfield').each(function() { jQuery(this).find('.mdl-textfield__input').each(function() {
        if (jQuery(this).val() && jQuery(this).val() != '') {
          jQuery(this).parent().addClass('is-dirty');
        } else {
          jQuery(this).parent().removeClass('is-dirty');
        }
        if (jQuery(this).attr('placeholder') && jQuery(this).attr('placeholder') != '') {
          jQuery(this).parent().addClass('has-placeholder');
        } else {
          jQuery(this).parent().removeClass('has-placeholder');
        }
      }); });
      },100);
    },
    get_payment_method: function() {
      console.log('get_payment_method');
      return wc_checkout_form.$checkout_form.find( 'input[name="payment_method"]:checked' ).val();
    },
    payment_method_selected: function() {
      console.log('payment_method_selected');
      if ( $( '.payment_methods input.input-radio' ).length > 1 ) {
        var target_payment_box = $( 'div.payment_box.' + $( this ).attr( 'ID' ) );
        if ( $( this ).is( ':checked' ) && ! target_payment_box.is( ':visible' ) ) {
          $( 'div.payment_box' ).filter( ':visible' ).slideUp( 250 );
          if ( $( this ).is( ':checked' ) ) {
            $( 'div.payment_box.' + $( this ).attr( 'ID' ) ).slideDown( 250 );
          }
        }
      } else {
        $( 'div.payment_box' ).show();
      }
      if ( $( this ).data( 'order_button_text' ) ) {
        $( '#place_order' ).val( $( this ).data( 'order_button_text' ) );
      } else {
        $( '#place_order' ).val( $( '#place_order' ).data( 'value' ) );
      }
    },
    toggle_create_account: function() {
      console.log('toggle_create_account');
      $( 'div.create-account' ).hide();
      if ( $( this ).is( ':checked' ) ) {
        $( 'div.create-account' ).slideDown();
      }
    },
    init_checkout: function() {
      console.log('init_checkout');
      $( '#billing_country, #shipping_country, .country_to_state' ).change();
      $( document.body ).trigger( 'update_checkout' );
    },
    maybe_input_changed: function( e ) {
      console.log('maybe_input_changed');
      if ( wc_checkout_form.dirtyInput ) {
        wc_checkout_form.input_changed( e );
      }
    },
    input_changed: function( e ) {
      console.log('input_changed');
      wc_checkout_form.dirtyInput = e.target;
      wc_checkout_form.maybe_update_checkout();
    },
    queue_update_checkout: function( e ) {
    console.log('queue_update_checkout');
      var code = e.keyCode || e.which || 0;
      if ( code === 9 ) {
        return true;
      }
      wc_checkout_form.dirtyInput = this;
      wc_checkout_form.reset_update_checkout_timer();
      wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.maybe_update_checkout, '1000' );
    },
    trigger_update_checkout: function() {
      console.log('trigger_update_checkout');
      wc_checkout_form.reset_update_checkout_timer();
      wc_checkout_form.dirtyInput = false;
      $( document.body ).trigger( 'update_checkout' );
    },
    maybe_update_checkout: function() {
      console.log('maybe_update_checkout');
      var update_totals = true;
      if ( $( wc_checkout_form.dirtyInput ).length ) {
        var $required_inputs = $( wc_checkout_form.dirtyInput ).closest( 'div' ).find( '.address-field.validate-required' );
        if ( $required_inputs.length ) {
          $required_inputs.each( function() {
            if ( $( this ).find( 'input.input-text' ).val() === '' ) {
              update_totals = false;
            }
          });
        }
      }
      if ( update_totals ) {
        wc_checkout_form.trigger_update_checkout();
      }
    },
    ship_to_different_address: function() {
      console.log('ship_to_different_address');
      if ( $( this ).is( ':checked' ) ) {
        $( '#shipping_address_section' ).slideDown();
      } else {
        $( '#shipping_address_section' ).slideUp();
      }
    },
    order_notes: function() {
      console.log('order_notes');
      if ( $( this ).is( ':checked' ) ) {
        $( '#order_notes_section' ).slideDown();
      } else {
        $( '#order_notes_section' ).slideUp();
      }
    },
    reset_update_checkout_timer: function() {
      console.log('reset_update_checkout_timer');
      clearTimeout( wc_checkout_form.updateTimer );
    },
    validate_field: function() {
      console.log('validate_field');
      var $this     = $( this ),
        $parent   = $this.closest( '.form-row' ),
        validated = true;
      if ( $parent.is( '.validate-required' ) ) {
        if ( 'checklox' === $this.attr( 'type' ) && ! $this.is( ':checked' ) ) {
          $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
          validated = false;
        } else if ( $this.val() === '' ) {
          $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-required-field' );
          validated = false;
        }
      }
      if ( $parent.is( '.validate-email' ) ) {
        if ( $this.val() ) {
          /* https://stackoverflow.com/questions/2855865/jquery-validate-e-mail-address-regex */
          var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i);
          if ( ! pattern.test( $this.val()  ) ) {
            $parent.removeClass( 'woocommerce-validated' ).addClass( 'woocommerce-invalid woocommerce-invalid-email' );
            validated = false;
          }
        }
      }
      if ( validated ) {
        $parent.removeClass( 'woocommerce-invalid woocommerce-invalid-required-field' ).addClass( 'woocommerce-validated' );
      }
    },
    update_checkout: function( event, args ) {
      console.log('update_checkout');
      // Small timeout to prevent multiple requests when several fields update at the same time
      wc_checkout_form.reset_update_checkout_timer();
      wc_checkout_form.updateTimer = setTimeout( wc_checkout_form.update_checkout_action, '5', args );
    },
    update_checkout_action: function( args ) {
      console.log('update_checkout_action');
      if ( wc_checkout_form.xhr ) {
        wc_checkout_form.xhr.abort();
      }
      if ( $( 'form.checkout' ).length === 0 ) {
        return;
      }
      args = typeof args !== 'undefined' ? args : {
        update_shipping_method: true
      };
      var country            = $( '#billing_country' ).val(),
        state            = $( '#billing_state' ).val(),
        postcode         = $( 'input#billing_postcode' ).val(),
        city             = $( '#billing_city' ).val(),
        address          = $( 'input#billing_address_1' ).val(),
        address_2        = $( 'input#billing_address_2' ).val(),
        s_country        = country,
        s_state          = state,
        s_postcode       = postcode,
        s_city           = city,
        s_address        = address,
        s_address_2      = address_2;
        has_full_address = true;
      if ( $( '#ship-to-different-address' ).find( 'input' ).is( ':checked' ) ) {
        s_country        = $( '#shipping_country' ).val();
        s_state          = $( '#shipping_state' ).val();
        s_postcode       = $( 'input#shipping_postcode' ).val();
        s_city           = $( '#shipping_city' ).val();
        s_address        = $( 'input#shipping_address_1' ).val();
        s_address_2      = $( 'input#shipping_address_2' ).val();
      }
      var data = {
        security:                   wc_checkout_params.update_order_review_nonce,
        payment_method:             wc_checkout_form.get_payment_method(),
        country:                    country,
        state:                      state,
        postcode:                   postcode,
        city:                       city,
        address:                    address,
        address_2:                  address_2,
        s_country:                  s_country,
        s_state:                    s_state,
        s_postcode:                 s_postcode,
        s_city:                     s_city,
        s_address:                  s_address,
        s_address_2:                s_address_2,
        has_full_address: has_full_address,
        post_data:                  $( 'form.checkout' ).serialize()
      };
      console.log(data);
      if ( false !== args.update_shipping_method ) {
        var shipping_methods = {};
        $( 'select.shipping_method, input[name^="shipping_method"][type="radio"]:checked, input[name^="shipping_method"][type="hidden"]' ).each( function() {
          shipping_methods[ $( this ).data( 'index' ) ] = $( this ).val();
        } );
        data.shipping_method = shipping_methods;
      }
      $( '.woocommerce-checkout-payment, .woocommerce-checkout-review-order-table' ).block({
        message: null,
        overlayCSS: {
          background: '#fff',
          opacity: 0.6
        }
      });
      wc_checkout_form.xhr = $.ajax({
        type:       'POST',
        url:        wc_checkout_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'update_order_review' ),
        data:       data,
        success:    function( data ) {
          var selectedPaymentMethod = $( '.woocommerce-checkout input[name="payment_method"]:checked' ).attr( 'id' );
          // Reload the page if requested
          if ( 'true' === data.reload ) {
            window.location.reload();
            return;
          }
          // Remove any notices added previously
          $( '.woocommerce-NoticeGroup-updateOrderReview' ).remove();
          var termsCheckBoxChecked = $( '#terms' ).prop( 'checked' );
          window.fragmentss = data.fragments;
          // Always update the fragments
          if ( data && data.fragments ) {
            $.each( data.fragments, function ( key, value ) {
              $( key ).replaceWith( value );
              $( key ).unblock();
            } );
          }
          // Recheck the terms and conditions box, if needed
          if ( termsCheckBoxChecked ) {
            $( '#terms' ).prop( 'checked', true );
          }
          // Check for error
          if ( 'failure' === data.result ) {
            var $form = $( 'form.checkout' );
            // Remove notices from all sources
            $( '.woocommerce-error, .woocommerce-message' ).remove();
            // Add new errors returned by this event
            if ( data.messages ) {
              $form.prepend( '<div class="woocommerce-NoticeGroup-updateOrderReview">' + data.messages + '</div>' );
            } else {
              $form.prepend( data );
            }
            // Lose focus for all fields
            $form.find( '.input-text, select, input:checkbox' ).blur();
            // Scroll to top
            $( 'html, body' ).animate( {
              scrollTop: ( $( 'form.checkout' ).offset().top - 100 )
            }, 1000 );
          }
          // Re-init methods
          wc_checkout_form.init_payment_methods( selectedPaymentMethod );
          // Fire updated_checkout e
          $( document.body ).trigger( 'updated_checkout', [ data ] );
        }
      });
    },
    prevent_checkout: function(e) {
      console.log('prevent_checkout');
      console.log('clickprevent');
      e.preventDefault();
    },
    submit: function(e) {
      console.log('submit');
      e.preventDefault();
      wc_checkout_form.reset_update_checkout_timer();
      var $form = $( wc_checkout_form.$checkout_form );
      console.log('Form:' + $form);
      if ( $form.is( '.processing' ) ) {
        return false;
      }
      $( document )
        .on(
          'stripeError',
          wc_checkout_form.updated_checkout
        )
        .on(
          'checkout_error',
          wc_checkout_form.updated_checkout
        );
      // Trigger a handler to let gateways manipulate the checkout if needed
      if ( $form.triggerHandler( 'checkout_place_order' ) !== false && $form.triggerHandler( 'checkout_place_order_' + wc_checkout_form.get_payment_method() ) !== false ) {
        $form.addClass( 'processing' );
        var form_data = $form.data();
        console.log ('Form Data:' + form_data );
        if ( 1 !== form_data['blockUI.isBlocked'] ) {
          $form.block({
            message: null,
            overlayCSS: {
              background: '#fff',
              opacity: 0.6
            }
          });
        }
        // ajaxSetup is global, but we use it to ensure JSON is valid once returned.
        $.ajaxSetup( {
          dataFilter: function( raw_response, dataType ) {
            // We only want to work with JSON
            if ( 'json' !== dataType ) {
              return raw_response;
            }
            try {
              // Check for valid JSON
              var data = $.parseJSON( raw_response );
              if ( data && 'object' === typeof data ) {
                // Valid - return it so it can be parsed by Ajax handler
                return raw_response;
              }
            } catch ( e ) {
              // Attempt to fix the malformed JSON
              var valid_json = raw_response.match( /{"result.*"}/ );
              if ( null === valid_json ) {
                console.log( 'Unable to fix malformed JSON' );
              } else {
                console.log( 'Fixed malformed JSON. Original:' );
                console.log( raw_response );
                raw_response = valid_json[0];
              }
            }
            return raw_response;
          }
        } );
        console.log($form.serialize());
        alert($form.serialize());
        $.ajax({
          type:     'POST',
          url:      wc_checkout_params.checkout_url,
          data:     $form.serialize(),
          dataType:   'json',
This is the '$form.serialize()' data that is getting sent by the AJAX request:
billing_first_name=Testname&billing_last_name=Testlastname&billing_phone=0800000000&billing_email=test%40wpmad.com&billing_country=GB&billing_address_1=4+Test+Street&billing_address_2=&billing_city=Test+City&billing_state=Worcestershire&billing_postcode=DY11+1JR&shipping_first_name=Testname&shipping_last_name=Testlastname&shipping_company=&shipping_country=GB&shipping_address_1=99+Test+Street&shipping_address_2=&shipping_city=Worcester&shipping_state=Worcestershire&shipping_postcode=WR1+2DS&order_comments=&shipping_method%5B0%5D=free_shipping%3A1&terms=on&terms-field=1&_wpnonce=34d56c864b&_wp_http_referer=%2F%3Fwc-ajax%3Dupdate_order_review
As above, the order is placed, but the shipping address is replaced with the billing address on the order confirmation page and within the WooCommerce orders in the admin.
Any ideas why? Any help would be greatly appreciated!
