Permettre au client WooCommerce d’utiliser sa cagnotte comme réduction

Dans cette cinquième partie de tutoriels orientés développement WooCommerce, jetons un œil à la logique qui va offrir au parrain la possibilité d’utiliser ses points comme réduction sur le montant à payer de sa commande.

Si vous avez manqué le début de cette série de tutoriels, rendez-vous dans la première partie d’introduction pour en savoir plus 😉 Sachez aussi que ce plugin est téléchargeable gratuitement et  que vous pouvez découvrir son code sur GitHub.

Dans les articles précédents, nous avons réussi à mettre en place un système permettant à un utilisateur de devenir parrain d’un produit dans notre boutique WooCommerce. Après l’avoir proposé via un formulaire et une fois validé, le prescripteur reçoit une commission à chaque vente du dit produit.

Maintenant, il est temps de rendre possible l’utilisation de ces points (l’ensemble des commissions versées au parrain) afin que l’utilisateur puisse les utiliser lors de sa propre commande sur notre site e-commerce.

Dans ce tutoriel, nous allons donc apprendre à :

  1. ajouter une checkbox sur la page Commande pour demander au parrain acheteur si il souhaite utiliser ses points comme bon de réduction sur sa commande
  2. attribuer un frais négatif (une réduction) sur le total du panier WooCommerce si l’utilisateur a bel et bien indiqué qu’il souhaitait utiliser ses points
  3. enregistrer l’utilisation de ces points dans notre table SQL créée sur-mesure afin de pouvoir présenter un historique précis des gains et dépenses de points aux parrains

Mais d’abord, une image vaut mieux qu’on long discours…

Démo du tunnel d’achat avec la réduction proposée au parrain

Afficher une checkbox sur la page Commande de WooCommerce

Comme vous le savez probablement, WooCommerce propose un système intelligent de templating afin de modifier la mise en page des différents modèles utilisés par l’extension.

Utilisation des points de commission parrainage lors d'une commande WooCommerce
Utilisation des points de commission parrainage lors d’une commande WooCommerce

Vu que nous souhaitons afficher notre case à cocher sur la page Commande de WooCommerce, juste avant le récapitulatif de la commande et les infos de paiement, nous devrions agir sur le template checkout/form-checkout.php. En analysant le code de ce template (dans le dossier /templates du plugin /woocommerce), on peut remarquer de nombreuses actions ici et là qui nous permettent d’ajouter de nouveaux contenus où bon nous semble.

L’action qui nous intéresse ici est woocommerce_checkout_before_order_review : en insérant notre logique sur ce hook WooCommerce, nous réussirons à afficher notre checkbox avant le récapitulatif de commande et les moyens de paiement, sous le titre Votre commande.

A savoir

Si vous développez un thème e-commerce compatible WooCommerce, deux choix s’offrent à vous pour personnaliser les templates WooCommerce :
– la première, plus connue : écraser le template par défaut. Cela consiste à trouver dans le dossier /woocommerce/templates/ le template à modifier qui vous concerne, le dupliquer dans votre dossier de thème /themes/<votre-theme>/woocommerce/ et y effectuer les modifications que vous souhaitez.
– la deuxième : s’immiscer via un hook. Cela consiste à profiter des nombreux hooks (actions et filtres) que WooCommerce propose (776 actions listées dans le cœur WooCommerce 3 à l’heure où j’écris) et « injecter des logiques additionnelles » via ces hooks.
Dans la mesure du possible, essayez d’utiliser les hooks : en dupliquant un fichier template WooCommerce dans votre thème, vous risquez de devoir le maintenir de temps à autre lorsque WooCommerce se met à jour.
En effet, l’extension peut décider de modifier le nom d’une fonction, d’ajouter un nouveau hook ou effectuer toute autre modification sur un fichier de template. Il serait alors judicieux que votre template modifié reflète lui-aussi cette nouveauté, au risque de perdre en fonctionnalité voire tout simplement de ne plus marcher !
Avec une logique hookée sur une action ou sur un filtre, ce risque n’existe plus (ou devient très faible).
Dans notre cas, le template form-checkout.php peut évoluer à l’avenir, notre code a peu de risque d’être impacté car nous l’injectons sur l’action woocommerce_checkout_before_order_review appelée dans ce template. C’est sûr, WooCommerce peut décider de renommer cette action ou la placer ailleurs, mais ce genre de modification ne se fait jamais du jour au lendemain et les développeurs de l’extension font en sorte d’afficher des deprecated notices pour en informer les autres développeurs (nous) avant la mise en place définitive d’un changement drastique.

