symphonists / paypal_payments

Extension for Symphony CMS for processing and logging transaction made with PayPal Website Payments Standard.
http://makenosound.com/
Other
18 stars 12 forks source link

SecureTrading reference #2

Closed nickdunn closed 15 years ago

nickdunn commented 15 years ago

I thought I recognised the flow of this extension :-)

The event file line 233 has a reference to good ol' SecureTrading.

makenosound commented 15 years ago

Haha, that's not good. Fixed now. They're so similar I thought it'd be worth repurposing the workflow for PayPal. Been vaguely thinking of a way to make the payment provider abstracted from the flow of the ext. so that people can add providers easily.

If you have any thoughts along those lines I'd be happy to hear them :p

nickdunn commented 15 years ago

Haha, that's not good. Fixed now. They're so similar I thought it'd be worth repurposing the workflow for PayPal. Been vaguely thinking of a way to make the payment provider abstracted from the flow of the ext. so that people can add providers easily.

If you have any thoughts along those lines I'd be happy to hear them :p

An abstraction would be cool, but tricky considering not all providers are as simple as PayPal and SecureTrading. I remember the Worldpay callback mechanism being slightly different. If we were building a generic e-commerce platform (a la OSCommerce or Magento) then a generic layer is easier — we always know the structure of the data from our system (a cart object) and know how to reconcile it afterwards.

I'm working on a sport site that will be using PayPal but using both modes: single item and shopping cart. Some items are single (e.g. paying for tickets to a social, one product only) whereas a kit shop will use PayPal's basket. Ideally I also wanted stock reconciliation: nice and easy for single items:

# For single-item purchases attempt to reduce remaining stock levels
if ($log['quantity'] && $field['element_name'] == 'quantity-remaining')
{
    $remaining = $entry->getData($field['id']);
    $entry->setData($field['id'], array(
            'value' => (int)$value['remaining'] - (int)$log['quantity']
        )
    );
}

But almost impossible when using a PayPal cart, without telling the script where each product lives in a Symphony section.

I like the simplicity of your convention-over-configuration approach. Just name the Fields the same as the callback variables and it just works.

If you wanted to abstract it and make gateways as plugins, I guess the only things that change between them are:

The payments log table itself could just store id, date and callback (a serialised POST array). To render the log you would query as usual and unserialise the array. This way the database structure of the log isn't tied to specific provider variables. It wouldn't allow any filtering of successful/unsuccessful payments, but how would you achieve that anyway when the extension doesn't know which field contains the meaning of the payment status?

Therefore a gateway configuration file for this extension would provide:

  1. an array of fields to send in the POST
  2. an array of fields that are returned in the callback
  3. an array of fields to keep in the logs (2 & 3 could be the same array with a boolean flag beside each variable name)

With a sensible naming convention for each gateway "driver" and a public about() method, you could build a drop down of available gateways to choose from in the System Preferences.

Class Provider_PayPal {

    public function about() {
        return array(
            'name' => 'PayPal',
            'regions' => array(
                'PayPal UK' => 'https://www.paypal.co.uk/cgi-bin/webscr',
                'PayPal Australia' => 'https://www.paypal.com.au/cgi-bin/webscr',
                'PayPal USA' => 'https://www.paypal.com/cgi-bin/webscr',
            )
        );
    }

    public $merchant_variable = 'business'; // for SecureTrading this is 'merchant'
    public $order_id_variable = 'invoice'; // for SecureTrading this is 'orderref'

    public $send_variables = array(
        'cmd', 'notify_url', 'bn', 'amount', 'item_name', 'item_number',
        // etc.
    );

    // for reconciliation and whether to store in the log
    // the log table columns would be built from this array (where field is true)
    public $callback_variables = array(
        'invoice' => true,
        'address' => false,
        'address_city' => true
    );

}