ITransact and other fixes and migration of payment plugins.
This commit is contained in:
parent
ac9d8cf405
commit
152dfc2838
5 changed files with 501 additions and 273 deletions
|
|
@ -64,6 +64,7 @@ migrateSubscriptions( $session );
|
||||||
updateUsersOfCommerceMacros($session);
|
updateUsersOfCommerceMacros($session);
|
||||||
addDBLinkAccessToSQLMacro($session);
|
addDBLinkAccessToSQLMacro($session);
|
||||||
addAssetManager( $session );
|
addAssetManager( $session );
|
||||||
|
migratePaymentPlugins( $session );
|
||||||
|
|
||||||
finish($session); # this line required
|
finish($session); # this line required
|
||||||
|
|
||||||
|
|
@ -1386,6 +1387,33 @@ sub addDBLinkAccessToSQLMacro {
|
||||||
print "Done.\n" unless $quiet;
|
print "Done.\n" unless $quiet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
sub migratePaymentPlugins {
|
||||||
|
my $session = shift;
|
||||||
|
print "\tMigrating WebGUI default commerce plugins..." unless $quiet;
|
||||||
|
|
||||||
|
foreach my $namespace (qw{ Cash ITransact }) {
|
||||||
|
my $properties = $session->db->buildHashRef(
|
||||||
|
'select fieldName, fieldValue from commerceSettings where type=\'Payment\' and namespace=?',
|
||||||
|
[
|
||||||
|
$namespace,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$properties->{ groupToUse } = $properties->{ whoCanUse };
|
||||||
|
|
||||||
|
my $plugin = eval {
|
||||||
|
WebGUI::Pluggable::instanciate("WebGUI::Shop::PayDriver::$namespace", 'create', [
|
||||||
|
$session,
|
||||||
|
$properties->{ label },
|
||||||
|
$properties
|
||||||
|
])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
print "Done\n" unless $quiet;
|
||||||
|
}
|
||||||
|
|
||||||
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
|
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ sub getRecurringPeriodValues {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $session = $self->session;
|
my $session = $self->session;
|
||||||
|
|
||||||
my $i18n = WebGUI::International->new($session, 'Commerce');
|
my $i18n = WebGUI::International->new($session, 'Shop');
|
||||||
tie my %periods, "Tie::IxHash";
|
tie my %periods, "Tie::IxHash";
|
||||||
%periods = (
|
%periods = (
|
||||||
Weekly => $i18n->get('weekly'),
|
Weekly => $i18n->get('weekly'),
|
||||||
|
|
|
||||||
|
|
@ -183,10 +183,10 @@ sub definition {
|
||||||
},
|
},
|
||||||
receiptEmailTemplateId => {
|
receiptEmailTemplateId => {
|
||||||
fieldType => 'template',
|
fieldType => 'template',
|
||||||
namespace => "Shop/EmailReceipt",
|
namespace => "Shop/ReceiptEmail",
|
||||||
label => $i18n->get("receipt email template"),
|
label => $i18n->get("receipt email template"),
|
||||||
hoverHelp => $i18n->get("receipt email template help"),
|
hoverHelp => $i18n->get("receipt email template help"),
|
||||||
defaultValue => '',
|
defaultValue => 'BMzuE91-XB8E-XGll1zpvA',
|
||||||
},
|
},
|
||||||
saleNotificationGroupId => {
|
saleNotificationGroupId => {
|
||||||
fieldType => 'group',
|
fieldType => 'group',
|
||||||
|
|
@ -410,6 +410,51 @@ sub getName {
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 getSelectAddressButton ( returnMethod, [ buttonLabel ] )
|
||||||
|
|
||||||
|
Generates a button for selecting an address.
|
||||||
|
|
||||||
|
=head3 returnMethod
|
||||||
|
|
||||||
|
The name of the www_ method the selected addressId should be returned to. Without the 'www_' part.
|
||||||
|
|
||||||
|
=head3 buttonLabel
|
||||||
|
|
||||||
|
The label for the button, defaults to the internationalized version of 'Choose billing address'.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub getSelectAddressButton {
|
||||||
|
my $self = shift;
|
||||||
|
my $returnMethod = shift;
|
||||||
|
my $buttonLabel = shift || 'Choose billing address';
|
||||||
|
my $session = $self->session;
|
||||||
|
|
||||||
|
# Generate the json string that defines where the address book posts the selected address
|
||||||
|
my $callbackParams = {
|
||||||
|
url => $session->url->page,
|
||||||
|
params => [
|
||||||
|
{ name => 'shop', value => 'pay' },
|
||||||
|
{ name => 'method', value => 'do' },
|
||||||
|
{ name => 'do', value => $returnMethod },
|
||||||
|
{ name => 'paymentGatewayId', value => $self->getId },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
my $callbackJson = JSON::to_json( $callbackParams );
|
||||||
|
|
||||||
|
# Generate 'Choose billing address' button
|
||||||
|
my $addressButton = WebGUI::Form::formHeader( $session )
|
||||||
|
. WebGUI::Form::hidden( $session, { name => 'shop', value => 'address' } )
|
||||||
|
. WebGUI::Form::hidden( $session, { name => 'method', value => 'view' } )
|
||||||
|
. WebGUI::Form::hidden( $session, { name => 'callback', value => $callbackJson } )
|
||||||
|
. WebGUI::Form::submit( $session, { value => $buttonLabel } )
|
||||||
|
. WebGUI::Form::formFooter( $session );
|
||||||
|
|
||||||
|
return $addressButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
=head2 handlesRecurring ()
|
=head2 handlesRecurring ()
|
||||||
|
|
||||||
Returns 0. Should be overridden to return 1 by any subclasses that can handle recurring payments.
|
Returns 0. Should be overridden to return 1 by any subclasses that can handle recurring payments.
|
||||||
|
|
|
||||||
|
|
@ -39,198 +39,6 @@ sub _generateCancelRecurXml {
|
||||||
return $xml;
|
return $xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
sub _monthYear {
|
|
||||||
my $session = shift;
|
|
||||||
my $form = $session->form;
|
|
||||||
|
|
||||||
tie my %months, "Tie::IxHash";
|
|
||||||
tie my %years, "Tie::IxHash";
|
|
||||||
%months = map { sprintf( '%02d', $_ ) => sprintf( '%02d', $_ ) } 1 .. 12;
|
|
||||||
%years = map { $_ => $_ } 2004 .. 2099;
|
|
||||||
|
|
||||||
my $monthYear =
|
|
||||||
WebGUI::Form::selectBox( $session, {
|
|
||||||
name => 'expMonth',
|
|
||||||
options => \%months,
|
|
||||||
value => [ $form->process("expMonth") ]
|
|
||||||
})
|
|
||||||
. " / "
|
|
||||||
. WebGUI::Form::selectBox( $session, {
|
|
||||||
name => 'expYear',
|
|
||||||
options => \%years,
|
|
||||||
value => [ $form->process("expYear") ]
|
|
||||||
});
|
|
||||||
|
|
||||||
return $monthYear;
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
sub _resolveRecurRecipe {
|
|
||||||
my $self = shift;
|
|
||||||
my $duration = shift;
|
|
||||||
|
|
||||||
my %resolve = (
|
|
||||||
Weekly => 'weekly',
|
|
||||||
BiWeekly => 'biweekly',
|
|
||||||
FourWeekly => 'fourweekly',
|
|
||||||
Monthly => 'monthly',
|
|
||||||
Quarterly => 'quarterly',
|
|
||||||
HalfYearly => 'halfyearly',
|
|
||||||
Yearly => 'yearly',
|
|
||||||
);
|
|
||||||
|
|
||||||
# TODO: Throw exception
|
|
||||||
return $resolve{ $duration };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
|
|
||||||
=head2 doXmlRequest ( xml [ isAdministrative ] )
|
|
||||||
|
|
||||||
Post an xml request to the ITransact backend. Returns a LWP::UserAgent response object.
|
|
||||||
|
|
||||||
=head3 xml
|
|
||||||
|
|
||||||
The xml string. Must contain a valid xml header.
|
|
||||||
|
|
||||||
=head3 isGatewayInterface
|
|
||||||
|
|
||||||
Determines what kind of request the xml is. For GatewayRequests set this value to 1. For SaleRequests set to 0 or
|
|
||||||
undef.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub doXmlRequest {
|
|
||||||
my $self = shift;
|
|
||||||
my $xml = shift;
|
|
||||||
my $isGatewayInterface = shift;
|
|
||||||
|
|
||||||
# Figure out which cgi script we should post the XML to.
|
|
||||||
my $xmlTransactionScript = $isGatewayInterface
|
|
||||||
? 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans2.cgi'
|
|
||||||
: 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans.cgi'
|
|
||||||
;
|
|
||||||
# Set up LWP
|
|
||||||
my $userAgent = LWP::UserAgent->new;
|
|
||||||
$userAgent->env_proxy;
|
|
||||||
$userAgent->agent("WebGUI");
|
|
||||||
|
|
||||||
# Create a request and stuff the xml in it
|
|
||||||
my $request = HTTP::Request->new( POST => $xmlTransactionScript );
|
|
||||||
$request->content_type( 'application/x-www-form-urlencoded' );
|
|
||||||
$request->content( 'xml='.$xml );
|
|
||||||
|
|
||||||
# Do the request
|
|
||||||
my $response = $userAgent->request($request);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
|
|
||||||
=head2 cancelRecurringPayment ( transaction )
|
|
||||||
|
|
||||||
Cancels a recurring transaction. Returns an array containing ( isSuccess, gatewayStatus, gatewayError).
|
|
||||||
|
|
||||||
=head3 transaction
|
|
||||||
|
|
||||||
The instanciated recurring transaction object.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub cancelRecurringPayment {
|
|
||||||
my $self = shift;
|
|
||||||
my $transaction = shift;
|
|
||||||
my $session = $self->session;
|
|
||||||
#### TODO: Throw exception
|
|
||||||
|
|
||||||
# Get the payment definition XML
|
|
||||||
my $xml = $self->_generateCancelRecurXml( $transaction );
|
|
||||||
$session->errorHandler->info("XML Request: $xml");
|
|
||||||
|
|
||||||
# Post the xml to ITransact
|
|
||||||
my $response = $self->doXmlRequest( $xml, 1 );
|
|
||||||
|
|
||||||
# Process response
|
|
||||||
if ($response->is_success) {
|
|
||||||
# We got some XML back from iTransact, now parse it.
|
|
||||||
$session->errorHandler->info('Starting request');
|
|
||||||
my $transactionResult = XMLin( $response->content );
|
|
||||||
unless (defined $transactionResult->{ RecurUpdateResponse }) {
|
|
||||||
# GatewayFailureResponse: This means the xml is invalid or has the wrong mime type
|
|
||||||
$session->errorHandler->info( "GatewayFailureResponse: result: [" . $response->content . "]" );
|
|
||||||
return(
|
|
||||||
0,
|
|
||||||
$transactionResult->{ Status },
|
|
||||||
$transactionResult->{ ErrorMessage } . ' Category: ' . $transactionResult->{ ErrorCategory }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
# RecurUpdateResponse: We have succesfully sent the XML and it was correct. Note that this doesn't mean
|
|
||||||
# that the cancellation has succeeded. It only has if Status is set to OK and the remaining terms is 0.
|
|
||||||
$session->errorHandler->info( "RecurUpdateResponse: result: [" . $response->content . "]" );
|
|
||||||
my $transactionData = $transactionResult->{ RecurUpdateResponse };
|
|
||||||
|
|
||||||
my $status = $transactionData->{ Status };
|
|
||||||
my $errorMessage = $transactionData->{ ErrorMessage };
|
|
||||||
my $errorCategory = $transactionData->{ ErrorCategory };
|
|
||||||
my $remainingTerms = $transactionData->{ RecurDetails }->{ RemReps };
|
|
||||||
|
|
||||||
# Uppercase the status b/c the documentation is not clear on the case.
|
|
||||||
my $isSuccess = uc( $status ) eq 'OK' && $remainingTerms == 0;
|
|
||||||
|
|
||||||
return ( $isSuccess, $status, "$errorMessage Category: $errorCategory" );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
# Connection Error
|
|
||||||
$session->errorHandler->info("Connection error");
|
|
||||||
|
|
||||||
return ( 0, undef, 'ConnectionError', $response->status_line );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
sub definition {
|
|
||||||
my $class = shift;
|
|
||||||
my $session = shift;
|
|
||||||
my $definition = shift;
|
|
||||||
|
|
||||||
my $i18n = WebGUI::International->new($session, 'PayDriver_ITransact');
|
|
||||||
|
|
||||||
tie my %fields, 'Tie::IxHash';
|
|
||||||
%fields = (
|
|
||||||
vendorId => {
|
|
||||||
fieldType => 'text',
|
|
||||||
label => $i18n->echo('vendorId'),
|
|
||||||
hoverHelp => $i18n->echo('vendorId help'),
|
|
||||||
},
|
|
||||||
password => {
|
|
||||||
fieldType => 'password',
|
|
||||||
label => $i18n->echo('password'),
|
|
||||||
hoverHelp => $i18n->echo('password help'),
|
|
||||||
},
|
|
||||||
useCVV2 => {
|
|
||||||
fieldType => 'yesNo',
|
|
||||||
label => $i18n->echo('use cvv2'),
|
|
||||||
hoverHelp => $i18n->echo('use cvv2 help'),
|
|
||||||
},
|
|
||||||
emailMessage => {
|
|
||||||
fieldType => 'textarea',
|
|
||||||
label => $i18n->echo('emailMessage'),
|
|
||||||
hoverHelp => $i18n->echo('emailMessage help'),
|
|
||||||
},
|
|
||||||
# readonly stuff from old plugin here?
|
|
||||||
);
|
|
||||||
|
|
||||||
push @{ $definition }, {
|
|
||||||
name => $i18n->echo('Itransact'),
|
|
||||||
properties => \%fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
return $class->SUPER::definition($session, $definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub _generatePaymentRequestXML {
|
sub _generatePaymentRequestXML {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
@ -357,6 +165,266 @@ sub _generatePaymentRequestXML {
|
||||||
return $xml;
|
return $xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
sub _monthYear {
|
||||||
|
my $session = shift;
|
||||||
|
my $form = $session->form;
|
||||||
|
|
||||||
|
tie my %months, "Tie::IxHash";
|
||||||
|
tie my %years, "Tie::IxHash";
|
||||||
|
%months = map { sprintf( '%02d', $_ ) => sprintf( '%02d', $_ ) } 1 .. 12;
|
||||||
|
%years = map { $_ => $_ } 2004 .. 2099;
|
||||||
|
|
||||||
|
my $monthYear =
|
||||||
|
WebGUI::Form::selectBox( $session, {
|
||||||
|
name => 'expMonth',
|
||||||
|
options => \%months,
|
||||||
|
value => [ $form->process("expMonth") ]
|
||||||
|
})
|
||||||
|
. " / "
|
||||||
|
. WebGUI::Form::selectBox( $session, {
|
||||||
|
name => 'expYear',
|
||||||
|
options => \%years,
|
||||||
|
value => [ $form->process("expYear") ]
|
||||||
|
});
|
||||||
|
|
||||||
|
return $monthYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
sub _resolveRecurRecipe {
|
||||||
|
my $self = shift;
|
||||||
|
my $duration = shift;
|
||||||
|
|
||||||
|
my %resolve = (
|
||||||
|
Weekly => 'weekly',
|
||||||
|
BiWeekly => 'biweekly',
|
||||||
|
FourWeekly => 'fourweekly',
|
||||||
|
Monthly => 'monthly',
|
||||||
|
Quarterly => 'quarterly',
|
||||||
|
HalfYearly => 'halfyearly',
|
||||||
|
Yearly => 'yearly',
|
||||||
|
);
|
||||||
|
|
||||||
|
# TODO: Throw exception
|
||||||
|
return $resolve{ $duration };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 cancelRecurringPayment ( transaction )
|
||||||
|
|
||||||
|
Cancels a recurring transaction. Returns an array containing ( isSuccess, gatewayStatus, gatewayError).
|
||||||
|
|
||||||
|
=head3 transaction
|
||||||
|
|
||||||
|
The instanciated recurring transaction object.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub cancelRecurringPayment {
|
||||||
|
my $self = shift;
|
||||||
|
my $transaction = shift;
|
||||||
|
my $session = $self->session;
|
||||||
|
#### TODO: Throw exception
|
||||||
|
|
||||||
|
# Get the payment definition XML
|
||||||
|
my $xml = $self->_generateCancelRecurXml( $transaction );
|
||||||
|
$session->errorHandler->info("XML Request: $xml");
|
||||||
|
|
||||||
|
# Post the xml to ITransact
|
||||||
|
my $response = $self->doXmlRequest( $xml, 1 );
|
||||||
|
|
||||||
|
# Process response
|
||||||
|
if ($response->is_success) {
|
||||||
|
# We got some XML back from iTransact, now parse it.
|
||||||
|
$session->errorHandler->info('Starting request');
|
||||||
|
my $transactionResult = XMLin( $response->content );
|
||||||
|
unless (defined $transactionResult->{ RecurUpdateResponse }) {
|
||||||
|
# GatewayFailureResponse: This means the xml is invalid or has the wrong mime type
|
||||||
|
$session->errorHandler->info( "GatewayFailureResponse: result: [" . $response->content . "]" );
|
||||||
|
return(
|
||||||
|
0,
|
||||||
|
$transactionResult->{ Status },
|
||||||
|
$transactionResult->{ ErrorMessage } . ' Category: ' . $transactionResult->{ ErrorCategory }
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
# RecurUpdateResponse: We have succesfully sent the XML and it was correct. Note that this doesn't mean
|
||||||
|
# that the cancellation has succeeded. It only has if Status is set to OK and the remaining terms is 0.
|
||||||
|
$session->errorHandler->info( "RecurUpdateResponse: result: [" . $response->content . "]" );
|
||||||
|
my $transactionData = $transactionResult->{ RecurUpdateResponse };
|
||||||
|
|
||||||
|
my $status = $transactionData->{ Status };
|
||||||
|
my $errorMessage = $transactionData->{ ErrorMessage };
|
||||||
|
my $errorCategory = $transactionData->{ ErrorCategory };
|
||||||
|
my $remainingTerms = $transactionData->{ RecurDetails }->{ RemReps };
|
||||||
|
|
||||||
|
# Uppercase the status b/c the documentation is not clear on the case.
|
||||||
|
my $isSuccess = uc( $status ) eq 'OK' && $remainingTerms == 0;
|
||||||
|
|
||||||
|
return ( $isSuccess, $status, "$errorMessage Category: $errorCategory" );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# Connection Error
|
||||||
|
$session->errorHandler->info("Connection error");
|
||||||
|
|
||||||
|
return ( 0, undef, 'ConnectionError', $response->status_line );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
sub checkRecurringTransaction {
|
||||||
|
my $self = shift;
|
||||||
|
my $xid = shift;
|
||||||
|
my $expectedAmount = shift;
|
||||||
|
my $session = $self->session;
|
||||||
|
|
||||||
|
my $xmlStructure = {
|
||||||
|
GatewayInterface => {
|
||||||
|
VendorIdentification => {
|
||||||
|
VendorId => $self->get('vendorId'),
|
||||||
|
VendorPassword => $self->get('password'),
|
||||||
|
HomePage => ,
|
||||||
|
},
|
||||||
|
RecurDetails => {
|
||||||
|
OperiationXID => $xid,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
my $xml =
|
||||||
|
'<?xml version="1.0" standalone="yes"?>'
|
||||||
|
. XMLout( $xmlStructure,
|
||||||
|
NoAttr => 1,
|
||||||
|
KeepRoot => 1,
|
||||||
|
KeyAttr => [],
|
||||||
|
);
|
||||||
|
|
||||||
|
my $response = $self->doXmlRequest( $xml, 1 );
|
||||||
|
|
||||||
|
if ($response->is_success) {
|
||||||
|
$session->errorHandler->info("Check recurring postback response: [".$response->content."]");
|
||||||
|
# We got some XML back from iTransact, now parse it.
|
||||||
|
my $transactionResult = XMLin( $response->content || '<empty></empty>');
|
||||||
|
|
||||||
|
unless (defined $transactionResult->{ RecurDetailsResponse }) {
|
||||||
|
# Something went wrong.
|
||||||
|
$session->errorHandler->info("Check recurring postback failed!");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
$session->errorHandler->info("Check recurring postback! Response: [".$response->content."]");
|
||||||
|
|
||||||
|
my $data = $transactionResult->{ RecurDetailsResponse };
|
||||||
|
|
||||||
|
my $status = $data->{ Status };
|
||||||
|
my $amount = $data->{ RecurDetails }->{ RecurTotal };
|
||||||
|
|
||||||
|
$session->errorHandler->info("Check recurring postback! Status: $status");
|
||||||
|
if ( $amount != $expectedAmount ) {
|
||||||
|
$session->errorHandler->info(
|
||||||
|
"Check recurring postback, received amount: $amount not equal to expected amount: $expectedAmount"
|
||||||
|
);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# Connection Error
|
||||||
|
$session->errorHandler->info("Connection error");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
sub definition {
|
||||||
|
my $class = shift;
|
||||||
|
my $session = shift;
|
||||||
|
my $definition = shift;
|
||||||
|
|
||||||
|
my $i18n = WebGUI::International->new($session, 'PayDriver_ITransact');
|
||||||
|
|
||||||
|
tie my %fields, 'Tie::IxHash';
|
||||||
|
%fields = (
|
||||||
|
vendorId => {
|
||||||
|
fieldType => 'text',
|
||||||
|
label => $i18n->echo('vendorId'),
|
||||||
|
hoverHelp => $i18n->echo('vendorId help'),
|
||||||
|
},
|
||||||
|
password => {
|
||||||
|
fieldType => 'password',
|
||||||
|
label => $i18n->echo('password'),
|
||||||
|
hoverHelp => $i18n->echo('password help'),
|
||||||
|
},
|
||||||
|
useCVV2 => {
|
||||||
|
fieldType => 'yesNo',
|
||||||
|
label => $i18n->echo('use cvv2'),
|
||||||
|
hoverHelp => $i18n->echo('use cvv2 help'),
|
||||||
|
},
|
||||||
|
emailMessage => {
|
||||||
|
fieldType => 'textarea',
|
||||||
|
label => $i18n->echo('emailMessage'),
|
||||||
|
hoverHelp => $i18n->echo('emailMessage help'),
|
||||||
|
},
|
||||||
|
# readonly stuff from old plugin here?
|
||||||
|
);
|
||||||
|
|
||||||
|
push @{ $definition }, {
|
||||||
|
name => $i18n->echo('Itransact'),
|
||||||
|
properties => \%fields,
|
||||||
|
};
|
||||||
|
|
||||||
|
return $class->SUPER::definition($session, $definition);
|
||||||
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 doXmlRequest ( xml [ isAdministrative ] )
|
||||||
|
|
||||||
|
Post an xml request to the ITransact backend. Returns a LWP::UserAgent response object.
|
||||||
|
|
||||||
|
=head3 xml
|
||||||
|
|
||||||
|
The xml string. Must contain a valid xml header.
|
||||||
|
|
||||||
|
=head3 isGatewayInterface
|
||||||
|
|
||||||
|
Determines what kind of request the xml is. For GatewayRequests set this value to 1. For SaleRequests set to 0 or
|
||||||
|
undef.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub doXmlRequest {
|
||||||
|
my $self = shift;
|
||||||
|
my $xml = shift;
|
||||||
|
my $isGatewayInterface = shift;
|
||||||
|
|
||||||
|
# Figure out which cgi script we should post the XML to.
|
||||||
|
my $xmlTransactionScript = $isGatewayInterface
|
||||||
|
? 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans2.cgi'
|
||||||
|
: 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans.cgi'
|
||||||
|
;
|
||||||
|
# Set up LWP
|
||||||
|
my $userAgent = LWP::UserAgent->new;
|
||||||
|
$userAgent->env_proxy;
|
||||||
|
$userAgent->agent("WebGUI");
|
||||||
|
|
||||||
|
# Create a request and stuff the xml in it
|
||||||
|
my $request = HTTP::Request->new( POST => $xmlTransactionScript );
|
||||||
|
$request->content_type( 'application/x-www-form-urlencoded' );
|
||||||
|
$request->content( 'xml='.$xml );
|
||||||
|
|
||||||
|
# Do the request
|
||||||
|
my $response = $userAgent->request($request);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub getButton {
|
sub getButton {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
@ -371,6 +439,18 @@ sub getButton {
|
||||||
return $payForm;
|
return $payForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 handlesRecurring
|
||||||
|
|
||||||
|
Tells the commerce system that this payment plugin can handle recurring payments.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub handlesRecurring {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub processCredentials {
|
sub processCredentials {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
@ -427,18 +507,6 @@ sub processCredentials {
|
||||||
return \@error;
|
return \@error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
|
|
||||||
=head2 handlesRecurring
|
|
||||||
|
|
||||||
Tells the commerce system that this payment plugin can handle recurring payments.
|
|
||||||
|
|
||||||
=cut
|
|
||||||
|
|
||||||
sub handlesRecurring {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub processPayment {
|
sub processPayment {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
@ -449,20 +517,8 @@ sub processPayment {
|
||||||
my $xml = $self->_generatePaymentRequestXML( $transaction );
|
my $xml = $self->_generatePaymentRequestXML( $transaction );
|
||||||
$session->errorHandler->info("XML Request: $xml");
|
$session->errorHandler->info("XML Request: $xml");
|
||||||
|
|
||||||
# Set up LWP
|
# Send the xml to ITransact
|
||||||
my $userAgent = LWP::UserAgent->new;
|
my $response = $self->doXmlRequest( $xml );
|
||||||
$userAgent->env_proxy;
|
|
||||||
$userAgent->agent("WebGUI ");
|
|
||||||
|
|
||||||
# Create a request and stuff the xml in it
|
|
||||||
$session->errorHandler->info('Starting request');
|
|
||||||
my $xmlTransactionScript = 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans.cgi';
|
|
||||||
my $request = HTTP::Request->new( POST => $xmlTransactionScript );
|
|
||||||
$request->content_type( 'application/x-www-form-urlencoded' );
|
|
||||||
$request->content( 'xml='.$xml );
|
|
||||||
|
|
||||||
# Do the request
|
|
||||||
my $response = $userAgent->request($request);
|
|
||||||
|
|
||||||
# Process response
|
# Process response
|
||||||
if ($response->is_success) {
|
if ($response->is_success) {
|
||||||
|
|
@ -501,55 +557,45 @@ sub processPayment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
|
||||||
sub www_processRecurringTransactionPostback {
|
|
||||||
my $self = shift;
|
|
||||||
my $session = $self->session;
|
|
||||||
my $form = $session->form;
|
|
||||||
|
|
||||||
# Get posted data of interest
|
|
||||||
my $originatingXid = $form->process( 'orig_xid' );
|
|
||||||
my $status = $form->process( 'status' );
|
|
||||||
my $xid = $form->process( 'xid' );
|
|
||||||
my $errorMessage = $form->process( 'error_message' );
|
|
||||||
|
|
||||||
# Fetch the original transaction
|
|
||||||
my $baseTransaction = WebGUI::Shop::Transaction->newByGatewayId( $session, $originatingXid, $self->getId );
|
|
||||||
|
|
||||||
# Create a new transaction for this term
|
|
||||||
my $transaction = $baseTransaction->duplicate( {
|
|
||||||
originatingTransactionId => $baseTransaction->getId,
|
|
||||||
});
|
|
||||||
|
|
||||||
# Check the transaction status and act accordingly
|
|
||||||
if ( uc $status eq 'OK' ) {
|
|
||||||
# The term was succesfully payed
|
|
||||||
$transaction->completePurchase( $xid, $status, $errorMessage );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# The term has not been payed succesfully
|
|
||||||
$transaction->denyPurchase( $xid, $status, $errorMessage );
|
|
||||||
}
|
|
||||||
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub www_getCredentials {
|
sub www_getCredentials {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
my $errors = shift;
|
||||||
my $session = $self->session;
|
my $session = $self->session;
|
||||||
my $form = $session->form;
|
my $form = $session->form;
|
||||||
my $i18n = WebGUI::International->new($self->session, 'CommercePaymentITransact');
|
my $i18n = WebGUI::International->new($self->session, 'CommercePaymentITransact');
|
||||||
my $u = WebGUI::User->new($self->session,$self->session->user->userId);
|
my $u = WebGUI::User->new($self->session,$self->session->user->userId);
|
||||||
|
|
||||||
|
# Process address from address book if passed
|
||||||
|
my $addressId = $session->form->process('addressId');
|
||||||
|
my $addressData = {};
|
||||||
|
if ( $addressId ) {
|
||||||
|
$addressData = eval{ $self->getCart->getAddressBook->getAddress( $addressId )->get() } || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
my $output;
|
||||||
|
|
||||||
|
# Process form errors
|
||||||
|
if ( $errors ) {
|
||||||
|
#### TODO: i18n
|
||||||
|
$output .= $i18n->echo('The following errors occurred:')
|
||||||
|
. '<ul><li>' . join( '</li><li>', @{ $errors } ) . '</li></ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$output .= $self->getSelectAddressButton( 'getCredentials' );
|
||||||
|
|
||||||
my $f = WebGUI::HTMLForm->new( $session );
|
my $f = WebGUI::HTMLForm->new( $session );
|
||||||
$self->getDoFormTags( 'pay', $f );
|
$self->getDoFormTags( 'pay', $f );
|
||||||
|
$f->hidden(
|
||||||
|
-name => 'addressId',
|
||||||
|
-value => $addressId,
|
||||||
|
) if $addressId;
|
||||||
|
|
||||||
# Address data form
|
# Address data form
|
||||||
$f->text(
|
$f->text(
|
||||||
-name => 'firstName',
|
-name => 'firstName',
|
||||||
-label => $i18n->get('firstName'),
|
-label => $i18n->get('firstName'),
|
||||||
-value => $form->process("firstName") || $u->profileField('firstName'),
|
-value => $form->process("firstName") || $addressData->{ name } || $u->profileField('firstName'),
|
||||||
);
|
);
|
||||||
$f->text(
|
$f->text(
|
||||||
-name => 'lastName',
|
-name => 'lastName',
|
||||||
|
|
@ -559,32 +605,32 @@ sub www_getCredentials {
|
||||||
$f->text(
|
$f->text(
|
||||||
-name => 'address',
|
-name => 'address',
|
||||||
-label => $i18n->get('address'),
|
-label => $i18n->get('address'),
|
||||||
-value => $form->process("address") || $u->profileField('homeAddress'),
|
-value => $form->process("address") || $addressData->{ address1 } || $u->profileField('homeAddress'),
|
||||||
);
|
);
|
||||||
$f->text(
|
$f->text(
|
||||||
-name => 'city',
|
-name => 'city',
|
||||||
-label => $i18n->get('city'),
|
-label => $i18n->get('city'),
|
||||||
-value => $form->process("city") || $u->profileField('homeCity'),
|
-value => $form->process("city") || $addressData->{ city } || $u->profileField('homeCity'),
|
||||||
);
|
);
|
||||||
$f->text(
|
$f->text(
|
||||||
-name => 'state',
|
-name => 'state',
|
||||||
-label => $i18n->get('state'),
|
-label => $i18n->get('state'),
|
||||||
-value => $form->process("state") || $u->profileField('homeState'),
|
-value => $form->process("state") || $addressData->{ state } || $u->profileField('homeState'),
|
||||||
);
|
);
|
||||||
$f->zipcode(
|
$f->zipcode(
|
||||||
-name => 'zipcode',
|
-name => 'zipcode',
|
||||||
-label => $i18n->get('zipcode'),
|
-label => $i18n->get('zipcode'),
|
||||||
-value => $form->process("zipcode") || $u->profileField('homeZip'),
|
-value => $form->process("zipcode") || $addressData->{ code } || $u->profileField('homeZip'),
|
||||||
);
|
);
|
||||||
$f->country(
|
$f->country(
|
||||||
-name => "country",
|
-name => "country",
|
||||||
-label => $i18n->get("country"),
|
-label => $i18n->get("country"),
|
||||||
-value => ($form->process("country",'country') || $u->profileField("homeCountry") || 'United States'),
|
-value => ($form->process("country",'country') || $addressData->{ country } || $u->profileField("homeCountry") || 'United States'),
|
||||||
);
|
);
|
||||||
$f->phone(
|
$f->phone(
|
||||||
-name => "phone",
|
-name => "phone",
|
||||||
-label => $i18n->get("phone"),
|
-label => $i18n->get("phone"),
|
||||||
-value => $form->process("phone",'phone') || $u->profileField("homePhone"),
|
-value => $form->process("phone",'phone') || $addressData->{ phoneNumber } || $u->profileField("homePhone"),
|
||||||
);
|
);
|
||||||
$f->email(
|
$f->email(
|
||||||
-name => 'email',
|
-name => 'email',
|
||||||
|
|
@ -611,13 +657,15 @@ sub www_getCredentials {
|
||||||
-value => 'Checkout',
|
-value => 'Checkout',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $session->style->userStyle($f->print);
|
$output .= $f->print;
|
||||||
|
return $session->style->userStyle( $output );
|
||||||
}
|
}
|
||||||
|
|
||||||
#-------------------------------------------------------------------
|
#-------------------------------------------------------------------
|
||||||
sub www_pay {
|
sub www_pay {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my $session = $self->session;
|
my $session = $self->session;
|
||||||
|
my $addressId = $session->form->process( 'addressId' ) || undef;
|
||||||
|
|
||||||
# Check whether the user filled in the checkout form and process those.
|
# Check whether the user filled in the checkout form and process those.
|
||||||
my $credentialsErrors = $self->processCredentials;
|
my $credentialsErrors = $self->processCredentials;
|
||||||
|
|
@ -626,12 +674,71 @@ sub www_pay {
|
||||||
return $self->www_getCredentials( $credentialsErrors ) if $credentialsErrors;
|
return $self->www_getCredentials( $credentialsErrors ) if $credentialsErrors;
|
||||||
|
|
||||||
# Payment time!
|
# Payment time!
|
||||||
my $transaction = $self->processTransaction;
|
my $transaction = $self->processTransaction( $addressId );
|
||||||
if ($transaction->get('isSuccessful')) {
|
if ($transaction->get('isSuccessful')) {
|
||||||
return $transaction->thankYou();
|
return $transaction->thankYou();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Payment has failed...
|
||||||
return $self->displayPaymentError($transaction);
|
return $self->displayPaymentError($transaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
sub www_processRecurringTransactionPostback {
|
||||||
|
my $self = shift;
|
||||||
|
my $session = $self->session;
|
||||||
|
my $form = $session->form;
|
||||||
|
|
||||||
|
# Get posted data of interest
|
||||||
|
my $originatingXid = $form->process( 'orig_xid' );
|
||||||
|
my $status = $form->process( 'status' );
|
||||||
|
my $xid = $form->process( 'xid' );
|
||||||
|
my $errorMessage = $form->process( 'error_message' );
|
||||||
|
|
||||||
|
# Fetch the original transaction
|
||||||
|
my $baseTransaction = WebGUI::Shop::Transaction->newByGatewayId( $session, $originatingXid, $self->getId );
|
||||||
|
|
||||||
|
#---- Check the validity of the request -------
|
||||||
|
# First check whether the original transaction actualy exists
|
||||||
|
unless ( $baseTransaction ) {
|
||||||
|
$session->errorHandler->warn->("Check recurring postback: No base transction for XID: [$originatingXid]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Secondly check if the postback is coming from secure.paymentclearing.com
|
||||||
|
# This will most certainly fail on mod_proxied webgui instances
|
||||||
|
# unless ( $ENV{ HTTP_HOST } eq 'secure.paymentclearing.com') {
|
||||||
|
# $session->errorHandler->info('ITransact Recurring Payment Postback is coming from host: ['.$ENV{ HTTP_HOST }.']');
|
||||||
|
# return;
|
||||||
|
# }
|
||||||
|
|
||||||
|
# Third, check if the new xid exists and if the amount is correct.
|
||||||
|
my $expectedAmount = sprintf("%.2f",
|
||||||
|
$baseTransaction->get('amount') + $baseTransaction->get('taxes') + $baseTransaction->get('shippingPrice') );
|
||||||
|
|
||||||
|
unless ( $self->checkRecurringTransaction( $xid, $expectedAmount ) ) {
|
||||||
|
$session->errorHandler->warn('Check recurring postback: transaction check failed.');
|
||||||
|
#return;
|
||||||
|
}
|
||||||
|
#---- Passed all test, continue ---------------
|
||||||
|
|
||||||
|
# Create a new transaction for this term
|
||||||
|
my $transaction = $baseTransaction->duplicate( {
|
||||||
|
originatingTransactionId => $baseTransaction->getId,
|
||||||
|
});
|
||||||
|
|
||||||
|
# Check the transaction status and act accordingly
|
||||||
|
if ( uc $status eq 'OK' ) {
|
||||||
|
# The term was succesfully payed
|
||||||
|
$transaction->completePurchase( $xid, $status, $errorMessage );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# The term has not been payed succesfully
|
||||||
|
$transaction->denyPurchase( $xid, $status, $errorMessage );
|
||||||
|
}
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -843,6 +843,54 @@ our $I18N = {
|
||||||
context => q|The label for the add to cart button.|
|
context => q|The label for the add to cart button.|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'weekly' => {
|
||||||
|
message => q|Week|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a weekly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'biweekly' => {
|
||||||
|
message => q|Two weeks|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a biweekly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'fourweekly' => {
|
||||||
|
message => q|Four weeks|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a four weekly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'monthly' => {
|
||||||
|
message => q|Month|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a monthly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'quarterly' => {
|
||||||
|
message => q|Three months|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a Quarterly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'halfyearly' => {
|
||||||
|
message => q|Half year|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a semi yearly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
'yearly' => {
|
||||||
|
message => q|Year|,
|
||||||
|
lastUpdated => 0,
|
||||||
|
context => q|Period name for a yearly subscription.|
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue