Building a custom Webform handler

Cornell DrupalCamp 2019

Cheng Chen

 

Cheng Chen

 

Sr. Drupal Developer

Case Western Reserve University

Cleveland, OH

 

Drupal 6/7/8

 

 

Agenda

  1. Brief intro to QuikPAY
  2. Why use webform?
  3. Form Demo
  4. Code Demo
  5. Q & A

QuikPAY

How does it work - 1/2

$orderNumber = round(microtime(true)*1000);
$orderType = "MED_IDEAS";
$amountDue = "";
$timestamp = round(microtime(true)*1000);
$secret = "ftkFgD57nh4371yOuI2";
$redirect_url = "http://yoursite.com/confirmation" ......

$hash = md5($orderNumber.$orderType.$amountDue.$userChoice3.$userChoice4.
$userChoice5.$userChoice6.$userChoice7.$userChoice8.$userChoice9.$userChoice10.
$redirectUrl.$redirectUrlParameters.$retriesAllowed.$timestamp.
$userChoice11.$userChoice12.$userChoice13.$userChoice14.$userChoice15.$userChoice16.
$userChoice17.$userChoice18.$userChoice19.$userChoice20.$userChoice21.$secret);

$URL_TO_QUIKPAY = "https://quikpayasp.com/cwru2/commerce_manager/payer.do?
orderNumber=$orderNumber&orderType=$orderType&amountDue=$amountDue
&userChoice3=$userChoice3&userChoice4=$userChoice4
&userChoice5=$userChoice5&userChoice6=$userChoice6&userChoice7=$userChoice7
&userChoice8=$userChoice8&userChoice9=$userChoice9&userChoice10=$userChoice10
&userChoice11=$userChoice11&userChoice12=$userChoice12&userChoice13=$userChoice13
&userChoice14=$userChoice14&userChoice15=$userChoice15&userChoice16=$userChoice16
&userChoice17=$userChoice17&userChoice18=$userChoice18&userChoice19=$userChoice19
&userChoice20=$userChoice20&userChoice21=$userChoice21&redirectUrl=$redirectUrl
&redirectUrlParameters=$redirectUrlParameters&retriesAllowed=$retriesAllowed
&timestamp=$timestamp&hash=$hash";

QuikPAY

How does it work - 2/2

http://yoursite.com/confirmation
?transactionType=1&transactionStatus=1&transactionId=5001601231
&transactionTotalAmount=500&transactionDate=201908141917&transactionAcountType=VISA
&transactionDescription=Verification%2C+Transaction+Payments
&orderType=MED_Registrar_Payments&payerFullName=Commerce+Manager+Payer
&accountHolderName=cheng+chen&email=example%40gmail.com
&userChoice3=$userChoice3&userChoice4=$userChoice4
&userChoice5=$userChoice5&userChoice6=$userChoice6&userChoice7=$userChoice7
&userChoice8=$userChoice8&userChoice9=$userChoice9&userChoice10=$userChoice10
&userChoice11=$userChoice11&userChoice12=$userChoice12&userChoice13=$userChoice13
&userChoice14=$userChoice14&userChoice15=$userChoice15&userChoice16=$userChoice16
&userChoice17=$userChoice17&userChoice18=$userChoice18&userChoice19=$userChoice19
&userChoice20=$userChoice20&userChoice21=$userChoice21&timestamp=1565824644370&hash=cfea01350ba0a7a881750a42039ba1c2

Webform

PHP files

PHP files Webform
Manually get and validate user inputs Robust Client/Server side validations
Manually compose emails Email handler
Manually deal with database CRUD, etc... WebformSubmission Entity (extends core's EntityInterface)
Repeating process Webform template
Editors cannot help. Webform template

Quikpay_handler dir

quikpay_handler
  quikpay_handler.info.yml
  quikpay_handler.routing.yml
  quikpay_handler.module
  
  - src/
    - Controller/
      - ResponseActionController.php
    - Plugin/
      - WebformHandler
        - QuikpayHandler.php
  
  - templates/
    - quikpay-confirmation.html.twig
    - quikpay-confirmation-session-expire.html.twig
  
  - test/...
quikpay_handler.info.yml
name: 'QuikPay Handler'
type: module
description: 'Custom handler for QuikPAY webforms'
core: 8.x
package: 'Custom'
dependencies:
  - webform:webform
  - webform:webform_ui
quikpay_handler.routing.yml
quikpay_handler.response_action_controller_update:
  path: '/form/{webform_id}/confirmation'
  defaults:
    _controller: '\Drupal\quikpay_handler\Controller\ResponseActionController::update'
    _title: 'update'
  requirements:
    _permission: 'access content'
  options:
    no_cache: 'TRUE'
quikpay_handler.module

 

<?php

/**
 * @file
 * Contains quikpay_handler.module.
 */

use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\webform\Entity\WebformSubmission;

/**
 * Implements hook_help().
 */
function quikpay_handler_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the quikpay_handler module.
    case 'help.page.quikpay_handler':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Custom handler for Quikpay webforms') . '</p>';
      return $output;

    default:
  }
}


