Jump to content

Custom Customer Form input


Spinbox
 Share

Recommended Posts

I would like to add extra fields to the customer form fields next to the default ones specified in https://docs.kongondo.com/start/checkout/custom-customer-form.html while checking out.

In this particular instance I would like to add a checkbox for newsletter signup, which I would like to process after submitting or on next page of checkout. I've tried to add a field to custom_form_fields, however I don't seem to get the desired value (from $padloper->getOrderCustomer()).

Is this possible and how would I handle this?

  • Like 1
Link to comment
Share on other sites

Hi @Spinbox,

On 6/1/2023 at 2:48 PM, Spinbox said:

I would like to add extra fields to the customer form fields next to the default ones specified in https://docs.kongondo.com/start/checkout/custom-customer-form.html while checking out.

Please see the example in demo-2 of the Padloper 2 Starter Site repository. This is for when you want to save the extra form inputs at the checkout form stage. In summary:

  • Add your extra fields to your checkout form
  • In ready.php use a hook to listen to PadloperProcessOrder::orderSaved as shown here in demo-2. 
  • In the hook function, process the newsletter signup checkbox. It should be in $input->post('name_of_your_checkbox')
  • Save that to your backend field where you are saving customer signups. In this hook you have access to $orderPage. This will give you access to $orderPage->padloper_order_customer. You can grab the customer details from there if you need them for your signup.

Given that you might have repeat customers, you might not want to save the customer signup details with the order itself since this will mean saving the same information with each order for this one customer. Secondly, you might want to check if the current customer has already signed up for the newsletter. You can do this in the checkout form using htmx (ajax). I.e, after they enter an email address, htmx sends ajax request to your server. In the backend you check your signup records. If customer already signed up, do nothing. Else send back html with checkbox markup for htmx to insert in your checkout form. 

If you instead want to do the signup request in the next step when collecting payment, let me know and I'll tell you where to hook.

On 6/1/2023 at 2:48 PM, Spinbox said:

however I don't seem to get the desired value (from $padloper->getOrderCustomer()).

This only works for the current order in the session and only for the customer details that Padloper knows about, i.e. not your custom ones.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Thank you,

I got it working, probably not the best code

$this->addHookBefore('PadloperProcessOrder::orderSaved', null, 'processOrderSaveCustomer');

	function processOrderSaveCustomer(HookEvent $event) {
		$input = $event->input;
		$user = user();
		$users = users();
		
		if($user->isLoggedin()) {
			$currentUser = user();
		} else {
			$currentUser = $users->add($input->email);
			$users->setCurrentUser($currentUser);
		}
		
		if($currentUser) {
			$currentUser->of(false);
			$currentUser->email = $currentUser->email != '' ? $currentUser->email : $input->post->email;
			$currentUser->pass = $currentUser->pass != '' ? $currentUser->pass : $input->post->pass;
			$currentUser->newsletter = $currentUser->newsletter != 1 ? $currentUser->newsletter : $input->post->newsletter;
			$currentUser->addRole('login-register');
			$currentUser->save();
		}

		if($currentUser->newsletter != 1) {
			// load module into template
			$mc = modules()->get("SubscribeToMailchimp");

			// add merge_fields to fill out user data, based on your audience MERGE_FIELD options
			// You need to setup the fields at "Settings > List fields and *|MERGE|* tags" first!
			$mc->subscribe($currentUser->email, ['FNAME' => $currentUser->shippingAddressFirstName, 'LNAME' => $currentUser->shippingAddressLastName]);
		}
	}

I have also an ajax check if the emailaddress exists for a user. If so, they need to login and are redirected back to the checkoutpage. Only problem I face is that the userID isn't added to the current order. (If i'm already logged in and start a new order, the userID is added to the order). How can I add or update the userID for this order?

Link to comment
Share on other sites

Looks like you might be saving some unvalidated data in the $currentUser there. I'm not familiar enough with Padloper to know if it handles pre-validating posted data like $input->post->email - but if it doesn't you might be leaving yourself open to stored XSS or an email header injection depending on how that field is used later in the code.

  • Like 1
Link to comment
Share on other sites

4 hours ago, Spinbox said:

I got it working, probably not the best code

@Spinbox. Glad you got it sorted. What @netcarver said is very important. You should never trust user input. Sanitize their inputs (except for passwords; these need to be validated though).

2 hours ago, netcarver said:

