Zen Cart Logo
Forums / Code Collaboration / Google oAuth 2 zencart login - final debug!

Google oAuth 2 zencart login - final debug!

Views: 24,836

Results 1 to 1 of 1
2 Aug 2025, 6:07 PM
#1
flappingfish avatar

flappingfish

Zen Follower

Join Date:
Nov 2020
Posts:
318
Plugin Contributions:
3

Google oAuth 2 zencart login - final debug!

Good morning/evening folks,

I am pleased to be able to confirm i have a working google oAuth 2.0 sign in implemented on my zencart store, however, i am getting a few php warnings I would like to iron out before i submit my contribution. I shall quickly explain, so if a user is already registered they can "link" their zencart account to google. If they have never shopped on the site before they are redirected to a "complete_social_profile" page where they are requested to enter their shipping address. The issue I am encountering appears to be caused by this file or rather what the file is forgetting to do...?

[02-Aug-2025 17:18:10 UTC] Request URI: /checkout_shipping, IP address: REDACTED, Language id 1
#0 /includes/modules/pages/checkout_shipping/header_php.php(65): zen_debug_error_handler()
#1 /index.php(41): require('...')
--> PHP Warning: Undefined array key "customer_default_address_id" in /includes/modules/pages/checkout_shipping/header_php.php on line 65.

[02-Aug-2025 17:18:10 UTC] Request URI: /checkout_shipping, IP address: REDACTED, Language id 1
#0 /includes/functions/functions_addresses.php(380): zen_debug_error_handler()
#1 /includes/templates/wokiee/templates/tpl_checkout_shipping_default.php(22): zen_address_label()
#2 /includes/templates/wokiee/common/tpl_main_page.php(180): require('...')
#3 /index.php(100): require('...')
--> PHP Warning: Undefined array key "country_id" in /includes/functions/functions_addresses.php on line 380.

my current tpl_social_complete_profile is as follows....

<?php
/**
 * tpl_social_complete_profile_default.php
 * Displayed when a social login user is missing address info
 */

require(DIR_WS_LANGUAGES . $_SESSION['language'] . '/lang.social_complete_profile.php');

$customer_id = (int)$_SESSION['customer_id'];
if (!isset($customer_id) || (int)$_SESSION['customers_default_address_id'] > 0) {
  zen_redirect(zen_href_link(FILENAME_DEFAULT));
}

$customer = $db->Execute("SELECT * FROM " . TABLE_CUSTOMERS . " WHERE customers_id = '" . $customer_id . "'");
$first = $customer->fields['customers_firstname'];
$last  = $customer->fields['customers_lastname'];
$email = $customer->fields['customers_email_address'];
$phone = $customer->fields['customers_telephone'];
?>

<div class="centerColumn" id="socialCompleteProfile">
  <h1><?php echo HEADING_TITLE; ?></h1>
  <p class="alert alert-warning"><?php echo TEXT_INCOMPLETE_ADDRESS; ?></p>
  <form method="POST" action="<?php echo zen_href_link('social_complete_profile', '', 'SSL'); ?>">
    <fieldset>
      <legend><?php echo TEXT_YOUR_ADDRESS; ?></legend>

      <label for="gender"><?php echo ENTRY_GENDER; ?></label>
      <select name="gender" required>
        <option value="m"><?php echo MALE; ?></option>
        <option value="f"><?php echo FEMALE; ?></option>
      </select>

      <label for="street_address">Street Address</label>
      <input type="text" name="street_address" required>

      <label for="address_line2">Address Line 2</label>
      <input type="text" name="address_line2">

      <label for="city">City</label>
      <input type="text" name="city" required>

      <label for="postcode">Postcode</label>
      <input type="text" name="postcode" required>

      <label for="country">Country</label>
      <?php echo zen_get_country_list('country', STORE_COUNTRY, 'required'); ?>

      <label for="phone">Telephone Number</label>
      <input type="tel" name="phone" required>

      <button type="submit" class="btn btn-success mt-3"><?php echo TEXT_CONTINUE; ?></button>
    </fieldset>
  </form>
</div>

I also have an additional tpl in the pages folder for some reason...

<?php global $csp_nonce; ?>
<?php
/**
 * Template: tpl_social_complete_profile_default.php
 */