/**
 * Implements hook_theme().
 */
function quikpay_handler_theme($existing, $type, $theme, $path) {
  return [
    'quikpay_confirmation' => [
      'variables' => ['payment_var' => NULL],
    ],
    'quikpay_confirmation_session_expire' => [
    ],
  ];
}


/**
 * Implements hook_cron().
 */
function quikpay_handler_cron() {

  $webform_id = 'transcript_and_mspe_request_form';

  $submission_ids = \Drupal::service('database')
    ->select('webform_submission_data', 'wsd')
    ->fields('wsd', ['sid'])
    ->condition('wsd.webform_id', $webform_id)
    ->condition('wsd.name', 'transaction_id')
    ->condition('wsd.value', '')
    ->execute()
    ->fetchAllAssoc('sid');

  foreach ($submission_ids as $sid => $v) {
    WebformSubmission::load($sid)->delete();
  }
}
QuikpayHandler.php

 

<?php

namespace Drupal\quikpay_handler\Plugin\WebformHandler;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Form submission handler.
 *
 * @WebformHandler(
 *   id = "quikpay_form_handler",
 *   label = @Translation("QuikPay Webform Handler"),
 *   category = @Translation("Form Handler"),
 *   description = @Translation("Generate string and redirect to QuikPay"),
 *   cardinality =
 *   \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 * )
 */
class QuikpayHandler extends WebformHandlerBase {

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    
    return $form;
    
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {

    return [];
    
  }

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

    parent::submitConfigurationForm($form, $form_state); // TODO: Change the autogenerated stub

    ....
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
    $message = parent::validateForm($form, $form_state, $webform_submission); // TODO: Change the autogenerated stub
    return $message;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {

    return $form_state->setResponse(new TrustedRedirectResponse($redirect_url));
  
  }
}
ResponseActionController.php

 

<?php

namespace Drupal\quikpay_handler\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\webform\Entity\WebformSubmission;
use Symfony\Component\HttpFoundation\Request;

/**
 * Class ResponseActionController.
 */
class ResponseActionController extends ControllerBase {

  /**
   * Update Submission records with transaction related data
   * @var $webform_id
   * @return
   *   Return a confirmation page via template.
   */
  public function update($webform_id) {

    $request = Request::createFromGlobals();
    $query = $request->query->all();

    $order_number = $query['userChoice3'];
    $transaction_id = $query['transactionId'];
    $transaction_status = $query['transactionStatus'];
    $transaction_type = $query['transactionType'];
    $transaction_accounttype = $query['transactionAcountType'];
    $transaction_accountholdername = $query['accountHolderName'];
    $transaction_amount = $query['transactionTotalAmount'];

    $session = \Drupal::request()->getSession();

    $payment_clear = FALSE;

    if ($session->get('quikpay_session') == $order_number) {

      $submission_id = \Drupal::service('database')
        ->select('webform_submission_data', 'wsd')
        ->fields('wsd', ['sid'])
        ->condition('wsd.webform_id', $webform_id, '=')
        ->condition('wsd.name', 'order_number', '=')
        ->condition('wsd.value', $order_number, '=')
        ->execute()
        ->fetchAssoc();
      //  use submission update to update the value

      $sid = $submission_id['sid'];

      $webform_submission = WebformSubmission::load($sid);

      $webform = $webform_submission->getWebform();

      $twig_var = [];

      $twig_var['payment_type'] = $transaction_type;
      $twig_var['transaction_id'] = $transaction_id;
      $twig_var['confirmation_message'] = $webform->getSettings()['confirmation_message'];

      if ((($transaction_type == 1 or $transaction_type == 2) && $transaction_status == 1) or ($transaction_type == 3 && in_array($transaction_status, [
            5,
            6,
            8,
          ]))) {
        $payment_clear = TRUE;
        $twig_var['payment_clear'] = $payment_clear;
      }

      $webform_submission->setElementData('transaction_id', $transaction_id);
      $webform_submission->setElementData('transaction_status', $transaction_status);
      $webform_submission->setElementData('transaction_type', $transaction_type);
      $webform_submission->setElementData('transaction_accounttype', $transaction_accounttype);
      $webform_submission->setElementData('transaction_accountholdername', $transaction_accountholdername);
      $webform_submission->setElementData('transaction_amount', $transaction_amount / 100);
      $webform_submission->save();

      $session->remove('quikpay_session');

      return [
        '#theme' => 'quikpay_confirmation',
        '#payment_var' => $twig_var,
      ];

    }
    else {

      return [
        '#theme' => 'quikpay_confirmation_session_expire',
      ];

    }
  }
}

Building a custom webform handler

By Cheng Chen

Building a custom webform handler

a custom module case study with QuikPAY

  • 33