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 14ad93c5e..e2b7a8770 100644
--- a/docs/upgrades/upgrade_6.2.9-6.3.0.sql
+++ b/docs/upgrades/upgrade_6.2.9-6.3.0.sql
@@ -88,3 +88,83 @@ create table layout (
printableStyleTemplateId varchar(22) not null
);
+INSERT INTO settings VALUES ('commerceCheckoutCanceledTemplateId','1');
+INSERT INTO settings VALUES ('commerceConfirmCheckoutTemplateId','1');
+INSERT INTO settings VALUES ('commercePaymentPlugin','PayFlowPro');
+INSERT INTO settings VALUES ('commerceSendDailyReportTo','martin@geefmegeld.nl');
+INSERT INTO settings VALUES ('commerceTransactionErrorTemplateId','1');
+INSERT INTO template VALUES ('1','Subscription code redemption','\r\nBatch: \r\n\r\n\r\n
\r\n','Operation/RedeemSubscription',1,1);
+INSERT INTO template VALUES ('1','Subscriptionitem default template','
\r\n
\r\n
\r\n
\r\n$
\r\n\">Subscribe now
','Macro/SubscriptionItem',1,1);
+INSERT INTO template VALUES ('1','Default transaction error template','\r\n \r\n | Transaction description | \r\n Price | \r\n Status | \r\n Error | \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/TransactionError',1,1);
+INSERT INTO template VALUES ('1','Default checkout confirmation template','
\r\n
\r\n\r\n\r\n\r\n\r\n\r\n \r\n | Subscription \"\" | \r\n : | \r\n $ every | \r\n
\r\n\r\n
\r\n
\r\n\r\n','Commerce/ConfirmCheckout',1,1);
+INSERT INTO template VALUES ('1','Default view purchase history template','\r\n\r\n \r\n | \r\n | \r\n $ | \r\n | \r\n \">Cancel | \r\n
\r\n \r\n \r\n | x | \r\n | \r\n $ | \r\n
\r\n \r\n\r\n
','Commerce/ViewPurchaseHistory',1,1);
+INSERT INTO template VALUES ('1','Default cancel checkout template','','Commerce/CheckoutCanceled',1,1);
+CREATE TABLE shoppingCart (
+ sessionId varchar(22) NOT NULL default '',
+ itemId varchar(64) NOT NULL default '',
+ itemType varchar(40) NOT NULL default '',
+ quantity int(4) NOT NULL default '0',
+ PRIMARY KEY (sessionId,itemId,itemType)
+) TYPE=MyISAM;
+CREATE TABLE subscription (
+ subscriptionId varchar(22) NOT NULL default '',
+ name varchar(128) default NULL,
+ price float default '0',
+ description mediumtext,
+ subscriptionGroup varchar(22) NOT NULL default '',
+ duration varchar(12) NOT NULL default 'Monthly',
+ executeOnSubscription varchar(128) default NULL,
+ karma int(4) default '0',
+ deleted int(1) default '0',
+ PRIMARY KEY (subscriptionId)
+) TYPE=MyISAM;
+CREATE TABLE subscriptionCodeBatch (
+ batchId varchar(22) NOT NULL default '',
+ name varchar(128) default NULL,
+ description mediumtext NOT NULL,
+ subscriptionId varchar(22) NOT NULL default '',
+ PRIMARY KEY (batchId)
+) TYPE=MyISAM;
+CREATE TABLE subscriptionCode (
+ batchId varchar(22) NOT NULL default '',
+ code varchar(64) NOT NULL default '',
+ status varchar(10) NOT NULL default 'Unused',
+ dateCreated int(11) NOT NULL default '0',
+ dateUsed int(11) NOT NULL default '0',
+ expires int(11) NOT NULL default '0',
+ usedBy varchar(22) NOT NULL default '0',
+ PRIMARY KEY (code)
+) TYPE=MyISAM;
+CREATE TABLE subscriptionCodeSubscriptions (
+ code varchar(64) NOT NULL default '',
+ subscriptionId varchar(22) NOT NULL default '',
+ UNIQUE KEY code (code,subscriptionId)
+) TYPE=MyISAM;
+CREATE TABLE transaction (
+ transactionId varchar(22) NOT NULL default '',
+ userId varchar(22) NOT NULL default '',
+ amount float NOT NULL default '0',
+ gatewayId varchar(128) default NULL,
+ gateway varchar(64) NOT NULL default '',
+ recurring tinyint(1) NOT NULL default '0',
+ initDate int(11) NOT NULL default '0',
+ completionDate int(11) default '0',
+ status varchar(10) NOT NULL default 'Pending',
+ lastPayedTerm int(6) NOT NULL default '0',
+ PRIMARY KEY (transactionId)
+) TYPE=MyISAM;
+CREATE TABLE transactionItem (
+ transactionId varchar(22) NOT NULL default '',
+ itemName varchar(64) NOT NULL default '',
+ amount float NOT NULL default '0',
+ quantity int(4) NOT NULL default '0',
+ itemId varchar(64) NOT NULL default '',
+ itemType varchar(40) NOT NULL default ''
+) TYPE=MyISAM;
+CREATE TABLE commerceSettings (
+ fieldName varchar(64) NOT NULL default '',
+ fieldValue varchar(255) NOT NULL default '',
+ namespace varchar(64) NOT NULL default '',
+ type varchar(10) NOT NULL default ''
+) TYPE=MyISAM;
+
diff --git a/sbin/Hourly/ExpireSubscriptionCodes.pm b/sbin/Hourly/ExpireSubscriptionCodes.pm
new file mode 100644
index 000000000..017aaafac
--- /dev/null
+++ b/sbin/Hourly/ExpireSubscriptionCodes.pm
@@ -0,0 +1,12 @@
+package Hourly::ExpireSubscriptionCodes;
+
+use strict;
+use WebGUI::SQL;
+
+#-------------------------------------------------------------------
+sub process {
+ WebGUI::SQL->write("update subscriptionCode set status='Expired' where status = 'Unused' and dateCreated + expires < ".time);
+}
+
+1;
+
diff --git a/sbin/Hourly/ProcessRecurringPayments.pm b/sbin/Hourly/ProcessRecurringPayments.pm
new file mode 100644
index 000000000..689ea7782
--- /dev/null
+++ b/sbin/Hourly/ProcessRecurringPayments.pm
@@ -0,0 +1,76 @@
+package Hourly::ProcessRecurringPayments;
+
+use strict;
+use WebGUI::SQL;
+use WebGUI::Commerce::Payment;
+use WebGUI::Commerce::Transaction;
+use WebGUI::Commerce::Item;
+use WebGUI::DateTime;
+use WebGUI::Session;
+
+sub _getDuration {
+ my $duration = shift;
+
+ return addToDate(0,0,0,7) if $duration eq 'Weekly';
+ return addToDate(0,0,0,14) if $duration eq 'BiWeekly';
+ return addToDate(0,0,0,28) if $duration eq 'FourWeekly';
+ return addToDate(0,0,1,0) if $duration eq 'Monthly';
+ return addToDate(0,0,3,0) if $duration eq 'Quarterly';
+ return addToDate(0,0,6,0) if $duration eq 'HalfYearly';
+ return addToDate(0,1,0,0) if $duration eq 'Yearly';
+}
+
+
+
+
+sub process {
+ my @recurringTransactions = WebGUI::SQL->buildArray("select transactionId from transaction where recurring=1 and status='Completed'");
+
+ my (@unprocessed, @ok, @failed, @fatal);
+ foreach (@recurringTransactions) {
+ my $transaction = WebGUI::Commerce::Transaction->new($_);
+ my $itemProperties = $transaction->getItems->[0];
+ my $item = WebGUI::Commerce::Item->new($itemProperties->{itemId}, $itemProperties->{itemType});
+ my $time = time;
+ $time -= $transaction->get('initDate');
+ my $term = int($time / _getDuration($item->duration)) + 1;
+
+ if ($term > $transaction->lastPayedTerm) {
+ my $payment = WebGUI::Commerce::Payment->load($transaction->gateway);
+
+ $transaction->gatewayId;
+ my $status = $payment->getRecurringPaymentStatus($transaction->gatewayId, $term);
+
+ my $output = $item->name." (tid: ".$transaction->get('transactionId').") ";
+ $output .= " by user ".WebGUI::User->new($transaction->get(userId))->username." (uid: ".$transaction->get(userId).") ";
+ $output .= " for term ". sprintf('% 6d', $term)." ";
+ $output .= " -> ".$transaction->gateway.": (".$transaction->gatewayId.")\t";
+ unless ($payment->resultCode) {
+ unless (defined $status) {
+ $output .= "NOT PROCESSED YET";
+ push (@unprocessed, $output);
+ } elsif ($status->{resultCode} eq '0') {
+ $output .= "OK";
+ push (@ok, $output);
+ $item->apply unless ($term == 1);
+ $transaction->lastPayedTerm($term);
+ } else {
+ $output .= "PAYMENT FAILED: ".$status->{resultCode};
+ push (@failed, $output);
+ }
+ } else {
+ $output .= "FATAL ERROR: ".$payment->resultMessage." (".$payment->errorCode.")";
+ }
+ }
+ }
+
+ my $message = "FAILED PAYMENTS:\n-----------------------------\n".join("\n", @failed)."\n\n\n";
+ $message .= "UNPROCESSED PAYMENTS:\n-----------------------------\n".join("\n", @unprocessed)."\n\n\n";
+ $message .= "FATAL ERRORS:\n-----------------------------\n".join("\n",@fatal)."\n\n\n";
+ $message .= "SUCCESFUL PAYMENTS:\n-----------------------------\n".join("\n", @ok)."\n\n\n";
+
+ WebGUI::Mail::send($session{setting}{commerceSendDailyReportTo}, 'Daily recurring payments report', $message);
+}
+
+1;
+
diff --git a/www/extras/adminConsole/commerce.gif b/www/extras/adminConsole/commerce.gif
new file mode 100644
index 000000000..b0f570e10
Binary files /dev/null and b/www/extras/adminConsole/commerce.gif differ
diff --git a/www/extras/adminConsole/subscriptions.gif b/www/extras/adminConsole/subscriptions.gif
new file mode 100644
index 000000000..b0f570e10
Binary files /dev/null and b/www/extras/adminConsole/subscriptions.gif differ