News:

You may pay someone to create your store, or you visit our seminar and become a professional yourself with the silver certification

Main Menu

Payment method plugin with user input field

Started by vmfyelloq19, August 13, 2019, 11:46:57 AM

Previous topic - Next topic

vmfyelloq19

Hi folks,

I need to have a user input field for a payment method, baisally a text input filed that's presented when the payment method is selected.
( ) paypal
() other 1
(x) my payment method
     Please enter code [___________]

Any hints?

TIA!

GJC Web Design

you can over ride the protected function getPluginHtml () and provide a field but I assume there is a lot more to it than that.. what is the field supposed to do?
GJC Web Design
VirtueMart and Joomla Developers - php developers https://www.gjcwebdesign.com
VM4 AusPost Shipping Plugin - e-go Shipping Plugin - VM4 Postcode Shipping Plugin - Radius Shipping Plugin - VM4 NZ Post Shipping Plugin - AusPost Estimator
Samport Payment Plugin - EcomMerchant Payment Plugin - ccBill payment Plugin
VM2 Product Lock Extension - VM2 Preconfig Adresses Extension - TaxCloud USA Taxes Plugin - Virtuemart  Product Review Component
https://extensions.joomla.org/profile/profile/details/67210
Contact for any VirtueMart or Joomla development & customisation

vmfyelloq19

Hi GJC Web Design,

thank your for your reply.
This particular "payment method" just needs to pass a string with the customer's account number along with the order.
A shopper field would be fit the case better, as it would be stored into the customer's profile, but shopper fields are not be conditional for the specify payment method.

Cheers!

GJC Web Design

in that case u could use some JS to show/hide and enforce the shopper field if that radio is chosen
GJC Web Design
VirtueMart and Joomla Developers - php developers https://www.gjcwebdesign.com
VM4 AusPost Shipping Plugin - e-go Shipping Plugin - VM4 Postcode Shipping Plugin - Radius Shipping Plugin - VM4 NZ Post Shipping Plugin - AusPost Estimator
Samport Payment Plugin - EcomMerchant Payment Plugin - ccBill payment Plugin
VM2 Product Lock Extension - VM2 Preconfig Adresses Extension - TaxCloud USA Taxes Plugin - Virtuemart  Product Review Component
https://extensions.joomla.org/profile/profile/details/67210
Contact for any VirtueMart or Joomla development & customisation

vmfyelloq19

I never use Javascript for required functionality, as it is possibly disabled.

Cheers!

jenkinhill

Quote from: vmfyelloq19 on August 14, 2019, 08:49:59 AM
I never use Javascript for required functionality, as it is possibly disabled.

As VirtueMart makes heavy use of JavaScript then AFAIK someone with js "disabled" would be unable to use the store, anyway.
Kelvyn
Lowestoft, Suffolk, UK

Retired from forum life November 2023

Please mention your VirtueMart, Joomla and PHP versions when asking a question in this forum

vmfyelloq19

Hi jenkinhill,

You are right, and I adjusted my opinion on requiring JS with VM.

I've added some custom user text fields, added some JS to hide them via CSS if the payment mezhod does not match.
But that's causing several issues:
- I cannot set the fields to required
- there is no way to add a custom validation

Any hints anyone?

TIA!

GJC Web Design

just do it with JS

example

      if(jQuery('#companyreg_field').prop('checked')) {
         jQuery('#first_name_field').removeClass('required');
         jQuery('#first_name_field').removeAttr('required aria-required');
      }else{
         jQuery('#first_name_field').addClass('required')
         jQuery('#first_name_field').attr('required', 'required');
         jQuery('#first_name_field').attr('aria-required', 'true');
      }


validation can be handled the same way  with e.g.

jQuery('input#your_input ').bind("focusout keyup change", function() {
   // do stuff
});

or on submit etc
GJC Web Design
VirtueMart and Joomla Developers - php developers https://www.gjcwebdesign.com
VM4 AusPost Shipping Plugin - e-go Shipping Plugin - VM4 Postcode Shipping Plugin - Radius Shipping Plugin - VM4 NZ Post Shipping Plugin - AusPost Estimator
Samport Payment Plugin - EcomMerchant Payment Plugin - ccBill payment Plugin
VM2 Product Lock Extension - VM2 Preconfig Adresses Extension - TaxCloud USA Taxes Plugin - Virtuemart  Product Review Component
https://extensions.joomla.org/profile/profile/details/67210
Contact for any VirtueMart or Joomla development & customisation

vmfyelloq19

Thank you GJC Web Design,
that helped a lot!

After some consideration I'm giving the paymnet plugin another go.

