plainblack.com
Username Password
search
Bookmark and Share

Writing Payment Drivers

Payment drivers are plugins to WebGUI Shop that accept payment for an order. They may tie in to a traditional credit card payment gateway, or an e-check service, or to a payment service like Pay Pal© or Google Checkout©, or even to a custom billing system. No matter what they connect to, the idea is the same: verify and accept payment for an order purchased through WebGUI Shop.

 

API Highlights

WebGUI comes with many payment drivers. Cash is good for point of sale type orders. iTransact is a traditional credit card payment gateway service. PayPal is the ubiquitous web payment gateway. Ogone is a popular European payment gateway. All are great reference material that you should have a look at and take the time to understand before you start writing your own payment driver. When you are ready to write your own payment driver, there are several parts of the API of which you should be aware.

 

The Master Classes

Before you start writing your own payment driver subclass, check out WebGUI::Shop::Pay and WebGUI::Shop::PayDriver. Pay is the management interface for all PayDrivers. PayDriver is the master class that you'll be subclassing to create your own.

 

Pay hands off web requests to the PayDrivers. It does this through its www_do() method. Along the URL it looks like:

 

/home?shop=pay;method=do;driverId=XXXXX;do=someMethod

 

Note the bolded “someMethod” part of the URL. That refers to a www_someMethod() method in your PayDriver subclass. This allows you to build web accessible content through your payment methods, and use that to integrate your payment module with any payment gateway you want to over HTTP.

 

PayDriver Basics

There are a few methods you're going to need to know in order to write your own payment driver.

 

The first is the definition() method. If you've written an asset or workflow activity then you're familiar with how this works. In definition() you specify the properties of the driver and its human readable name.

 

You'll need to override the getButton() method and return an HTML button that the user will click on to select this payment method. This is generally the entrance point into a series of www_ methods that you'll create. However, for external payment systems like PayPal, this may redirect the user to the external system.

 

You'll then need to override the processPayment() method, which will be called by the processTransaction() method. The processPayment() method should actually perform the payment request to the payment gateway. This needs to be an atomic (pass/fail) type of transaction; either the whole thing succeeds or the whole thing fails.

 

Recurring Methods

If your payment gateway can be used to handle recurring payments (most can't), then there are a few additional methods of which you'll need to be aware.

 

You'll need to override the cancelRecurringPayment() method, which should make a call to the payment gateway requesting that the recurring transaction be terminated. Like processPayment() this request must be atomic.

 

You'll also need to override the handlesRecurring() method and make it return 1. This way WebGUI Shop knows this driver is capable of handling recurring payments.

 

Karma Payment Example

This example creates a payment gateway that uses Karma, instead of money, to pay for goods. This is not only an interesting concept, but it requires nothing other than WebGUI to use it. This means you'll be able to test it without getting involved with some external payment gateway service, which can be a daunting task if you haven't done it before.

 

First, create your definition() method, where you'll define a special property used as a currency conversion ratio. In other words, how many points of karma it takes to purchase an item worth $1.

 

sub definition {

my ( $class, $session, $definition ) = shift;



tie my %fields, 'Tie::IxHash', (

conversionRatio => {

fieldType => 'integer',

label => 'Conversion Ratio',

hoverHelp => 'The amount of karma it takes to equal 1 of the shop\'s currency (dollars, yen, euros, etc)',

defaultValue => 500,

subtext => 'karma : 1 currency'

},

);



push @{ $definition }, {

name => 'Karma',

properties => \%fields,

};



return $class->SUPER::definition($session, $definition);

}

 

Now, define the getButton() method. Note the use of getDoFormTags(), which is just a helper that adds the shop, method, do, and payId variables to the form as hidden input tags. Redirect to the www_pay() method here.

 

sub getButton {

my ( $self ) = @_;

my $session = $self->session;

my $payForm = WebGUI::Form::formHeader($session)

. $self->getDoFormTags('pay')

. WebGUI::Form::submit($session, {value => 'Karma' })

. WebGUI::Form::formFooter($session);



return $payForm;

}

 

Now you can write the payment processing method, which checks to see that the user has enough karma, then subtracts it for the payment.

 

sub processPayment {

my ($self, $transaction) = @_;

my $session = $self->session;

my $user = WebGUI::User->new($session, $transaction->get('userId'));

if ($user->karma / $self->get('conversionRatio') > $transaction->get('amount')) {

$user->karma(

$transaction->get('amount') * -1,

'Shop',

'Purchase of Order #'.$transaction->get('orderNumber')

);

return (1, # success

undef, # transaction code

1, # status code

'Success'); # status message

}

return (0, # success

undef, # transaction code

0, # status code

'Insufficient Karma'); # status message

}

 

Finish up the driver with the method that processes the payment and displays the “thank you” page.

 

sub www_pay {

my ( $self ) = @_;

my $cart = $self->getCart;

 

# Make sure you can checkout the cart

return "" unless $cart->readyForCheckout;



# Complete the transaction

my $transaction = $self->processTransaction( );

return $transaction->thankYou();

}

 

The finished class will look something like the following:

 

package WebGUI::Shop::PayDriver::Karma;



use strict;



use WebGUI::Shop::PayDriver;

use WebGUI::Exception;



use base qw/WebGUI::Shop::PayDriver/;



#-------------------------------------------------------------------



sub definition {

my ( $class, $session, $definition ) = shift;



tie my %fields, 'Tie::IxHash', (

conversionRatio => {

fieldType => 'integer',

label => 'Conversion Ratio',

hoverHelp => 'The amount of karma it takes to equal 1 of the shop\'s currency (dollars, yen, euros, etc)',

defaultValue => 500,

subtext => 'karma : 1 currency'

},

);



push @{ $definition }, {

name => 'Karma',

properties => \%fields,

};



return $class->SUPER::definition($session, $definition);

}



#-------------------------------------------------------------------



sub getButton {

my ( $self ) = @_;

my $session = $self->session;

my $payForm = WebGUI::Form::formHeader($session)

. $self->getDoFormTags('pay')

. WebGUI::Form::submit($session, {value => 'Karma' })

. WebGUI::Form::formFooter($session);



return $payForm;

}



#-------------------------------------------------------------------



sub processPayment {

my ($self, $transaction) = @_;

my $session = $self->session;

my $user = WebGUI::User->new($session, $transaction->get('userId'));

if ($user->karma / $self->get('conversionRatio') > $transaction->get('amount')) {

$user->karma(

$transaction->get('amount') * -1,

'Shop',

'Purchase of Order #'.$transaction->get('orderNumber')

);

return (1, # success

undef, # transaction code

1, # status code

'Success'); # status message

}

return (0, # success

undef, # transaction code

0, # status code

'Insufficient Karma'); # status message

}



#-------------------------------------------------------------------



sub www_pay {

my ( $self ) = @_;

my $cart = $self->getCart;

 

# Make sure you can checkout the cart

return "" unless $cart->readyForCheckout;



# Complete the transaction

my $transaction = $self->processTransaction( );

return $transaction->thankYou();

}





1;

Keywords: API cart payment driver

Search | Most Popular | Recent Changes | Wiki Home
© 2018 Plain Black Corporation | All Rights Reserved