Voyons maintenant la fonction que nous allons insérer sur la page Commande :

<?php

function msk_display_use_points_checkbox() {
	// On récupère le nombre de points de l'utilisateur
	$user_points = msk_get_customer_commission_balance(get_current_user_id())['balance'];

	// Si l'acheteur a des points...
	if ($user_points > 0) {
		if (isset($_POST['post_data'])) {
			parse_str($_POST['post_data'], $form_data);
		} else {
			$form_data = $_POST;
		} 

		if (empty($form_data)) $form_data['use-points'] = 'on';

		// Si il a plus de points que le total de la commande à payer, il utilisera moins de points
		if ($user_points > WC()->cart->subtotal_ex_tax) {
			$user_points = WC()->cart->subtotal_ex_tax;
		} ?>
		
		<!-- Idéalement, placer ce bout de Javascript dans un fichier .js de votre thème/plugin -->
		<script>jQuery('form.checkout').on('change', '#use-points', function(){ jQuery('body').trigger('update_checkout'); });</script>

		<fieldset class="use-points">
			<label for="use-points">
				<input type="hidden" name="use-points" value="off" />
				<input type="checkbox" <?php checked($form_data['use-points'], 'on'); ?> id="use-points" name="use-points" value="on" />
				<span><?php printf(__('J\'utilise mes %1$s points.', 'mosaika'), msk_money_to_points_value($user_points)); ?></span>
			</label>	
		</fieldset>
	<?php }
}
add_action('woocommerce_checkout_before_order_review', 'msk_display_use_points_checkbox');

Analysons les grandes lignes de cette logique PHP :

  1. ligne 5 : d’abord, on récupère le nombre de points que l’utilisateur actuellement connecté possède
  2. ligne 8 : nous n’afficherons la checkbox si et seulement si l’utilisateur a des points à utiliser, sinon aucun intérêt !
  3. ligne 9 à 15 : on regarde si la checkbox est cochée ou décochée.
  4. ligne 18 à 20 : on limite le nombre de points affichés/proposés si le parrain a plus de points que le montant actuel du panier. Si son panier coûte 100€ et qu’il a 200€ de réductions possibles grâce à ces points, nous limitons évidemment la réduction à 100€ maximum. Notez que nous comparons à la valeur hors-taxe du panier (que l’on récupère avec WC()->cart->subtotal_ex_tax) car nous ne souhaitons pas appliquer la réduction sur la TVA à payer.
    1. pour information, il vous suffit de remplacer subtotal_ex_tax par subtotal pour obtenir le montant du panier incluant les taxes
  5. ligne 23 : on insère un petit peu de Javascript qui va déclencher l’événement update_checkout lorsque la checkbox est cochée ou décochée.
    Cet événement est géré par le Javascript de WooCommerce et va permettre de demander (en AJAX) de recalculer et régénérer le tableau récapitulatif de commande (juste avant le bouton Commander). C’est cette logique AJAX que nous allons intercepter un peu plus bas afin d’appliquer (ou non) une réduction sur le panier de l’acheteur si il a coché la checkbox J’utilise mes X points.
  6. et enfin, ligne 25 à 31 : on affiche la structure HTML de notre checkbox en indiquant le nombre de points dans le <label>.
    1. notez l’utilisation d’une fonction msk_money_to_points_value() qui nous permet de convertir une valeur monétaire en nombre de points : dans notre plugin, 1 point vaut 10 centimes donc 10 points valent 1€ de réduction. Nous stockons la valeur monétaire (le prix de la commission versée ou le montant de la réduction appliquée) mais affichons un nombre de points : une conversion est donc peut-être nécessaire.

Appliquer une réduction (frais négatif) à la commande WooCommerce

Nous affichons la checkbox permettant au parrain d’utiliser ses points : il nous faut désormais intercepter son choix et recalculer le montant du panier si il souhaite utiliser ses points.