I managed to display the required user input fields using plgVmDisplayListFEPayment().
I can check user input just fine in plgVmOnSelectCheckPayment() by checking the values I get from vRequest::getString([...]).

Still I'm stuck with re-displaying user input in the layout called from plgVmDisplayListFEPayment().
It seems user input is empty here.
I also tried to settting a class variable ($this->myformdata) in plgVmOnSelectCheckPayment() but it this var is empty in plgVmDisplayListFEPayment(). Might be a method-call ordering thing.

So what's the proper way to fill in the former user input in the form fields?
<input type... value="<insert former user input here>" ... />


What I have so far in plugins/vmpayment/mypayment/mypayment.php is:

class plgVmpaymentMypayment extends vmPSPlugin {
    [...]
    public function plgVmDisplayListFEPayment(VirtueMartCart $cart, $selected=0, &$htmlIn) {
        if ($this->getPluginMethods($cart->vendorId) === 0) {
            if (empty($this->_name)) {
                $app=JFactory::getApplication();
                $app->enqueueMessage(vmText::_('COM_VIRTUEMART_CART_NO_' . strtoupper($this->_psType)));
                return false;
            } else {
                return false;
            }
        }
        $htmlarray=[];
        foreach ($this->methods as $this->_currentMethod) {
            if($this->checkConditions($cart, $this->_currentMethod, $cart->cartPrices)) {
            $cartPrices=$cart->cartPrices;
                $methodSalesPrice=$this->calculateSalesPrice($cart, $this->_currentMethod, $cartPrices);
                $payment_cost='';
                if ($methodSalesPrice) {
                    $payment_cost=$currency->priceDisplay($methodSalesPrice);
                }
                if ($selected == $this->_currentMethod->virtuemart_paymentmethod_id) {
                    $checked='checked="checked"';
                } else {
                    $checked='';
                }
                /*
                 *
                 *  Need to pass former user input into layout here
                 *  If possible: also result of validation to highlight bad input fields
                 *
                 */

                $html=$this->renderByLayout('display_payment', [
                    'plugin' => $this->_currentMethod
                   ,'checked' => $checked
                   ,'payment_cost' => $payment_cost
                ]);
                $htmlarray[]=$html;
                if (!empty($htmlarray)) {
                    $htmlIn[]=$htmlarray;
                }
            }
        }
        return true;
    } // plgVmDisplayListFEPayment()

    [...]

    public function plgVmOnSelectCheckPayment(VirtueMartCart $cart,  &$msg) {
        if(! $this->selectedThisByMethodId($cart->virtuemart_paymentmethod_id)) {
            return NULL; // Another method was selected, do nothing
        }
        [...] // [my checks here]
        if($errors) {
            vmWarn($errormessage);
       return false;
        } else {
            // Just return true?
            return $this->OnSelectCheck($cart);
        }
    } // plgVmOnSelectCheckPayment()

vmfyelloq19

#9
I could not find a "proper" way to save/recall/pass user input to my form.
Please find what I managed so far after fidling around, trying to make sense of vm source etc.

I'm storing user input data in the user's session to be able to recall it later.
Here's the relevant plugin code, plus a stub to render the html form fields required.
User input get's saved to database automatically as announced in constructor.

Hope it helps someone looking for something similar.

[Update: fixed copy/paste error / missing methods]

* htdocs/plugins/vmpayment/mypayment.php:

<?php
defined ('_JEXEC') or die();
use Joomla\CMS\Factory;
if(! class_exists('vmPSPlugin')) require(VMPATH_PLUGINLIBS . DS . 'vmpsplugin.php');

class plgVmpaymentMypayment extends vmPSPlugin {

    private $_formdata=[];

    function __construct(& $subject, $config) {
        parent::__construct($subject, $config);
        $this->_loggable = true;
        $this->tableFields = array_keys($this->getTableSQLFields());
        $this->_tablepkey = 'id'; //virtuemart_directdebit_id';
        $this->_tableId = 'id'; //'virtuemart_directdebit_id';
        $varsToPush=[
             'myvar1' => array('', 'char')
            ,'myvar2' => array('', 'char')
            ,'myvar3' => array('', 'char')
        ];
        $this->addVarsToPushCore($varsToPush, 1);
        $this->setConfigParameterable($this->_configTableFieldName, $varsToPush);
        this->setConvertable(array('min_amount','max_amount','cost_per_transaction','cost_min_transaction'));
        $this->setConvertDecimal(array('min_amount','max_amount','cost_per_transaction','cost_min_transaction','cost_percent_total'));
    } // __construct();