Looks like you might be saving some unvalidated data in the $currentUser there. I'm not familiar enough with Padloper to know if it handles pre-validating posted data like $input->post->email

Padloper does not involve itself directly with $input->post->email in this case; @Spinbox, you will need to sanitize that yourself since Padloper does not save users on your behalf. However, Padloper cleans the values for the order customer. In your case, you could just get the customer email from the order. Something like below (amendment of your code):

untested (but sanitized) - e.g. I don't know how your current $user is getting shippingAddressFirstName.

<?php

namespace ProcessWire;

function processOrderSaveCustomer(HookEvent $event) {
  $input = $event->input;
  $user = user();
  $users = users();
  // $sanitizer = wire('sanitizer');
  $currentUser = Null;
  /** @var Page $orderPage */
  $orderPage = $event->arguments('orderPage');

  if ($user->isLoggedin()) {
    $currentUser = $user();
  } else {

    // $currentUserName = $sanitizer->email($input->email);
    # ---- OR ----
    /** @var WireData $orderCustomer */
    $orderCustomer = $orderPage->get('padloper_order_customer');
    $email = $orderCustomer->email;
    // ++++++++++++
    if($email){
      // IF WE HAVE A USER NAME
      $currentUser = $users->add($email);
      $currentUser->email = $email;
      $users->setCurrentUser($currentUser);
    }
  }

  if ($currentUser) {
    $currentUser->of(false);
    // @NOTE IF WE HAVE A USER FROM ABOVE, THEY WILL ALREADY HAVE AN EMAIL SO THIS CHECK IS REDUNDANT
    // $currentUser->email = !empty($currentUser->email) ? $currentUser->email : $sanitizer->email($input->post->email);
    $currentUser->pass = !empty($currentUser->pass) ? $currentUser->pass : $input->post->pass;
    $currentUser->newsletter = $currentUser->newsletter != 1 ? $currentUser->newsletter : (int) $input->post->newsletter;
    $currentUser->addRole('login-register');
    $currentUser->save();
  }

  if ($currentUser->newsletter != 1) {
    // load module into template
    $mc = modules()->get("SubscribeToMailchimp");

    // add merge_fields to fill out user data, based on your audience MERGE_FIELD options
    // You need to setup the fields at "Settings > List fields and *|MERGE|* tags" first!
    $mc->subscribe($currentUser->email, ['FNAME' => $currentUser->shippingAddressFirstName, 'LNAME' => $currentUser->shippingAddressLastName]);
  }
}

 

4 hours ago, Spinbox said:

Only problem I face is that the userID isn't added to the current order. (If i'm already logged in and start a new order, the userID is added to the order). How can I add or update the userID for this order?

That's right. A user ID is only added to the order customer if they are logged in. The user ID is saved in the $orderCustomer (which is the value of $orderPage->padloper_order_customer). Below pseudo code assumes you already have a logged in user and the $orderPage:

<?php

namespace ProcessWire;

/** @var WireData $orderCustomer */
$orderCustomer = $orderPage->get('padloper_order_customer');
$orderCustomer->userID = $user->id;
$orderPage->save('padloper_order_customer');

Hope this helps.

Edited by kongondo
clarification
  • Like 1
Link to comment
Share on other sites

@netcarver Thank you,

@kongondo I got it working like I wanted. Thank you for your detailed reply. I hope I sanitized everything correctly.

edit: I'm not sure how to validate a password, found some topics about checking isValidPassword($pass) but not sure how to implement it here

