For version 1.3.0 and up:

For this tutorial, let's say we want to add the "Paypal2" payment gateway.

1. Create a new file for the payment in [Joomla dir]/administrator/components/com_lovefactory/plugins/payment folder. We'll call it paypal2.php (only letters and numbers are allowed!)

2. Add the following php code to the file:

<?php

defined('_JEXEC') or die('Restricted access');

require_once(JPATH_COMPONENT_ADMINISTRATOR.DS.'plugins'.DS.'factoryPaymentPlugin.class.php');

class Paypal2 extends factoryPaymentPlugin
{
}

This creates a new payment class. Note that the name of the class must be the same as the file name (case insensitive).

Right now, if you go to the Backend -> Lovefactory -> Payments -> Payment gateways, you'll see that our payment gateway has appeared in the list. But it cannot be used, as it has not been configured and set up (if you click on it's name, "Configuration form not set up!" message will appear).

3. To configure the payment plugin, add the "configure" method to the class:

function configure()
{
  parent::configure();

  $this->description = 'Paypal2 Payment Tutorial';
  $this->params      = array('email');
  $this->action      = 'https://www.paypal.com/cgi-bin/webscr';
}

First we set the payment gateway description, an array of parameters (in our case the email) and the action (url) of the processing payment gateway.

4. To be able to change the parameters from the backend, we need to add the "showAdminConfig" method to the class:

function showAdminConfig()
{
  ?>

  <table>
    <tr>
      <td><label for="email">Email:</label></td>
      <td><input name="email" id="email" value="<?php echo $this->_params->get('email', 'This email address is being protected from spambots. You need JavaScript enabled to view it.'); ?>"></td>
    </tr>
  </table>

  <?php
}

Note: to access the values of the parameters defined in the configure section, use the following: $this->_params->get('NAME OF THE PARAMETER', 'DEFAULT VALUE IF NOT SET');

The above code outputs an input box for the email parameter.Now if you go on the gateway plugin page, you'll be able to edit and save the parameters.

5. Go ahead and edit and save the parameters. Now you can publish the plugin by clicking on the icon from the Published column. Publish the plugin, and go to frontend -> Membership plans and click on any available plans. The new payment gateway has appeared in the list.

If you select now the new payment method and click on the Purchase button, an error will show up: "paypal2 does not have step 1". This is because we have not defined what the plugin is supposed to do. We do that in steps. You can have as many steps as you want, but only step1 is required.

For Paypal only one step is required. All you have to do is to show a confirmation form that the user submits to Paypal and that's it.

But other payment gateways may require some additional steps. For example, first you need the user to fill some billing information, and only then show the confirmation form.

6. Let's add step one to the class:

function step1()
{
  $order = $this->createOrder();

  ?>

  <h1>Confirm</h1>
  <p>Are you sure you want to buy <b><?php echo $order->title; ?></b> using Paypal2?</p>

  <br />

  <form action="<?php echo $this->action; ?>" method="post">
    <input type="hidden" name="item_number"   value="<?php echo $order->id; ?>" />
    <input type="hidden" name="on0"           value="userid" />
    <input type="hidden" name="os0"           value="<?php echo $this->user->id; ?>" />
    <input type="hidden" name="amount"        value="<?php echo $order->amount; ?>" />
    <input type="hidden" name="currency_code" value="<?php echo $order->currency; ?>" />

    <input type="hidden" name="cmd"           value="_xclick" />
    <input type="hidden" name="business"      value="<?php echo $this->_params->get('email', ''); ?>" />
    <input type="hidden" name="item_name"     value="<?php echo $order->title; ?>" />
    <input type="hidden" name="quantity"      value="1" />

    <input type="hidden" name="return"        value="<?php echo $this->return_url; ?>" />
    <input type="hidden" name="cancel_return" value="<?php echo $this->cancel_return; ?>" />
    <input type="hidden" name="notify_url"    value="<?php echo $this->notify_url; ?>" />

    <input type="hidden" name="tax"           value="0" />
    <input type="hidden" name="no_note"       value="1" />
    <input type="hidden" name="no_shipping"   value="1" />

    <input type="submit" value="Purchase" />
  </form>

  <?php
}