Cela s’effectue en 2 parties : la première chose (déjà faite) a été de déclencher l’événement Javascript update_checkout lorsque l’utilisateur agit sur la checkbox d’utilisation de ses points. En faisant cela, le Javascript natif de WooCommerce va envoyer une requête AJAX avec en paramètre post_data tout le contenu du formulaire de commande (dont notre checkbox). Le code PHP de WooCommerce va :

  1. intercepter cette requête AJAX
  2. analyser le contenu du formulaire stocké dans $_POST[‘post_data’]
  3. effectuer les logiques nécessaires, comme :
    1. recalculer le montant du panier,
    2. vérifier quelle(s) méthode(s) de livraison sont possibles,
    3. appliquer des bons de réductions,
    4. etc.
  4. et renvoyer en réponse les nouvelles informations à afficher pour cette commande (dont le montant du panier fraîchement recalculé) une fois toutes ces logiques effectuées.

La deuxième chose à faire est donc d’appliquer une réduction sur le panier si l’utilisateur a demandé d’utiliser ses points sur sa commande.

Voila donc la logique PHP.

<?php 

function msk_add_discount_to_cart_total($cart) {
	if (!$_POST || (is_admin() && !is_ajax())) {
		return;
	}

	if (isset($_POST['post_data'])) {
		parse_str($_POST['post_data'], $form_data);
	} else {
		$form_data = $_POST; // fallback for final checkout (non-ajax)
	}

	// Si l'acheteur a coché la checkbox pour utiliser ses points...
	if (isset($form_data['use-points']) && $form_data['use-points'] == 'on') {
		// On récupère son nombre de points
		$discount = msk_get_customer_commission_balance(get_current_user_id())['balance'];

		// Si il a des points...
		if ($discount > 0) {
			$cart_subtotal = WC()->cart->subtotal_ex_tax;

			// Si il a plus de points que le montant de la commande, on limite
			if ($discount > $cart_subtotal) {
				$discount = $cart_subtotal;
			}

			// On ajoute un frais négatif (réduction) sur sa commande
			WC()->cart->add_fee(__('Utilisation de vos points', 'mosaika'), -$discount, false, '');
		}
	}
}
add_action('woocommerce_cart_calculate_fees', 'msk_add_discount_to_cart_total');

Ici, on se hooke sur l’action woocommerce_cart_calculate_fees qui est exécutée lorsque WooCommerce calcule les différents frais additionnels à appliquer sur la commande.

Comme mentionné précédemment, notre code PHP a connaissance des données du formulaire de commande grâce à la variable globale $_POST[‘post_data’] que nous récupérons de la requête AJAX.

Nous allons, dans notre cas :

  1. ligne 15 : vérifier si l’utilisateur a bien demandé à utiliser ses points (donc si la valeur de la checkbox est on)
  2. ligne 17 : récupérer le nombre de points disponibles que l’acheteur peut utiliser
  3. ligne 20 : vérifier si il a un nombre de points positif, auquel cas…
  4. ligne 21 à 26 : appliquer la même logique que précédemment au cas où il bénéficie, grâce à tous ses points, d’une réduction supérieure au montant de son panier : dans ce cas, nous n’appliquons une réduction équivalente à la valeur hors-taxe du panier
  5. ligne 29 : on ajoute au panier un frais négatif (-$discount) grâce à la fonction WC()->cart->add_fee(). Notez l’importance du premier paramètre (le nom du frais additionnel) que nous intercepterons un peu plus tard dans notre code.

Récapitulons : à chaque fois que WooCommerce calculera les frais d’une commande, l’action woocommerce_cart_calculate_fees sera exécutée. Notre code ci-dessus sera exécuté lui aussi. Nous aurons accès aux données du formulaire, dont la checkbox d’utilisation des points : si cette checkbox est cochée (donc si $form_data[use-points] == on), nous appliquerons un frais négatif sur le panier pour qu’une réduction soit prise en compte.

Une fois la commande passée, ce frais sera lié et enregistré dans la commande WooCommerce et s’affichera dans le back-office de la boutique comme suit…

Affichage de la réduction (frais négatif) dans le back-office de la commande WooCommerce
Affichage de la réduction (frais négatif) dans le back-office de la commande WooCommerce

Déduire les points utilisés de la cagnotte du parrain après validation de la commande