    function getTableSQLFields() {
        return [
            'id'                                 => 'int(11) UNSIGNED NOT null AUTO_INCREMENT'
            ,'virtuemart_order_id'               => 'int(1) UNSIGNED'
            ,'order_number'                      => 'char(64)'
            ,'virtuemart_paymentmethod_id'       => 'mediumint(1) UNSIGNED'
            ,'payment_name'                      => 'varchar(5000)' // might include html
            ,'payment_order_total'               => 'decimal(15,5) NOT null DEFAULT \'0.00000\''
            ,'payment_currency'                  => 'char(3) '
            ,'email_currency'                    => 'smallint(1)'
            ,'cost_per_transaction'              => 'decimal(10,2)'
            ,'cost_percent_total'                => 'decimal(10,2)'
            ,'tax_id'                            => 'smallint(1)'
            ,'myvar1'                            => 'varchar(255)'
            ,'myvar3'                           => 'varchar(255)'
            ,'myvar3'                           => 'varchar(255)'
        ];
    } // getTableSQLFields()

    public function plgVmDisplayListFEPayment(VirtueMartCart $cart, $selected = 0, &$htmlIn) {
            if ($this->getPluginMethods($cart->vendorId) === 0) {
                if (empty($this->_name)) {
                    $app = JFactory::getApplication();
                    $app->enqueueMessage(vmText::_('COM_VIRTUEMART_CART_NO_' . strtoupper($this->_psType)));
                    return false;
                } else {
                    return false;
                }
            }
            $method_name = $this->_psType . '_name';
            $currentMethod_id_key = 'virtuemart_' . $this->_psType . 'method_id';
            $session = JFactory::getSession();
            // get/store user input in session as there seems to be "official" way to keep it
            $session_node=$this->_type . '__' . $this->_name;
            $session_data = $session->get($session_node);
            foreach($this->methods as $this->_currentMethod) {
                if($this->checkConditions($cart, $this->_currentMethod, $cart->cartPrices)) {
                    $html = '';
                    $cartPrices=$cart->cartPrices;
                    if (isset($this->_currentMethod->cost_method)) {
                        $cost_method=$this->_currentMethod->cost_method;
                    } else {
                        $cost_method=true;
                    }
                    $methodSalesPrice = $this->setCartPrices($cart, $cartPrices, $this->_currentMethod, $cost_method);
                    $payment_name_html = $this->renderPluginName($this->_currentMethod);
                    $key=$method_name . '_html';
                    $this->_currentMethod->$key=$payment_name_html;
                    $this->_currentMethod->payment_currency = $this->getPaymentCurrency($this->_currentMethod);

                    $pluginmethod_id = $this->_idName;
                    $checked=($selected == $this->_currentMethod->$pluginmethod_id);
                    $html=$this->renderByLayout('display_payment', [
                         // passed to template as $viewData[]:
                         'plugin' => $this->_currentMethod
                        ,'checked' => $checked
                        ,'payment_cost' => $methodSalesPrice
                        ,'formdata' => $session_data
                    ]);
                    $htmlIn[$this->_psType][$this->_currentMethod->$currentMethod_id_key] =$html;
                } // if this->checkConditions
            } // foreach($this->methods)
            return true;
    } // plgVmDisplayListFEPayment()

    public function plgVmOnSelectCheckPayment(VirtueMartCart $cart, &$msg) {
       if(! $this->selectedThisByMethodId($cart->virtuemart_paymentmethod_id)) {
          return null; // Another method was selected, do nothing
       }

       if(! ($this->_currentMethod = $this->getVmPluginMethod($cart->virtuemart_paymentmethod_id))) {
          return false;
       }

      if(! $this->selectedThisByMethodId($cart->virtuemart_paymentmethod_id)) {
          return null; // Another method was selected, do nothing
       }
       $userinput_result=$this->_userinput_validate($cart); // result: true|array of string with errormessage. More data in $this->_formdata

       // Fire a user warning:
       if(is_array($userinput_result)) {
          array_map('vmWarn', $userinput_result); // see helpers/vmecho.php for vmWarn(), vmInfo(), ...
          return false;
       }
       return true;
    } // plgVmOnSelectCheckPayment()