The first line creates and stores a new order in the database. You can access the following properties of the order, in case you need them:

- $order->id (the auto generated id of the order, numeric, eg: 10)
- $order->title (the auto generated title of the order, string, eg: 12 Months Advanced @ 100.00 EUR)
- $order->user_id (the Joomla user id of the current user)
- $order->membership_id (the id of the membership being ordered)
- $order->amount (the total amount of the order)
- $order->currency (the currency of the order)
- $order->months (the number of months of membership being ordered)
- $order->gateway (the gateway used)

In our example we need the the order id, amount, currency and title to send them to Paypal.

After that, the Paypal confirmation form is shown to the user. Other available variables:

- $this->action (this is the action defined in the configure function above)
- $this->user->id (the Joomla user id of the current user)
- $this->return_url (the url to return if the payment process was completed successfully)
- $this->cancel_return (the url to return if the payment process was not completed successfully or the user aborted the payment)
- $this->notify_url (the url used to notify LoveFactory if the payment was valid and successful or not)

7. Now select the paypal2 method and click on the Purchase button again. The confirmation form will show up. If the user clicks on the Purchase button, will be redirected to Paypal. If the payment is completed successfully, the user will be then redirected to $this->return_url or else to $this->cancel_return.

8. All it's left now to do, is create the method to handle the IPN from Paypal. Add the "processIpn" method to the class:

function processIpn()
{
  // Get values from request
  $this->refnumber      = trim(stripslashes($_POST['txn_id']));
  $this->order_title    = trim(stripslashes($_POST['item_name']));
  $this->order_id       = trim(stripslashes($_POST['item_number']));
  $this->status         = trim(stripslashes($_POST['payment_status']));
  $this->amount         = trim(stripslashes($_POST['mc_gross']));
  $this->currency       = trim(stripslashes($_POST['mc_currency']));
  $this->receiver_email = trim(stripslashes($_POST['receiver_email']));
  $this->payer_email    = trim(stripslashes($_POST['payer_email']));
  $this->test_ipn       = trim(stripslashes($_POST['test_ipn']));
  $this->first_name     = trim(stripslashes($_POST['first_name']));
  $this->last_name      = trim(stripslashes($_POST['last_name']));
  $this->user_id        = trim(stripslashes($_POST['option_selection1']));
  $this->date           = trim(stripslashes($_POST['payment_date']));

  $payment = $this->getNewPayment();

  $payment->payment_date = $this->date;
  $payment->user_id      = $this->user_id;
  $payment->amount       = $this->amount;
  $payment->currency     = $this->currency;
  $payment->order_id     = $this->order_id;
  $payment->refnumber    = $this->refnumber;

  // Check for errors
  $this->errors = $this->validatePayment();

  switch ($this->status)
  {
    case 'Completed':
    case 'Processed':
      $payment->status = count($this->errors) ? 'manual-check' : 'ok';
    break;

    case 'Failed':
    case 'Denied':
    case 'Canceled-Reversal':
    case 'Expired':
    case 'Voided':
    case 'Reversed':
    case 'Refunded':
      $payment->status = 'error';
    break;

    case 'In-Progress':
    case 'Pending':
    default:
      $payment->status = 'manual-check';
    break;
  }

  $this->savePayment($payment);
}

What this function does?

  • retrieves the variables sent by Paypal
  • creates a new payment object
  • set the payment's required properties (payment_date, user_id, amount, currency, order_id, refnumber)
  • checks if the payment has any errors (we'll create the validatePayment() function later)
  • depending on the Paypal response and if any errors were found, we set the payment status: a.) ok: no errors found and a valid response from Paypal; b.) error: Paypal rejected the payment c.) manual-check: the payment is pending or in progress.
  • we save the payment. Depending on the payment status, the membership will be automatically updated.

9. Validation functions:

function validatePayment()
  {
    $errors = array();

    // Validate IPN
    if (true !== $this->validateIpn())
      {
        $errors[] = JText::_('IPN not verified');
      }

      // Validate paypal email
      if ($this->receiver_email != $this->_params->get('email', ''))
      {
        $errors[] = JText::_('Receiver email is different from expected');
      }

      // Validate user
      if (!$this->validateUser())
      {
        $errors[] = JText::_('User doesn\'t exist or is not validated!');
      }

      // Validate Order
      $order = $this->getOrder($this->order_id);

      if ($order)
      {
        if ('pending' == $order->status)
        {
          // Validate amount
          if ($order->amount != $this->amount)
          {
            $errors[] = JText::_('Payment amount received is different from expected');
          }

          // Validate currency
          if ($order->currency != $this->currency)
          {
            $errors[] = JText::_('Payment currency is different from expected');
          }
        }
        else
        {
          $errors[] = JText::_('Order already processed');
        }
      }
      else
      {
        $errors[] = JText::_('Order not found');
      }

      return $errors;
  }

  function validateIpn()
  {
    // parse the paypal URL
    $url_parsed = parse_url($this->action);

    $post_string = '';
    foreach ($_POST as $field => $value)
    {
      $post_string .= $field . '=' . urlencode($value) . '&';
    }

    $post_string .= "cmd=_notify-validate";

    $fp = fsockopen($url_parsed['host'], '80', $err_num, $err_str, 20);
    if (!$fp)
    {
      return 'Fsockopen error no. ' . $errnum . ': ' . $errstr . '. IPN not verified';
    }
    else
    {
      fputs($fp, "POST " . $url_parsed['path'] . " HTTP/1.1\r\n");
      fputs($fp, "Host: " . $url_parsed['host'] . "\r\n");
      fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n");
      fputs($fp, "Content-length: ".strlen($post_string)."\r\n");
      fputs($fp, "Connection: close\r\n\r\n");
      fputs($fp, $post_string . "\r\n\r\n");

      $response = '';
      while(!feof($fp))
      {
        $response .= fgets($fp, 1024);
      }

      fclose($fp);
    }

    if (eregi("VERIFIED", $response))
    {
      return true;
    }

    return false;
  }

These functions check if the payment is valid (for example: if the amount received from Paypal is the same as the expected one)

Love Factory comes with 3 payment plugins: Paypal, Moneybookers and Sagepay. Make sure you check them also, to get a better understanding of how the system works.

For older version then 1.3.0 having payment facilities:

The needed files and folders are located in the [Joomla folder]/components/com_lovefactory directory, since all the changes are frontend based.

For this tutorial, let's say we want to add the "Paypal 2" payment gateway.

  1. Add in memberships/tmpl/buy.php (for the smarty templates in smarty/templates/memberships.tpl) in the select with the name "method", the new payment. [<option value="3">Paypal 2</option>]

  2. Add in the memberships/view.html.php a new value to the $method array, coresponding to the new payment gateway. [3 => 'paypal2']

  3. Add in the file models/memberships.php around line 63 in the in_array function the new array key [if (!in_array($method, array(1, 2, 3)))]

  4. Create a new file named "confirm_[name of value added in array].php". [confirm_paypal2.php]

  5. Following the example of the paypal (confirm_paypal.php) and moneybookers (confirm_moneybookers.php) files, customize the file created on 4 to your specific payment gateway needs. 

  6. You will need to modifiy the "notify" or "status" link and set it to the new payment gateway:
    [<?php echo JURI::root();?>index.php?option=com_lovefactory&controller=memberships&task=notify_paypal2&format=raw]

  7. In the controllers/memberships.php, following the notify_paypal and notify_moneybookers function, add a new function to handle the response from the payment gateway. The function name must be the same as the "task" value set in the notify link set on 5 [notify_paypal2]

  8. [function notify_paypal2()
     {
       $payment =& $this->getModel('payment');
       $payment->validatePaypal2();

       $this->log('Paypal 2');
     }]

  9. In models/payment.php following the examples of validatePaypal and validateMoneybookers add a new function [validatePaypal2]. The purpose of this function is to validate and process the response from the payment gateway. Customize it to your specific needs.
That's it! The new payments will appear in the backend on the payments tab and also on the dashboard.