Skip to main content
Home Home

Join

  • Get in touch

Main navigation

  • Blog
    • Tracking changes on field level on entities
  • Team
  • Home
  • Blog
  • Creating a custom checkout pane for Drupal Commerce in Drupal 8

Creating a custom checkout pane for Drupal Commerce in Drupal 8

by Dalibor
Partner & CTO
17 May 2017

In this article, I'll show how you can create a custom checkout pane for Drupal Commerce in Drupal 8. For this purpose, we'll create a checkout pane with a configuration form and the ability for users to add coupons to their order.

Creating a custom checkout pane

Checkout pane needs to be created with the correct annotation and in the correct namespace. We'll create CommerceCoupons class in module_name/src/Plugin/Commerce/CheckoutPane directory with annotation like in code below.


namespace Drupal\module_name\Plugin\Commerce\CheckoutPane;

use Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowInterface;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneBase;
use Drupal\commerce_checkout\Plugin\Commerce\CheckoutPane\CheckoutPaneInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides the coupons pane.
 *
 * @CommerceCheckoutPane(
 *   id = "coupons",
 *   label = @Translation("Redeem Coupon"),
 *   default_step = "order_information",
 * )
 */
class CommerceCoupons extends CheckoutPaneBase implements CheckoutPaneInterface {


/**
 * {@inheritdoc}
 */
public function __construct(array $configuration, $plugin_id, $plugin_definition, CheckoutFlowInterface $checkout_flow, EntityTypeManagerInterface $entity_type_manager) {
  parent::__construct($configuration, $plugin_id, $plugin_definition, $checkout_flow, $entity_type_manager);
}

This means that our custom checkout pane will have an id 'coupons' and will be labelled as Redeem Coupon and will be set on order information step by default.

First, we'll create the settings (configuration) form for our pane. For this purpose, we'll have settings if we, for example, want to set up that user has the ability to redeem only one coupon on order. For this, we'll have to implement four functions.

First, we'll setup our default configuration.


/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {
  return [
      'single_coupon' => FALSE,
    ] + parent::defaultConfiguration();
}

Then we'll implement summary for our configuration form.


/**
 * {@inheritdoc}
 */
public function buildConfigurationSummary() {
  $summary = !empty($this->configuration['single_coupon']) ? $this->t('Single Coupon Usage on Order: Yes') : $this->t('Single Coupon Usage on Order: No');
  return $summary;
}

Next, we'll build our configuration form.


/**
 * {@inheritdoc}
 */
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  $form = parent::buildConfigurationForm($form, $form_state);
  $form['single_coupon'] = [
    '#type' => 'checkbox',
    '#title' => $this->t('Single Coupon Usage on Order?'),
    '#description' => $this->t('User can enter only one coupon on order.'),
    '#default_value' => $this->configuration['single_coupon'],
  ];

  return $form;
}

And save these settings on submit.


/**
 * {@inheritdoc}
 */
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  parent::submitConfigurationForm($form, $form_state);

  if (!$form_state->getErrors()) {
    $values = $form_state->getValue($form['#parents']);
    $this->configuration['single_coupon'] = !empty($values['single_coupon']);
  }
}

You can see how this will look like on Checkout flow edit page.

flow edit

Next, we'll build our pane form. We'll check our configuration, and if the order has a coupon applied on it and has config setup for the single coupon, won't show form.


/**
 * {@inheritdoc}
 */
public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) {
  $order_has_coupons = $this->order->coupons->referencedEntities();
  if($this->configuration['single_coupon'] && $order_has_coupons){
    return $pane_form;
  }
  $pane_form['coupon'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Coupon'),
    '#default_value' => '',
    '#required' => FALSE,
  ];
  return $pane_form;
}

After that, we'll implement validation for our coupons and check if a valid coupon is provided and if it can be applied to this order.


/**
 * {@inheritdoc}
 */
public function validatePaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
  $values = $form_state->getValue($pane_form['#parents']);
  if(!empty($values['coupon'])){
    $coupon_code = $values['coupon'];
    /* @var \Drupal\commerce_promotion\Entity\Coupon $commerce_coupon */
    $commerce_coupon = $this->entityTypeManager->getStorage('commerce_promotion_coupon')->loadByCode($coupon_code);
    $valid = false;
    if($commerce_coupon){
      $promotion = $commerce_coupon->getPromotion();
      $valid = $promotion->applies($this->order) ? true : false;
      if($valid){
        foreach($this->order->coupons->referencedEntities() as $coupon){
          if($commerce_coupon->id() == $coupon->id()){
            $form_state->setError($pane_form, $this->t('Coupon already applied to order.'));
          }
        }
      }
    }
    if(!$valid){
      $form_state->setError($pane_form, $this->t('The specified coupon does not apply for this order.'));
    }
  }
}

What is left for us is to save coupon on order and process coupon. For this, we'll use commerce promotion processor.


/**
 * {@inheritdoc}
 */
public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) {
  $values = $form_state->getValue($pane_form['#parents']);
  if(!empty($values['coupon'])){
    $coupon_code = $values['coupon'];
    $commerce_coupon = $this->entityTypeManager->getStorage('commerce_promotion_coupon')->loadByCode($coupon_code);
    if($commerce_coupon){
      $this->order->coupons[] = $commerce_coupon->id();
      $coupon_order_processor = \Drupal::service('commerce_promotion.promotion_order_processor');
      $coupon_order_processor->process($this->order);
      $this->order->save();
    }
  }
}

We're done and we can test our new checkout pane.

Share
Tags
Drupal
Drupal 8
Drupal modules
Planet Drupal

LOOKING FOR A DIGITAL EXPERIENCE?

Looking for a digital experience built on Drupal?
Get in touch with Drupal experts!

Get in touch

Croatia

Svetice 21
10000 Zagreb
hello@ws.agency
+385 1 3445 884

USA

Atlanta
+1 404 549 5896
Home

Footer menu

  • Privacy
  • Sitemap
  • Blog
  • Careers
  • Get in touch

© 2022 Websolutions Agency. All Rights Reserved.