diff --git a/docs/upgrades/upgrade_6.2.9-6.3.0.sql b/docs/upgrades/upgrade_6.2.9-6.3.0.sql index ff8ef09c6..a936c835c 100644 --- a/docs/upgrades/upgrade_6.2.9-6.3.0.sql +++ b/docs/upgrades/upgrade_6.2.9-6.3.0.sql @@ -179,6 +179,14 @@ CREATE TABLE commerceSettings ( namespace varchar(64) NOT NULL default '', type varchar(10) NOT NULL default '' ) TYPE=MyISAM; +create table ITransact_recurringStatus ( + gatewayId varchar(128) not null primary key, + initDate int(11) not null, + lastTransaction int(11) not null, + status varchar(10) not null, + errorMessage varchar(128), + recipe varchar(15) not null +); INSERT INTO template VALUES ('1','Default payment gateway selection template','\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n \r\n \r\n\r\n \r\n
','Commerce/SelectPaymentGateway',1,1); diff --git a/lib/WebGUI/Commerce/Payment/ITransact.pm b/lib/WebGUI/Commerce/Payment/ITransact.pm new file mode 100644 index 000000000..726ae3765 --- /dev/null +++ b/lib/WebGUI/Commerce/Payment/ITransact.pm @@ -0,0 +1,571 @@ +package WebGUI::Commerce::Payment::ITransact; + +use strict; +use WebGUI::Session; +use WebGUI::HTMLForm; +use WebGUI::Commerce::Payment; +use WebGUI::Commerce::Item; +use Tie::IxHash; +use WebGUI::International; +use LWP::UserAgent; +use XML::Simple; +use HTTP::Cookies; + +our @ISA = qw(WebGUI::Commerce::Payment); + +#------------------------------------------------------------------- +sub _resolveRecipe { + my %resolve = ( + Weekly => 'weekly', + BiWeekly => 'biweekly', + FourWeekly => 'fourweekly', + Monthly => 'monthly', + Quarterly => 'quarterly', + HalfYearly => 'halfyearly', + Yearly => 'yearly', + ); + + return $resolve{$_[0]}; +} + +#------------------------------------------------------------------- +sub cancelRecurringPayment { + my ($self, $recurring, $userAgent, $request, $response); + $self = shift; + $recurring = shift; + + if ($recurring) { + $self->{_recurring} = 1; + + my $itemProperties = $recurring->{transaction}->getItems->[0]; + my $item = WebGUI::Commerce::Item->new($itemProperties->{itemId}, $itemProperties->{itemType}); + my $recipe = _resolveRecipe($item->duration); + + # Set up a user agent that uses cookies and allows POST redirects + $userAgent = LWP::UserAgent->new; + $userAgent->agent("Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.7.5) Gecko/20041107 Firefox/1.0"); + push @{ $userAgent->requests_redirectable }, 'POST'; + $userAgent->cookie_jar({}); + + # Login to iTransact + $request = HTTP::Request->new(POST => 'https://secure.paymentclearing.com/cgi-bin/rc/sess.cgi'); + $request->content_type('application/x-www-form-urlencoded'); + $request->content('mid='.$self->get('vendorId').'&pwd='.$self->get('password').'&cookie_precheck=0'); + + $response = $userAgent->request($request); + + # Check the outcome of the response + if ($response->is_success) { +# print "FIRST PAGE SUCCESS!\n"; +# print "(".$response->base.")\n"; + } else { + WebGUI::ErrorHandler::fatalError( + 'Connection Error while trying to cancel transaction '.$recurring->{transaction}->transactionId." \n". + "Could not reach login page.\n". + "(".$response->base.")\n". + $response->status_line. "\n"); + } + + + # Post cancelation + my $request = HTTP::Request->new(POST => 'https://secure.paymentclearing.com/cgi-bin/rc/recur/update/update.cgi'); + $request->content_type('application/x-www-form-urlencoded'); + $request->content( + 'reps=0&'. # Set number of remaining repetition to zero in order to cancel + 'recipe_code='.$recipe.'&'. + 'xid='.$recurring->{id}); + + my $response = $userAgent->request($request); + + # Check the outcome of the response + if ($response->is_success) { +# print "CANCELATION PAGE SUCCESS!\n"; +# print "(".$response->base.")\n"; + } else { + WebGUI::ErrorHandler::fatalError( + 'Connection Error while trying to cancel transaction '.$recurring->{transaction}->transactionId." \n". + "(".$response->base.")\n". + $response->status_line. "\n"); + } + } +} + +#------------------------------------------------------------------- +sub connectionError { + my ($self, $resultCode); + $self = shift; + + return $self->resultMessage if ($self->{_connectionError}); + return undef; +} + +#------------------------------------------------------------------- +sub checkoutForm { + my ($self, $u, $f, %months, %years, $i18n); + $self = shift; + + $i18n = WebGUI::International->new('CommercePaymentITransact'); + + $u = WebGUI::User->new($session{user}{userId}); + + $f = WebGUI::HTMLForm->new; + $f->text( + -name => 'firstName', + -label => $i18n->get('firstName'), + -value => $session{form}{firstName} || $u->profileField('firstName') + ); + $f->text( + -name => 'lastName', + -label => $i18n->get('lastName'), + -value => $session{form}{lastName} || $u->profileField('lastName') + ); + $f->text( + -name => 'address', + -label => $i18n->get('address'), + -value => $session{form}{address} || $u->profileField('homeAddress') + ); + $f->text( + -name => 'city', + -label => $i18n->get('city'), + -value => $session{form}{city} || $u->profileField('homeCity') + ); + $f->text( + -name => 'state', + -label => $i18n->get('state'), + -value => $session{form}{state} || $u->profileField('homeState') + ); + $f->zipcode( + -name => 'zipcode', + -label => $i18n->get('zipcode'), + -value => $session{form}{zipcode} || $u->profileField('homeZip') + ); + $f->email( + -name => 'email', + -label => $i18n->get('email'), + -value => $session{form}{email} || $u->profileField('email') + ); + $f->text( + -name => 'cardNumber', + -label => $i18n->get('cardNumber'), + -value => $session{form}{cardNumber} + ); + tie %months, "Tie::IxHash"; + %months = map {sprintf('%02d',$_) => sprintf('%02d',$_)} 1..12; + tie %years, "Tie::IxHash"; + %years = map {$_ => $_} 2004..2099; + $f->readOnly( + -label => $i18n->get('expiration date'), + -value => + WebGUI::Form::selectList({name => 'expMonth', options => \%months, value => [$session{form}{expMonth}]}). + " / ". + WebGUI::Form::selectList({name => 'expYear', options => \%years, value => [$session{form}{expYear}]}) + ); + $f->integer( + -name => 'cvv2', + -label => $i18n->get('cvv2'), + -value => $session{form}{cvv2} + ) if ($self->get('useCVV2')); + + return $f->printRowsOnly; +} + +#------------------------------------------------------------------- +sub configurationForm { + my ($self, $f, $i18n); + $self = shift; + $i18n = WebGUI::International->new('CommercePaymentITransact'); + + $f = WebGUI::HTMLForm->new; + $f->text( + -name => $self->prepend('vendorId'), + -label => $i18n->get('vendorId'), + -value => $self->get('vendorId') + ); + $f->text( + -name => $self->prepend('password'), + -label => $i18n->get('password'), + -value => $self->get('password') + ); + $f->yesNo( + -name => $self->prepend('useCVV2'), + -label => $i18n->get('use cvv2'), + -value => $self->get('useCVV2'), + ); + $f->textarea( + -name => $self->prepend('emailMessage'), + -label => $i18n->get('emailMessage'), + -value => $self->get('emailMessage') + ); + $f->readOnly( + -value => '
' + ); + if ($self->get('vendorId')) { + $f->readOnly( + -value => ''.$i18n->get('show terminal').'' + ); + } else { + $f->readOnly( + -value => ''. + $i18n->get('set up account').'' + ); + } + $f->readOnly( + -value => '
' + ); + $f->readOnly( + -value => $i18n->get('extra info').'
'.$session{setting}{companyURL}.'?op=confirmRecurringTransaction&gateway='.$self->namespace + ); + + return $self->SUPER::configurationForm($f->printRowsOnly); +} + +#------------------------------------------------------------------- +sub confirmRecurringTransaction { + #### !!!Site checken!!! #### + my $self = shift; + + my $form = $session{form}; + my $transaction = WebGUI::Commerce::Transaction->getByGatewayId($session{form}{orig_xid}, $self->namespace); + my $itemProperties = $transaction->getItems->[0]; + my $item = WebGUI::Commerce::Item->new($itemProperties->{itemId}, $itemProperties->{itemType}); + + my $startEpoch = WebGUI::DateTime::setToEpoch(sprintf("%4d-%02d-%02d %02d:%02d:%02d", unpack('a4a2a2a2a2a2', $form->{start_date}))); + my $currentEpoch = WebGUI::DateTime::setToEpoch(sprintf("%4d-%02d-%02d %02d:%02d:%02d", unpack('a4a2a2a2a2a2', $form->{when}))); + + WebGUI::SQL->write("delete from ITransact_recurringStatus where gatewayId=".quote($form->{orig_xid})); + WebGUI::SQL->write("insert into ITransact_recurringStatus ". + "(gatewayId, initDate, lastTransaction, status, errorMessage, recipe) values ". + "(".quote($form->{orig_xid}).", $startEpoch, $currentEpoch, ".quote($form->{status}).", ".quote($form->{error_message}). + ", ".quote($form->{recipe_name}).")"); +} + +#------------------------------------------------------------------- +sub confirmTransaction { + # This function should never be called with site side payment gateways! + return 0; +} + +#------------------------------------------------------------------- +sub init { + my ($class, $self); + $class = shift; + + $self = $class->SUPER::init('ITransact'); + + return $self; +} + +#------------------------------------------------------------------- +sub gatewayId { + my $self = shift; + + return $self->{_response}->{XID}; +} + +#------------------------------------------------------------------- +sub getRecurringPaymentStatus { + my ($self, $term, $recurringId, $response, %paymentHistory); + $self = shift; + $recurringId = shift; + $term = shift || 1; + + my %resolve = { + weekly => 7*3600*24, + biweekly => 14*3600*24, + fourweekly => 28*3600*24, + monthly => 30*3600*24, + quarterly => 91*3600*24, + halfyearly => 182*3600*24, + yearly => 365*3600*24 + }; + + my $transactionData = WebGUI::SQL->quickHashRef("select * from ITransact_recurringStatus where gatewayId=".quote($recurringId)); + + my $lastTerm = int(($transactionData->{lastTransaction} - $transactionData->{initDate}) / $resolve{$transactionData->{recipe}}) + 1; + + # Process the response + + if ($lastTerm > $term) { + $paymentHistory{resultCode} = 0; + } elsif ($lastTerm == $term) { + $paymentHistory{resultCode} = $transactionData->{status}.' '.$transactionData->{errorMessage}; + $paymentHistory{resultCode} = 0 if $transactionData->{status} eq 'OK'; + } else { + return undef; + } + + return \%paymentHistory; +} + +#------------------------------------------------------------------- +sub errorCode { + my ($self, $resultCode); + $self = shift; + + $resultCode = $self->{_response}->{Status}; + return $resultCode unless ($resultCode eq 'OK'); + return undef; +} + +#------------------------------------------------------------------- +sub name { + return WebGUI::International::get('module name', "CommercePaymentITransact"); +} + +#------------------------------------------------------------------- +sub namespace { + my $self = shift; + + return $self->{_namespace}; +} + +#------------------------------------------------------------------- +sub normalTransaction { + my ($self, $normal); + $self = shift; + $normal = shift; + + if ($normal) { + $self->{_recurring} = 0; + $self->{_transactionParams} = { + AMT => sprintf('%.2f', $normal->{amount}), + DESCRIPTION => $normal->{description} || WebGUI::International::get('no description', "CommercePaymentITransact"), + INVOICENUMBER => $normal->{invoiceNumber}, + ORGID => $normal->{id}, + }; + } + + return $self->submit; +} + +#------------------------------------------------------------------- +sub recurringTransaction { + my ($self, $recurring, $initialAmount); + $self = shift; + $recurring = shift; + + + if ($recurring) { + # initial amount = (daysInMonth - dayInMonth) / daysInMonth * amount + $initialAmount = (WebGUI::DateTime::getDaysInMonth(time) - (WebGUI::DateTime::localtime)[2])*$recurring->{amount}/WebGUI::DateTime::getDaysInMonth(time); + $self->{_recurring} = 1; + $self->{_transactionParams} = { + START => $recurring->{start} || WebGUI::DateTime::epochToHuman(WebGUI::DateTime::addToDate(time, 0, 0, 1), '%m%d%y'), + AMT => sprintf('%.2f', $recurring->{amount}), + INITIALAMT => sprintf('%.2f', $initialAmount), + TERM => $recurring->{term} || 9999, + RECIPE => _resolveRecipe($recurring->{payPeriod}), + DESCRIPTION => $recurring->{description} || WebGUI::International::get('no description', "CommercePaymentITransact"), + INVOICENUMBER => $recurring->{invoiceNumber}, + ORGID => $recurring->{id}, + }; + } + + return $self->submit; +} + +#------------------------------------------------------------------- +sub resultCode { + my $self = shift; + + return $self->{_response}->{Status}; +} + +#------------------------------------------------------------------- +sub resultMessage { + my $self = shift; + + return $self->{_errorMessage} if ($self->connectionError); + return $self->{_response}->{ErrorMessage}; +} + +#------------------------------------------------------------------- +sub submit { + my ($self, $xml, $items); + $self = shift; + +my %cardData = %{$self->{_cardData}} if $self->{_cardData}; +my %userData = %{$self->{_userData}} if $self->{_userData}; +my %transactionData = %{$self->{_transactionParams}}; + + # Set up the XML. + + $xml = +''. +" + + $userData{EMAIL} + + $userData{STREET} + $userData{FIRSTNAME} + $userData{LASTNAME} + $userData{CITY} + $userData{STATE} + $userData{ZIP} + USA + 230-555-1212 + + + + $cardData{ACCT} + $cardData{EXPMONTH} + $cardData{EXPYEAR}\n"; + + $xml .= "$cardData{CVV2}\n" if $self->get('useCVV2'); +# 1 + $xml .= +" + + + + ".$self->get('vendorId')." + ".$self->get('password')." + ".$session{setting}{companyURL}.""; + + if ($self->{_recurring}) { + $xml .= +" + $transactionData{RECIPE} + $transactionData{TERM} + $transactionData{INITIALAMT} + $transactionData{DESCRIPTION} + "; + }; + + $xml .= +" + ".$self->get('emailMessage')." + ID: $transactionData{ORGID} + \n"; + + $items = WebGUI::Commerce::Transaction->new($transactionData{ORGID})->getItems; + foreach (@{$items}) { + $xml .= +" + + ".$_->{itemName}." + ".$_->{amount}." + ".$_->{quantity}." + \n"; + } + + $xml .= +" + +"; + + +my $xmlTransactionScript = 'https://secure.paymentclearing.com/cgi-bin/rc/xmltrans.cgi'; + + # Set up LWP to post the XML to iTransact. +my $userAgent = LWP::UserAgent->new; + $userAgent->agent("WebGUI "); + +my $request = HTTP::Request->new(POST => $xmlTransactionScript); + $request->content_type('application/x-www-form-urlencoded'); + $request->content('xml='.$xml); + +my $response = $userAgent->request($request); + + if ($response->is_success) { + # We got some XML back from iTransact, now parse it. +my $xmlParser = XML::Simple->new; +my $transactionResult = $xmlParser->XMLin($response->content); + + unless (defined $transactionResult->{TransactionData}) { + # Some error occurred + $self->{_transactionError} = 1; + $self->{_response} = $transactionResult; + $self->{_resultMessage} = $self->{_response}->{ErrorMessage}; + } else { + $self->{_response} = $transactionResult->{TransactionData}; + } + } else { + # Connection Error + $self->{_connectionError} = 1; + $self->{_resultMessage} = $response->status_line; + } +} + +#------------------------------------------------------------------- +sub supports { + return { + single => 1, + recurring => 1, + } +} + +#------------------------------------------------------------------- +sub transactionCompleted { + my ($self) = shift; + return ($self->{_response}->{Status} eq 'OK'); +} + +#------------------------------------------------------------------- +sub transactionError { + my ($self, $resultCode); + $self = shift; + + $resultCode = $self->resultCode; + return $self->resultMessage if ($resultCode ne 'OK'); + return undef; +} + +#------------------------------------------------------------------- +sub transactionPending { + return 0; +} + +#------------------------------------------------------------------- +sub validateFormData { + my ($self, @error, $i18n, $currentYear, $currentMonth); + $self = shift; + + $i18n = WebGUI::International->new('CommercePaymentITransact'); + + push (@error, $i18n->get('invalid firstName')) unless ($session{form}{firstName}); + push (@error, $i18n->get('invalid lastName')) unless ($session{form}{lastName}); + push (@error, $i18n->get('invalid address')) unless ($session{form}{address}); + push (@error, $i18n->get('invalid city')) unless ($session{form}{city}); + push (@error, $i18n->get('invalid zip')) unless ($session{form}{zipcode}); + push (@error, $i18n->get('invalid email')) unless ($session{form}{email}); + + push (@error, $i18n->get('invalid card number')) unless ($session{form}{cardNumber} =~ /^\d+$/); + push (@error, $i18n->get('invalid cvv2')) if ($session{form}{cvv2} !~ /^\d+$/ && $self->get('useCVV2')); + + ($currentYear, $currentMonth) = WebGUI::DateTime::localtime; + + # Check if expDate and expYear have sane values + unless (($session{form}{expMonth} =~ /^(0[1-9]|1[0-2])$/) && ($session{form}{expYear} =~ /^\d\d\d\d$/)) { + push (@error, $i18n->get('invalid expiration date')); + } elsif (($session{form}{expYear} < $currentYear) || + (($session{form}{expYear} == $currentYear) && ($session{form}{expMonth} < $currentMonth))) { + push (@error, $i18n->get('invalid expiration date')); + } + + unless (@error) { + $self->{_cardData} = { + ACCT => $session{form}{cardNumber}, + EXPMONTH => $session{form}{expMonth}, + EXPYEAR => $session{form}{expYear}, + CVV2 => $session{form}{cvv2}, + }; + + $self->{_userData} = { + STREET => $session{form}{address}, + ZIP => $session{form}{zipcode}, + CITY => $session{form}{city}, + FIRSTNAME => $session{form}{firstName}, + LASTNAME => $session{form}{lastName}, + EMAIL => $session{form}{email}, + STATE => $session{form}{state}, + }; + + return 0; + } + + return \@error; +} + +1; + diff --git a/lib/WebGUI/Commerce/Transaction.pm b/lib/WebGUI/Commerce/Transaction.pm index 24201d6d8..c0a1d5be8 100644 --- a/lib/WebGUI/Commerce/Transaction.pm +++ b/lib/WebGUI/Commerce/Transaction.pm @@ -42,7 +42,8 @@ sub cancelTransaction { $plugin = WebGUI::Commerce::Payment->load($self->gateway); $plugin->cancelRecurringPayment({ - id => $self->gatewayId + id => $self->gatewayId, + transaction => $self, }); return $plugin->resultMessage.' (code: '.$plugin->errorCode.')' if ($plugin->errorCode); @@ -112,6 +113,20 @@ sub get { return $self->{_properties}; } +#------------------------------------------------------------------- +sub getByGatewayId { + my ($self, $gatewayId, $paymentGateway, $transactionId); + $self = shift; + $gatewayId = shift; + $paymentGateway = shift; + + ($transactionId) = WebGUI::SQL->quickArray("select transaction Id from transaction where gatewayId=".quote($gatewayId). + " and gateway=".quote($paymentGateway)); + + return WebGUI::Commerce::Transaction->new($transactionId) if $transactionId; + return undef; +} + #------------------------------------------------------------------- sub getItems { my ($self); diff --git a/lib/WebGUI/Operation/Commerce.pm b/lib/WebGUI/Operation/Commerce.pm index 116aa4cc7..7e38f58b2 100644 --- a/lib/WebGUI/Operation/Commerce.pm +++ b/lib/WebGUI/Operation/Commerce.pm @@ -224,6 +224,16 @@ sub www_completePendingTransaction { return WebGUI::Operation::execute('listPendingTransactions'); } +#------------------------------------------------------------------- +sub www_confirmRecurringTransaction { + my($plugin, %var); + $plugin = WebGUI::Commerce::Payment->load($session{form}{gateway}); + + if ($plugin) { + $plugin->confirmRecurringTransaction; + } +} + #------------------------------------------------------------------- sub www_confirmTransaction { my($plugin, %var); @@ -288,7 +298,7 @@ sub www_editCommerceSettings { $plugin = WebGUI::Commerce::Payment->load($_); if ($plugin) { push(@paymentPlugins, $plugin); - $paymentPlugins{$_} = $_; + $paymentPlugins{$_} = $plugin->name; } else { push(@failedPaymentPlugins, $_); } diff --git a/lib/WebGUI/i18n/English/CommercePaymentITransact.pm b/lib/WebGUI/i18n/English/CommercePaymentITransact.pm new file mode 100755 index 000000000..ee09365ac --- /dev/null +++ b/lib/WebGUI/i18n/English/CommercePaymentITransact.pm @@ -0,0 +1,195 @@ +package WebGUI::i18n::English::CommercePaymentITransact; + +our $I18N = { + 'firstName' => { + message => q|First name|, + lastUpdated => 0, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'lastName' => { + message => q|Last name|, + lastUpdated => 0, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'address' => { + message => q|Address|, + lastUpdated => 1101772170, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'city' => { + message => q|City|, + lastUpdated => 1101772171, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'state' => { + message => q|State|, + lastUpdated => 1101772173, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'zipcode' => { + message => q|Zipcode|, + lastUpdated => 1101772174, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'email' => { + message => q|Email|, + lastUpdated => 1101772176, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'cardNumber' => { + message => q|Credit card number|, + lastUpdated => 1101772177, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'expiration date' => { + message => q|Expiration date|, + lastUpdated => 1101772180, + context => q|Form label in the checkout form of the iTransact module.| + }, + 'cvv2' => { + message => q|Verification number (ie. CVV2)|, + lastUpdated => 1101772182, + context => q|Form label in the checkout form of the iTransact module.| + }, + + 'vendorId' => { + message => q|Username (Vendor ID)|, + lastUpdated => 0, + context => q|Form label in the configuration form of the iTransact module.| + }, + 'use cvv2' => { + message => q|Use CVV2|, + lastUpdated => 0, + context => q|Form label in the configuration form of the iTransact module.| + }, + 'emailMessage' => { + message => q|Email message|, + lastUpdated => 0, + context => q|Form label in the configuration form of the iTransact module.| + }, + 'password' => { + message => q|Password|, + lastUpdated => 0, + context => q|Form label in the configuration form of the iTransact module.| + }, + + 'module name' => { + message => q|CDG Commerce|, + lastUpdated => 0, + context => q|The displayed name of the Payflo Pro payment module.| + }, + + 'invalid firstName' => { + message => q|You have to enter a valid first name.|, + lastUpdated => 0, + context => q|An error indicating that an invalid first name has been entered.| + }, + 'invalid lastName' => { + message => q|You have to enter a valid last name.|, + lastUpdated => 0, + context => q|An error indicating that an invalid last name has been entered.| + }, + 'invalid address' => { + message => q|You have to enter a valid address.|, + lastUpdated => 0, + context => q|An error indicating that an invalid street has been entered.| + }, + 'invalid city' => { + message => q|You have to enter a valid city.|, + lastUpdated => 0, + context => q|An error indicating that an invalid city has been entered.| + }, + 'invalid zip' => { + message => q|You have to enter a valid zipcode.|, + lastUpdated => 0, + context => q|An error indicating that an invalid zipcode has been entered.| + }, + 'invalid email' => { + message => q|You have to enter a valid email address.|, + lastUpdated => 0, + context => q|An error indicating that an invalid email address has been entered.| + }, + 'invalid card number' => { + message => q|You have to enter a valid credit card number.|, + lastUpdated => 0, + context => q|An error indicating that an invalid credit card number has been entered.| + }, + 'invalid cvv2' => { + message => q|You have to enter a valid card security code (ie. cvv2).|, + lastUpdated => 0, + context => q|An error indicating that an invalid card security code has been entered.| + }, + 'invalid expiration date' => { + message => q|You have to enter a valid expiration date.|, + lastUpdated => 0, + context => q|An error indicating that an invalid expiration date has been entered.| + }, + 'no description' => { + message => q|No description|, + lastUpdated => 0, + context => q|The default description of purchase of users.| + }, + 'show terminal' => { + message => q|Click here to use your virtual terminal.|, + lastUpdated => 0, + context => q|The label of the link that points to the virtual terminal login.| + }, + 'set up account' => { + message => q|Register for a merchant account now to get started processing online transactions.|, + lastUpdated => 0, + context => q|The label of the link that points to the account creation page of CDG.| + }, + 'extra info' => { + message => q|This plugin expects that you set up the following recipe's in your CDG account. Be very careful to enter the recipe names exactly as given below.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
weekly -> 7 days
biweekly -> 14 days
fourweekly -> 28 days
monthly -> 30 days
quarterly -> 91 days
halfyearly -> 182 days
yearly -> 365 days

+Please note that some of these recipe's are only roughly correct. They don't 'fit' exactly in a whole year. Below the affected recipe's are given together with their difference on a year's basis.
+
+Also set the 'RECURRING POST-BACK URL' field in the Account Settings part of the virtual terminal to:|, + lastUpdated => 0, + context => q|An informational message that's shown in the configuration form of this plugin.| + }, +}; + +1; +