?>
<div class="centerColumn" id="socialCompleteProfile">
  <h1><?php echo HEADING_TITLE; ?></h1>
  <p class="alert alert-warning"><?php echo TEXT_INCOMPLETE_ADDRESS; ?></p>
  <form method="POST" action="<?php echo zen_href_link('social_complete_profile', '', 'SSL'); ?>">
    <fieldset>
      <legend><?php echo TEXT_YOUR_ADDRESS; ?></legend>

      <label for="street_address"><?php echo ENTRY_STREET_ADDRESS; ?></label>
      <input type="text" name="street_address" id="street_address" required>

      <label for="postcode"><?php echo ENTRY_POST_CODE; ?></label>
      <input type="text" name="postcode" id="postcode" required>

      <label for="city"><?php echo ENTRY_CITY; ?></label>
      <input type="text" name="city" id="city" required>

      <label for="country"><?php echo ENTRY_COUNTRY; ?></label>
      <?php echo zen_get_country_list('country', STORE_COUNTRY, 'required onchange="update_zone(this.value);"'); ?>

      <label for="zone_id"><?php echo ENTRY_STATE; ?></label>
      <select name="zone_id" id="zone_id" required></select>

      <button type="submit" class="btn btn-success mt-3"><?php echo TEXT_CONTINUE; ?></button>
    </fieldset>
  </form>
</div>

<script nonce="<?php echo htmlspecialchars($csp_nonce, ENT_QUOTES, 'UTF-8'); ?>">
function update_zone(country_id) {
  fetch('<?php echo zen_href_link('ajax.php', 'action=get_zones'); ?>&country_id=' + country_id)
    .then(res => res.json())
    .then(data => {
      const zoneSelect = document.getElementById('zone_id');
      zoneSelect.innerHTML = '';
      data.forEach(zone => {
        const opt = document.createElement('option');
        opt.value = zone.id;
        opt.text = zone.name;
        zoneSelect.add(opt);
      });
    });
}
window.addEventListener('load', () => update_zone(document.querySelector('[name=country]').value));
</script>

both seem to be wildly different, unsure which one it is using although i suspect its the last one (i'm learning still lol) the matching header_php.php is as follows...

<?php
/**
 * Page handler for social_complete_profile
 */
global $csp_nonce;
global $messageStack;
if (!is_object($messageStack)) {
  require_once(DIR_WS_CLASSES . 'message_stack.php');
  $messageStack = new messageStack();
}

if (!isset($_SESSION['customer_id'])) {
    zen_redirect(zen_href_link(FILENAME_LOGIN, '', 'SSL'));
}

$customer_id = (int)$_SESSION['customer_id'];

// Check if they already have a default address
$check = $db->Execute("SELECT customers_default_address_id FROM " . TABLE_CUSTOMERS . " WHERE customers_id = '" . $customer_id . "'");
if ((int)$check->fields['customers_default_address_id'] > 0) {
    zen_redirect(zen_href_link(FILENAME_DEFAULT));
}

$customer = $db->Execute("SELECT * FROM " . TABLE_CUSTOMERS . " WHERE customers_id = '" . $customer_id . "'");

$first  = $customer->fields['customers_firstname'];
$last   = $customer->fields['customers_lastname'];
$email  = $customer->fields['customers_email_address'];
$phone  = $customer->fields['customers_telephone'];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $street   = zen_db_prepare_input($_POST['street_address']);
    $postcode = zen_db_prepare_input($_POST['postcode']);
    $city     = zen_db_prepare_input($_POST['city']);
    $country  = (int)$_POST['country'];

    // Insert address (zone_id removed)
    $db->Execute("INSERT INTO " . TABLE_ADDRESS_BOOK . " (
        customers_id, entry_firstname, entry_lastname, entry_street_address,
        entry_postcode, entry_city, entry_country_id
    ) VALUES (
        '" . $customer_id . "', '" . zen_db_input($first) . "', '" . zen_db_input($last) . "', '" . zen_db_input($street) . "',
        '" . zen_db_input($postcode) . "', '" . zen_db_input($city) . "', '" . $country . "'
    )");

    $address_id = $db->Insert_ID();
    $db->Execute("UPDATE " . TABLE_CUSTOMERS . "
        SET customers_default_address_id = '" . (int)$address_id . "'
        WHERE customers_id = '" . $customer_id . "'"
    );

    $_SESSION['customer_country_id'] = $country;
    // Refresh customer session data
    $_SESSION['customer_default_address_id'] = $address_id;
    $_SESSION['sendto'] = $address_id;
    $messageStack->add_session('account', 'Profile updated successfully!', 'success');
    zen_redirect(zen_href_link(FILENAME_ACCOUNT, '', 'SSL'));
}

I'd also like to clarify something from the community contributions guidelines, it states that a module must be self installing, does an install sql and instructions qualify (sql must be edited by pasting in your google oAuth keys) otherwise it is a simple drag and drop install... does it qualify based on that description or do i need to build admin page to enter oAuth keys under configuration??

other files are language files, autoloaders, config files and an additional 3 page folders not mentioned above, these are social_login, social_link and social_callback.

So far i have had a few folk use the google login option, it does not seem to prevent checkout in its current form despite the error logs but it would be nice (and expected) if this would be functioning without generating error logs.

Feel free to take a look at the login in action at ```
crazygamer.uk