For reference

  • Added extra fields to the usertemplate (not sure if it's better to create a seperate profiletemplate) 
  • Added an ajax check for email
  • User is created upon order save, optionally with a password
  • User mailchimp signup
<?php namespace ProcessWire;

	$this->addHookBefore('PadloperProcessOrder::orderSaved', null, 'processOrderSaveCustomer');

	function processOrderSaveCustomer(HookEvent $event) {
		
		$input = $event->input;
		$user = user();
		$users = users();
		$sanitizer = wire('sanitizer');
		$orderPage = $event->arguments('orderPage');
		$currentUser = Null;
		$session = session();

		if($user->isLoggedin()) {

			$currentUser = user();

		} else {

			$orderCustomer = $orderPage->get('padloper_order_customer');
			$email = $orderCustomer->email;

			if($email){
				// We add an account for the email provided to store customerdata (newsletter) even if the user doesn't want a login
				// The account can later be activated
				$currentUser = $users->add($email);
				$currentUser->email = $email;

				// Login to the new account
				if($input->post->createAccount == 1) {
					// Not sure if I need both of these
					$users->setCurrentUser($currentUser);
					$session->forceLogin($currentUser);
				}
			}

		}

		// Save order to currentuser
		$orderCustomer = $orderPage->get('padloper_order_customer');
		$orderCustomer->userID = $currentUser->id;
		$orderPage->save('padloper_order_customer');

		if($currentUser) {
			
			$currentUser->of(false);
			$currentUser->pass = !empty($currentUser->pass) ? $currentUser->pass : $input->post->pass;
			$currentUser->shippingAddressFirstName = !empty($currentUser->shippingAddressFirstName) ? $currentUser->shippingAddressFirstName : $sanitizer->text($input->post->firstName);
			$currentUser->shippingAddressMiddleName = !empty($currentUser->shippingAddressMiddleName) ? $currentUser->shippingAddressMiddleName : $sanitizer->text($input->post->middleName);
			$currentUser->shippingAddressLastName = !empty($currentUser->shippingAddressLastName) ? $currentUser->shippingAddressLastName : $sanitizer->text($input->post->lastName);
			$currentUser->shippingAddressPhone = !empty($currentUser->shippingAddressPhone) ? $currentUser->shippingAddressPhone : $sanitizer->digits($input->post->shippingAddressPhone);
			$currentUser->shippingAddressLineOne = !empty($currentUser->shippingAddressLineOne) ? $currentUser->shippingAddressLineOne : $sanitizer->text($input->post->shippingAddressLineOne);
			$currentUser->shippingAddressLineTwo = !empty($currentUser->shippingAddressLineTwo) ? $currentUser->shippingAddressLineTwo : $sanitizer->text($input->post->shippingAddressLineTwo);
			$currentUser->shippingAddressPostalCode = !empty($currentUser->shippingAddressPostalCode) ? $currentUser->shippingAddressPostalCode : $sanitizer->text($input->post->shippingAddressPostalCode);
			$currentUser->shippingAddressCity = !empty($currentUser->shippingAddressCity) ? $currentUser->shippingAddressCity : $sanitizer->text($input->post->shippingAddressCity);
			$currentUser->shippingAddressCountry = !empty($currentUser->shippingAddressCountry) ? $currentUser->shippingAddressCountry : $sanitizer->text($input->post->shippingAddressCountry);

			$newsletterSignup = $sanitizer->int($input->post->newsletter);

			if($newsletterSignup == 1) {
				$currentUser->newsletter = $newsletterSignup;
				$mc = modules()->get("SubscribeToMailchimp");
				$mc->subscribe($currentUser->email, ['FNAME' => $currentUser->shippingAddressFirstName, 'LNAME' => $currentUser->shippingAddressLastName]);

			}

			$currentUser->addRole('login-register');
			$currentUser->save();

		}

		$event->arguments('orderPage', $orderPage);

	}

 

Check if an users email exists

<?php namespace ProcessWire;

	if($config->ajax) {
		$email = sanitizer()->email(input()->get->emailExists);

		if($email != '' && users()->get("email=".$email)->id > 0) {
			echo json_encode(users()->get("email=".$email)->id);
		} else {
			echo json_encode(false);
		}
		exit();
	}

 

Link to comment
Share on other sites

9 hours ago, Spinbox said:

I got it working like I wanted. Thank you for your detailed reply. I hope I sanitized everything correctly.

@Spinbox. Glad you got it sorted! Things look sanitized correctly.

9 hours ago, Spinbox said:

edit: I'm not sure how to validate a password, found some topics about checking isValidPassword($pass) but not sure how to implement it here

Did you see this topic as well?

The docs state that InputfieldPassword::isValidPassword() (https://processwire.com/api/ref/inputfield-password/is-valid-password/:

Quote

Return whether or not the given password is valid according to configured requirements.

This means that it will use the password requirements set in the backend for ProcessWire users. If that suits your needs, then you could use the method as described in the post I have linked to above. If not, i.e. if you have a different set of requirements, you might have to roll out your own validation. An example is shown in the post I have linked to above. I think you should be fine with matching the requirements sent in the backend (i.e., use InputfieldPassword::isValidPassword() as is). You would need to handle cases where the password is found to be invalid (per your requirements).

Hope this helps. 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...