Jusqu’ici, nous avons fait en sorte qu’une réduction s’applique sur le montant du panier WooCommerce de l’acheteur si il dispose de points. Mais ces points n’ont pas encore été déduits réellement de sa cagnotte.

Il faut donc que l’on enregistre l’usage de ces points une fois que la commande est définie comme étant terminée. Nous allons encore une fois profiter de l’action WooCommerce woocommerce_order_status_changed et allons exécuter une logique assez proche de celle développée pour l’attribution de points à l’utilisateur parrain d’un produit.

<?php

function msk_save_commissions_use_from_order($order_id, $old_status, $new_status) {
	global $wpdb;
	$commissions_table_name = $wpdb->prefix . 'commissions';

	// On récupère les infos de la commande
	$order = wc_get_order($order_id);
	$order_status = $new_status;
	$order_data = $order->get_data();
	$type = 'use';

	// Si la commande était "terminée", on supprime tout usage de points enregistré dans notre table SQL
	if ($old_status == 'completed') {
		$wpdb->delete(
			$commissions_table_name,
			array('order_id' => $order_id, 'type' => $type),
			array('%d', '%s')
		);
	}

	// Si la commande devient "terminée"...
	if ($order_status == 'completed' && isset($order_data['fee_lines'])) {
		foreach ($order_data['fee_lines'] as $fee) {
			// Si la commande possède des "frais"
			if (is_a($fee, 'WC_Order_Item_Fee')) {
				// Si la commande possède un frais "Utilisation de vos points" ajouté par notre plugin
				if ($fee->get_name() == __('Utilisation de vos points', 'mosaika')) {
					// On récupère le nombre de points utilisés
					$commission_used = abs($fee->get_total());

					// Si il a utilisé des points, on enregistre l'usage de ces points dans notre table SQL
					if ($commission_used > 0) {
						$data = array(
							'type' => $type,
							'amount' => $commission_used,
							'user_id' => $order->get_customer_id(),
							'order_id' => $order_id,
							'time' => current_time('mysql')
						);

						$wpdb->insert(
							$commissions_table_name,
							$data
						);
					}
				}
			}
		}
	}
}
add_action('woocommerce_order_status_changed', 'msk_save_commissions_use_from_order', 10, 3);

Décortiquons tout ça :

  1. ligne 8 : on récupère l’objet PHP WC_Order représentant la commande à traiter grâce à la fonction wc_get_order()
  2. ligne 10 : on récupère les données extra de cette commande avec la méthode get_data()
  3. ligne 14 à 20 : si l’ancien statut de la commande était Terminée, on supprime toute référence aux usages de points reliés à cette commande. Peut-être que la commande a été annulée ou remboursée ; il nous faut alors oublier toute utilisation de points du parrain sur cette commande afin qu’il puisse indirectement récupérer les points qu’il a utilisés
  4. ligne 23 : au contraire, si la commande pas en statut Terminée
    1. ligne 24 : on va analyser chaque frais ($fee) de la commande
    2. ligne 28 : et si le nom du frais que l’on analyse correspond au nom que l’on a donné précédemment à notre réduction
    3. ligne 30 : on récupère le montant du frais avec sa méthode get_total()
    4. ligne 33 à 46 : et on va enregistrer les infos de ce frais dans notre table SQL en indiquant un type use (pour usage), le montant de la réduction appliquée, l’identifiant de l’utilisateur acheteur concerné, l’identifiant de la commande concernée et le jour/heure à laquelle cette commande passe en statut Terminée

Grâce à cette fonction hookée dans la logique de WooCommerce, le plugin va donc pouvoir enregistrer dans notre table SQL l’usage des points fait par le parrain.

Commissions versées et réductions faites !

Et voila, les logiques les plus importantes de notre extension WooCommerce de gestion de commissions sont prêtes ! Les deux prochains tutoriels de cette série nous permettront d’améliorer un peu plus ce plugin : dans la sixième partie de ce tutoriel, nous découvrirons comment créer un cron et envoyer un e-mail pour informer les parrains des nouveaux points qu’ils ont récemment reçus.

Et si vous avez des questions, n’hésitez pas à nous en faire part en commentaire !


Vous avez aimé cet article ?

Partagez-le sur vos réseaux sociaux en guise de remerciement :)


Laisser un commentaire