commit cef8c5bde10f793db0890dcfd18cbf20b0e69c90
Author: Colin Kuskie <colink@perldreamer.com>
Date: Tue May 24 11:52:48 2011 -0700
Build a Shop::Transaction.
commit c386079ac29fc70c1cc63d0c2844511ce9db553e
Author: Colin Kuskie <colink@perldreamer.com>
Date: Tue May 24 11:34:13 2011 -0700
Pull a session out of the object.
commit 59d780eb0ffcc82572491f85f08eb4ac04cab109
Author: Colin Kuskie <colink@perldreamer.com>
Date: Tue May 24 11:15:05 2011 -0700
Rework PayPalStd driver to create transaction before sending the user off to PayPal.
commit cd2683aa8595875f7c501f29c622abaf350e90f6
Author: Colin Kuskie <colink@perldreamer.com>
Date: Tue May 24 11:14:19 2011 -0700
Fix some comments to be more cleanly readable.
commit cf1fecfb916906c4d8ec8d33bd85c59b0aea3b7c
Author: Colin Kuskie <colink@perldreamer.com>
Date: Tue May 24 11:13:52 2011 -0700
Make the transaction optional when displaying a payment error.
commit f3c949e03a18ac513938f2ed483002c5304663d5
Author: Colin Kuskie <colink@perldreamer.com>
Date: Mon May 23 19:19:56 2011 -0700
Remove dead code.
commit 5ddcb49f094fd054f79da38c4a95dd86c55a157f
Author: Colin Kuskie <colink@perldreamer.com>
Date: Thu May 19 11:31:15 2011 -0700
If a transaction is updated with a cart object, remove all transationItems from the transaction before adding new ones.
commit 3315cb30a5c1fd4d583ce352cbc9022e52544404
Author: Colin Kuskie <colink@perldreamer.com>
Date: Thu May 19 11:30:58 2011 -0700
Remove duplicate entries from ExpressCheckout i18n
commit b46d5af528d7223d12ecbed383e798cfc376ad2a
Author: Colin Kuskie <colink@perldreamer.com>
Date: Mon May 23 16:07:33 2011 -0700
Fix the version number of the PayPal API we send. Add the serialized request to the log file. Fix i18n labels so it's clear which username and password to use.
commit 1604d375822eb013c633b72993fa524703a72127
Author: Colin Kuskie <colink@perldreamer.com>
Date: Wed May 18 15:50:42 2011 -0700
Fix copy/paste errors from other i18n modules into ExpressCheckout.
commit f2c958fc7128348a18a005bfbadf83457861d6e2
Author: Colin Kuskie <colink@perldreamer.com>
Date: Wed May 18 11:26:15 2011 -0700
Update out of date POD about checking out.
commit 61ca80b15701733a1a7c7eae5d825b161e0c71c1
Author: Colin Kuskie <colink@perldreamer.com>
Date: Mon May 23 16:05:09 2011 -0700
Fix documentation in appendCartVariables, and return the hash instead of $self.
commit d3b7341c44c924f395f8594c8ae77d8187170c9f
Author: Colin Kuskie <colink@perldreamer.com>
Date: Mon May 23 16:03:20 2011 -0700
Remove variables that were not being used.
commit 2913f96182a7630bce01998bb022d3ebf4842171
Author: Colin Kuskie <colink@perldreamer.com>
Date: Mon May 16 21:05:54 2011 -0700
Pull isRecurring directly from the Cart if creating/updating a transaction from one.
522 lines
16 KiB
Perl
522 lines
16 KiB
Perl
package WebGUI::Shop::PayDriver::Ogone;
|
|
|
|
=head1 LEGAL
|
|
|
|
-------------------------------------------------------------------
|
|
WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
|
-------------------------------------------------------------------
|
|
Please read the legal notices (docs/legal.txt) and the license
|
|
(docs/license.txt) that came with this distribution before using
|
|
this software.
|
|
-------------------------------------------------------------------
|
|
http://www.plainblack.com info@plainblack.com
|
|
-------------------------------------------------------------------
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
|
|
use WebGUI::Shop::PayDriver;
|
|
use WebGUI::Exception;
|
|
use Digest::SHA qw{ sha1_hex };
|
|
use WebGUI::International;
|
|
use Data::Dumper;
|
|
|
|
use base qw{ WebGUI::Shop::PayDriver };
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 canCheckoutCart ( )
|
|
|
|
Returns whether the cart can be checked out by this plugin.
|
|
|
|
=cut
|
|
|
|
sub canCheckoutCart {
|
|
my $self = shift;
|
|
my $cart = $self->getCart;
|
|
|
|
return 0 unless $cart->readyForCheckout;
|
|
return 0 if $cart->requiresRecurringPayment;
|
|
|
|
return 1;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 definition ( session, definition )
|
|
|
|
See WebGUI::Shop::PayDriver->definition.
|
|
|
|
=cut
|
|
|
|
sub definition {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $definition = shift;
|
|
|
|
WebGUI::Error::InvalidParam->throw( error => q{Must provide a session variable} )
|
|
unless $session && ref $session eq 'WebGUI::Session';
|
|
|
|
my $i18n = WebGUI::International->new($session, 'PayDriver_Ogone');
|
|
|
|
tie my %fields, 'Tie::IxHash';
|
|
|
|
%fields = (
|
|
pspid => {
|
|
fieldType => 'text',
|
|
label => $i18n->get('psp id'),
|
|
hoverHelp => $i18n->get('psp id help'),
|
|
defaultValue => '',
|
|
},
|
|
shaSecret => {
|
|
fieldType => 'password',
|
|
label => $i18n->get('sha secret'),
|
|
hoverHelp => $i18n->get('sha secret help'),
|
|
},
|
|
postbackSecret => {
|
|
fieldType => 'password',
|
|
label => $i18n->get('postback secret'),
|
|
hoverHelp => $i18n->get('postback secret help'),
|
|
},
|
|
locale => {
|
|
fieldType => 'text',
|
|
label => $i18n->get('locale'),
|
|
hoverHelp => $i18n->get('locale help'),
|
|
defaultValue => 'en_US',
|
|
maxlength => 5,
|
|
size => 5,
|
|
},
|
|
currency => {
|
|
fieldType => 'text',
|
|
label => $i18n->get('currency'),
|
|
hoverHelp => $i18n->get('currency help'),
|
|
defaultValue => 'EUR',
|
|
maxlength => 3,
|
|
size => 3,
|
|
},
|
|
useTestMode => {
|
|
fieldType => 'yesNo',
|
|
label => $i18n->get('use test mode'),
|
|
hoverHelp => $i18n->get('use test mode help'),
|
|
defaultValue => 1,
|
|
},
|
|
summaryTemplateId => {
|
|
fieldType => 'template',
|
|
label => $i18n->get('summary template'),
|
|
hoverHelp => $i18n->get('summary template help'),
|
|
namespace => 'Shop/Credentials',
|
|
defaultValue => 'jysVZeUR0Bx2NfrKs5sulg',
|
|
},
|
|
);
|
|
|
|
push @{ $definition }, {
|
|
name => $i18n->get('Ogone'),
|
|
properties => \%fields,
|
|
};
|
|
|
|
return $class->SUPER::definition($session, $definition);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getCart
|
|
|
|
Returns the cart for either the current user or the transaction passed back by Ogone.
|
|
|
|
=cut
|
|
|
|
sub getCart {
|
|
my $self = shift;
|
|
my $cart;
|
|
|
|
if ($self->{_cartId}) {
|
|
$cart = WebGUI::Shop::Cart->new( $self->session, $self->{_cartId} );
|
|
}
|
|
|
|
return $cart || $self->SUPER::getCart;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 processPayment ()
|
|
|
|
See WebGUI::Shop::PayDriver->processPayment
|
|
|
|
=cut
|
|
|
|
sub processPayment {
|
|
my $self = shift;
|
|
# Since we'll have to create a transaction before doing the actual tranasction, we let it fail
|
|
# initially with a message that it is pending.
|
|
# Unless the transaction result is updated via _setPaymentStatus the transaction will fail.
|
|
|
|
my $success = $self->{_transactionSuccessful} || 0;
|
|
my $id = $self->{_ogoneId} || undef;
|
|
my $status = $self->{_statusCode} || undef;
|
|
my $message = $self->{_statusMessage} || 'Waiting for checkout';
|
|
|
|
return ( $success, $id, $status, $message );
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 ogoneCheckoutButton ( transaction, address )
|
|
|
|
Generates a form with a submit button that, when clicked, posts the payment data for the given transaction to Ogone
|
|
and takes the user there.
|
|
|
|
=head3 transaction
|
|
|
|
The instanciated transaction that should be paid.
|
|
|
|
=head3 address
|
|
|
|
An instanciated WebGUI::Shop::Address object that contains the billing address.
|
|
|
|
=cut
|
|
|
|
sub ogoneCheckoutButton {
|
|
my $self = shift;
|
|
my $transaction = shift;
|
|
my $address = shift;
|
|
my $session = $self->session;
|
|
my $i18n = WebGUI::International->new( $session, 'PayDriver_Ogone' );
|
|
|
|
$self->{ _ogoneTransaction } = "done" ;
|
|
|
|
# Ogone needs the transaction amount in cents
|
|
my $amount = sprintf( "%.2f", $transaction->get('amount') ) * 100;
|
|
$amount =~ s/[^\d]//g; # Remove any character from amount except digits.
|
|
|
|
my $orderId = $transaction->getId;
|
|
my $description = "Transaction ID: $orderId";
|
|
my $pspId = $self->get('pspid');
|
|
my $name = join " ", $address->get( 'firstName' ), $address->get( 'lastName' );
|
|
my $email = $address->get('email');
|
|
|
|
my $currency = $self->get('currency');
|
|
|
|
# Generate sha signature the payment data
|
|
my $passphrase = join '', $orderId, $amount, $currency, $pspId, $self->get('shaSecret');
|
|
my $shaSignature = uc sha1_hex( $passphrase );
|
|
|
|
# Define the data to be sent to ogone
|
|
my %parameters = (
|
|
PSPID => $pspId,
|
|
orderID => $orderId,
|
|
amount => $amount,
|
|
currency => $currency,
|
|
language => $self->get('locale'),
|
|
CN => join( " ", $address->get('firstName'), $address->get('lastName') ),
|
|
EMAIL => $email,
|
|
ownerZIP => $address->get( 'code' ),
|
|
owneraddress => join( " ", $address->get('address1'), $address->get('address2'), $address->get('address3') ),
|
|
ownercty => $address->get('country'),
|
|
ownertown => $address->get('city'),
|
|
ownertelno => $address->get('phoneNumber'),
|
|
COMPLUS => $self->getCart->getId,
|
|
COM => $description,
|
|
SHASign => $shaSignature,
|
|
accepturl =>
|
|
$self->session->url->getSiteURL.'/?shop=pay&method=do&do=acceptTransaction&paymentGatewayId='.$self->getId,
|
|
cancelurl =>
|
|
$self->session->url->getSiteURL.'/?shop=pay&method=do&do=cancelTransaction&paymentGatewayId='.$self->getId,
|
|
declineurl =>
|
|
$self->session->url->getSiteURL.'/?shop=pay&method=do&do=declineTransaction&paymentGatewayId='.$self->getId,
|
|
exceptionurl =>
|
|
$self->session->url->getSiteURL.'/?shop=pay&method=do&do=exceptionTransaction&paymentGatewayId='.$self->getId
|
|
);
|
|
|
|
# Convert payment data to hidden input tags
|
|
my $formFields =
|
|
join "\n",
|
|
map { WebGUI::Form::hidden( $session, { name => $_, value => $parameters{ $_ } } ) }
|
|
keys %parameters;
|
|
|
|
# Construct actual checkout form
|
|
|
|
my $action = $self->get('useTestMode')
|
|
? 'https://secure.ogone.com/ncol/test/orderstandard.asp'
|
|
: 'https://secure.ogone.com/ncol/prod/orderstandard.asp'
|
|
;
|
|
|
|
my $form =
|
|
WebGUI::Form::formHeader( $session, {
|
|
action => $action,
|
|
method => 'POST',
|
|
enctype => 'application/x-www-form-urlencoded',
|
|
} )
|
|
. $formFields
|
|
. WebGUI::Form::submit( $session, { name => 'submit2', value => $i18n->get('pay') } )
|
|
. WebGUI::Form::formFooter( $session );
|
|
|
|
return $form;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 checkPostbackSHA ( )
|
|
|
|
Processes the postback data Ogone sends after a payment/cancelation. Figures out which transaction the data belongs
|
|
to and checks whether the data isn't tampered with by comparing SHA hashes.
|
|
|
|
If everything checks out, returns the instanciated transaction object, otherwise returns undef.
|
|
|
|
=cut
|
|
|
|
sub checkPostbackSHA {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
my $url = $session->url;
|
|
|
|
# Instanciate transaction
|
|
my $transactionId = $url->unescape( $form->process( 'orderID' ) );
|
|
my $transaction = WebGUI::Shop::Transaction->new( $session, $transactionId );
|
|
return undef unless $transaction;
|
|
|
|
# Fetch and format amount from transaction
|
|
my $amount = $transaction->get('amount');
|
|
$amount =~ s/\.00$//; # remove trailing .00
|
|
my $currency = $self->get('currency');
|
|
|
|
# Construct the passphrase...
|
|
my $passphrase = join '',
|
|
$transactionId, $currency, $amount,
|
|
map( { $url->unescape( $form->process( $_ ) ) } qw{ PM ACCEPTANCE STATUS CARDNO PAYID NCERROR BRAND } ),
|
|
$self->get('postbackSecret');
|
|
|
|
# and obtain its sha-1 hash in uppercase
|
|
my $shaSignature = uc sha1_hex( $passphrase );
|
|
|
|
# Return the instanciated transaction if the hash is valid, else return undef.
|
|
return $transaction if $shaSignature eq $form->process('SHASIGN');
|
|
return undef;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 _setPaymentStatus ( transactionSuccessful, ogoneId, statusCode, statusMessage )
|
|
|
|
Stores the results of a postback in the object for later use by other methods.
|
|
|
|
=head3 transactionSuccessful
|
|
|
|
A boolean indicating whether or not the payment was successful.
|
|
|
|
=head3 ogoneId
|
|
|
|
The Ogone issued transaction ID.
|
|
|
|
=head3 statusCode
|
|
|
|
The Ogone issued status code.
|
|
|
|
=head3 statusMessage
|
|
|
|
The ogone issued status message.
|
|
|
|
=cut
|
|
|
|
sub _setPaymentStatus {
|
|
my $self = shift;
|
|
my ($form, $url) = $self->session->quick( 'form', 'url' );
|
|
|
|
$self->{_transactionSuccessful} = shift || 0;
|
|
$self->{_ogoneId} = shift || undef;
|
|
$self->{_statusCode} = shift || undef;
|
|
$self->{_statusMessage} = shift || undef;
|
|
|
|
$self->{_cartId} = $url->unescape( $form->process('COMPLUS') );
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_acceptTransaction ( )
|
|
|
|
The user is redirected to this screen when the payment was successful.
|
|
|
|
=cut
|
|
|
|
sub www_acceptTransaction {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
|
|
my $transaction = $self->checkPostbackSHA;
|
|
return $session->style->userStyle('Invalid postback data.') unless $transaction;
|
|
|
|
if ( $form->process('NCERROR') == 0 ) {
|
|
if ( !$transaction->isSuccessful ) {
|
|
$self->_setPaymentStatus( 1, $form->process('PAYID'), $form->process('STATUS'), 'Complete' );
|
|
$self->processTransaction( $transaction );
|
|
}
|
|
return $transaction->thankYou;
|
|
}
|
|
|
|
return $session->style->userStyle( 'An error occurred with your transaction.' );
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_cancelTransaction ( )
|
|
|
|
The user is redirected to this screen when the payment was canceled.
|
|
|
|
=cut
|
|
|
|
sub www_cancelTransaction {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
|
|
my $transaction = $self->checkPostbackSHA;
|
|
return $session->style->userStyle('Invalid postback data.') unless $transaction;
|
|
|
|
$self->_setPaymentStatus( 0, $form->process('PAYID'), $form->process('STATUS'), 'Cancelled' );
|
|
$self->processTransaction( $transaction );
|
|
|
|
$session->http->setRedirect($self->session->url->getSiteURL.'?shop=cart');
|
|
return $session->style->userStyle('Transaction cancelled');
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_declineTransaction ( )
|
|
|
|
The user is redirected to this screen when the payment was declined.
|
|
|
|
=cut
|
|
|
|
sub www_declineTransaction {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
|
|
my $transaction = $self->checkPostbackSHA;
|
|
return $session->style->userStyle('Invalid postback data.') unless $transaction;
|
|
|
|
$self->_setPaymentStatus( 0, $form->process('PAYID'), $form->process('STATUS'), 'Declined' );
|
|
$self->processTransaction( $transaction );
|
|
|
|
$session->http->setRedirect($self->session->url->getSiteURL.'?shop=cart');
|
|
return $session->style->userStyle('Transaction declined');
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_exceptionTransaction ( )
|
|
|
|
The user is redirected to this screen when a payment exception occurred.
|
|
|
|
=cut
|
|
|
|
sub www_exceptionTransaction {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
|
|
my $transaction = $self->checkPostbackSHA;
|
|
return $session->style->userStyle('Invalid postback data.') unless $transaction;
|
|
|
|
$self->_setPaymentStatus( 0, $form->process('PAYID'), $form->process('STATUS'), 'Transaction exception occurred' );
|
|
$self->processTransaction( $transaction );
|
|
|
|
$session->http->setRedirect($self->session->url->getSiteURL.'?shop=cart');
|
|
return $session->style->userStyle('A transaction exception occurred.');
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_edit ( )
|
|
|
|
Displays the properties screen.
|
|
|
|
|
|
=cut
|
|
|
|
sub www_edit {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $admin = WebGUI::Shop::Admin->new($session);
|
|
my $i18n = WebGUI::International->new($session, 'PayDriver_Ogone');
|
|
|
|
return $session->privilege->insufficient() unless $admin->canManage;
|
|
|
|
my $form = $self->getEditForm;
|
|
$form->submit;
|
|
|
|
my $processUrl = $self->session->url->getSiteURL.'/?shop=pay&method=do&do=processTransaction&paymentGatewayId='.$self->getId;
|
|
my $output = '<br />';
|
|
$output .= sprintf $i18n->get('ogone setup'), $processUrl, $processUrl;
|
|
|
|
return $admin->getAdminConsole->render($form->print.$output, $i18n->get('payment methods','PayDriver'));
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_getCredentials ( )
|
|
|
|
Displays the checkout form for this plugin.
|
|
|
|
=cut
|
|
|
|
sub www_getCredentials {
|
|
my ($self) = @_;
|
|
my $session = $self->session;
|
|
|
|
# Fetch transaction
|
|
my $transactionId = $session->form->process('transactionId');
|
|
my $transaction;
|
|
if ($transactionId) {
|
|
$transaction = WebGUI::Shop::Transaction->new( $session, $transactionId );
|
|
}
|
|
|
|
# Or generate a new one
|
|
unless ($transaction) {
|
|
$transaction = $self->processTransaction( );
|
|
}
|
|
|
|
# Generate 'Proceed' button
|
|
my $var = {
|
|
proceedButton => $self->ogoneCheckoutButton,
|
|
};
|
|
$self->appendCartVariables($var);
|
|
|
|
my $output = $self->processTemplate($self->get("summaryTemplateId"), $var);
|
|
return $session->style->userStyle($output);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 www_processTransaction ( )
|
|
|
|
This method is called by the post sale notfication.
|
|
|
|
=cut
|
|
|
|
sub www_processTransaction {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $form = $session->form;
|
|
|
|
my $transaction = $self->checkPostbackSHA;
|
|
return $session->style->userStyle('Invalid postback data.') unless $transaction;
|
|
|
|
if ( $form->process('NCERROR') == 0 ) {
|
|
if ( !$transaction->isSuccessful ) {
|
|
$self->_setPaymentStatus( 1, $form->process('PAYID'), $form->process('STATUS'), 'Complete' );
|
|
}
|
|
}
|
|
else {
|
|
$self->_setPaymentStatus( 0, $form->process('PAYID'), $form->process('STATUS'), 'A payment processing error occurred' );
|
|
}
|
|
|
|
$self->processTransaction( $transaction );
|
|
|
|
return 'ok';
|
|
}
|
|
|
|
1;
|
|
|