    function plgVmConfirmedOrder($cart, $order) {
        if(! ($this->_currentMethod = $this->getVmPluginMethod($order['details']['BT']->virtuemart_paymentmethod_id))) {
            return null; // Another method was selected, do nothing
        }
        if(! $this->selectedThisElement($this->_currentMethod->payment_element)) {
            return false;
        }
        $session = JFactory::getSession();
        $session_node=$this->_type . '__' . $this->_name;
        $session_data = $session->get($session_node);
        $this->_currentMethod->payment_currency = $this->getPaymentCurrency($this->_currentMethod,$order['details']['BT']->payment_currency_id);

        $payment_name = $this->renderPluginName($this->_currentMethod, $order);
        $currency_code_3 = shopFunctions::getCurrencyByID($this->_currentMethod->payment_currency, 'currency_code_3');
        $email_currency = $this->getEmailCurrency($this->_currentMethod);
        $totalInPaymentCurrency = vmPSPlugin::getAmountInCurrency($order['details']['BT']->order_total,$this->_currentMethod->payment_currency);

        // Prepare data that should be stored in the database
        $dbValues['order_number'] = $order['details']['BT']->order_number;
        $dbValues['virtuemart_paymentmethod_id'] = $cart->virtuemart_paymentmethod_id;
        $dbValues['payment_name'] = $payment_name;
        $dbValues['payment_order_total'] = $totalInPaymentCurrency['value']; // from standard plugin
        $dbValues['payment_currency'] = $currency_code_3; // from standard plugin
        $dbValues['email_currency'] = $email_currency;
        $dbValues['cost_per_transaction'] = (float) @$this->_currentMethod->cost_per_transaction;
        $dbValues['cost_min_transaction'] = (float) @$this->_currentMethod->cost_min_transaction; // from standard plugin
        $dbValues['cost_percent_total'] = (float) @$this->_currentMethod->cost_percent_total; // from standard plugin
        $dbValues['tax_id'] = $this->_currentMethod->tax_id;
        $dbValues['myvar1'] = $session_data['myvar1']['value'];
        $dbValues['myvar2'] = $session_data['myvar2']['value'];
        $dbValues['myvar3'] = $session_data['myvar3']['value'];
        $this->storePSPluginInternalData($dbValues); // returns  full dataset array incl. new dbid
    } // plgVmConfirmedOrder()

    /**
    * _userinput_validate()
    * param $cart
    * return true|array of strings with error messages.
    *        Additonal data in $this->_formdata[
    *            (string) value
    *            (string) inputkey
    *            (bool) valid
    *            (string) errormessage, if any
    *        ]
    */
    private function _userinput_validate($cart) {
        $session = JFactory::getSession();
        $session_node=$this->_type . '__' . $this->_name;
        $session_data = $session->get($session_node);
        if(is_null($session_data)) $session_data = [];

        $input = Factory::getApplication()->input;
        $errors=[];

        $inputkey='virtuemart_paymentmethod_' . $cart->virtuemart_paymentmethod_id . '_' . 'myvar1';
        $value=trim($input->get($inputkey, null, 'string'));
        if(null===$value) {
            if(! empty($session_data[$key]['value'])) $value=$session_data[$key]['value'];
        }
        $formdata_dataset=[
             'value'=>$value
            ,'inputkey'=>$inputkey
            ,'valid'=>true
            ,'errormessage'=>''
        ];
        $langkey='MYPAYMENTPLUGIN_' . strtoupper($key);
        // Just check if not empty

        // Sample validation for empty value:
        if(empty($value)) {
            $formdata_dataset['valid']=false;
            $formdata_dataset['errormessage']=vmText::sprintf('COM_VIRTUEMART_MISSING_VALUE_FOR_FIELD', JText::_($langkey));
        }
        if(! empty($formdata_dataset['errormessage'])) $errors[]=$formdata_dataset['errormessage'];
        $this->_formdata[$key]=$formdata_dataset;
        $session_data[$key]=$formdata_dataset; // store in session - have no other way to push data to layout?

        // DO MORE VALIDATIONS HERE

        $session->set($session_node, $session_data);
        if(empty($errors)) {
            return true;
        } else {
            return $errors;
        }
    } // _userinput_validate()

    // [...]

} / class



* htdocs/plugins/vmpayment/myfancypayment/tmpl/display_payment.php:

<?php   
defined ('_JEXEC') or die();
// Render our fancy pamyner input fields here:
// Previous entered form data in $viewData['formdata'] etc.
// if($viewData ['checked'] ) ...
// echo '<label for=...
// echo '<input type=....

vmfyelloq19

Hi folks,

anyone has a hint on how to get and show the plugin's data as stored during checkout?

For the order detail page I see my plugin's plgVmOnShowOrderFEPayment() get's fired, and I see I can set $payment_name.

a) What method does get called for the order email?

b) How would I retrieve the data? Do I need to do the DB selects for #__virtuemart_payment_plg_mypayment myself or is there a method for this?

TIA!