mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-04-26 07:47 pm

[dw-free] Finishing up payment system

[commit: http://hg.dwscoalition.org/dw-free/rev/9cc8ea02116c]

http://bugs.dwscoalition.org/show_bug.cgi?id=116

Lots of updates for the payment system. Now has receipts, order history.

Patch by [personal profile] janinedog.

Files modified:
  • bin/upgrading/en.dat
  • bin/upgrading/update-db-general.pl
  • cgi-bin/DW/Logic/MenuNav.pm
  • cgi-bin/DW/Shop.pm
  • cgi-bin/DW/Shop/Cart.pm
  • cgi-bin/DW/Shop/Engine/CheckMoneyOrder.pm
  • cgi-bin/DW/Shop/Engine/PayPal.pm
  • cgi-bin/LJ/Setting/Display/Orders.pm
  • doc/config-local.pl.txt
  • htdocs/manage/settings/index.bml
  • htdocs/shop.bml
  • htdocs/shop.bml.text
  • htdocs/shop/cancel.bml
  • htdocs/shop/cancel.bml.text
  • htdocs/shop/checkout.bml
  • htdocs/shop/checkout.bml.text
  • htdocs/shop/cmo_confirm.bml
  • htdocs/shop/confirm.bml
  • htdocs/shop/confirm.bml.text
  • htdocs/shop/history.bml
  • htdocs/shop/history.bml.text
  • htdocs/shop/pp_confirm.bml
  • htdocs/shop/receipt.bml
  • htdocs/shop/receipt.bml.text
--------------------------------------------------------------------------------
diff -r 3f7acfee7d4d -r 9cc8ea02116c bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Sun Apr 26 19:12:29 2009 +0000
+++ b/bin/upgrading/en.dat	Sun Apr 26 19:47:15 2009 +0000
@@ -2187,6 +2187,10 @@ menunav.read.syndicatedfeeds=Syndicated 
 
 menunav.read.tags=Tags
 
+menunav.shop=Shop
+
+menunav.shop.paidtime=Buy a Paid Account
+
 notification_method.email.title=Email
 
 notification_method.im.title=IM
@@ -2581,6 +2585,10 @@ setting.display.logins.label=Logins
 
 setting.display.logins.option=Manage login sessions
 
+setting.display.orders.label=Orders
+
+setting.display.orders.option=View order history
+
 setting.display.password.actionlink=Change
 
 setting.display.password.label=Password
diff -r 3f7acfee7d4d -r 9cc8ea02116c bin/upgrading/update-db-general.pl
--- a/bin/upgrading/update-db-general.pl	Sun Apr 26 19:12:29 2009 +0000
+++ b/bin/upgrading/update-db-general.pl	Sun Apr 26 19:47:15 2009 +0000
@@ -3146,12 +3146,13 @@ register_tablecreate('shop_carts', <<'EO
 register_tablecreate('shop_carts', <<'EOC');
 CREATE TABLE shop_carts (
     cartid INT UNSIGNED NOT NULL,
-    authcode VARCHAR(20) NOT NULL,
     starttime INT UNSIGNED NOT NULL,
     userid INT UNSIGNED,
     uniq VARCHAR(15) NOT NULL,
     state INT UNSIGNED NOT NULL,
+    paymentmethod INT UNSIGNED NOT NULL,
     nextscan INT UNSIGNED NOT NULL DEFAULT 0,
+    authcode VARCHAR(20) NOT NULL,
 
     cartblob MEDIUMBLOB NOT NULL,
 
@@ -4037,6 +4038,11 @@ register_alter(sub {
                   q{ALTER TABLE shop_carts ADD COLUMN authcode VARCHAR(20) NOT NULL AFTER nextscan} );
     }
 
+    unless ( column_type( 'shop_carts', 'paymentmethod' ) =~ /int/ ) {
+        do_alter( 'shop_carts',
+                  q{ALTER TABLE shop_carts ADD COLUMN paymentmethod INT UNSIGNED NOT NULL AFTER state} );
+    }
+
 });
 
 
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/DW/Logic/MenuNav.pm
--- a/cgi-bin/DW/Logic/MenuNav.pm	Sun Apr 26 19:12:29 2009 +0000
+++ b/cgi-bin/DW/Logic/MenuNav.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -189,9 +189,9 @@ sub get_menu_navigation {
             name => 'shop',
             items => [
                 {
-                    url => "$LJ::SITEROOT/shop?for=paidtime",
+                    url => "$LJ::SITEROOT/shop",
                     text => "menunav.shop.paidtime",
-                    display => $never,
+                    display => LJ::is_enabled( 'payments' ) ? 1 : 0,
                 },
             ],
         },
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/DW/Shop.pm
--- a/cgi-bin/DW/Shop.pm	Sun Apr 26 19:12:29 2009 +0000
+++ b/cgi-bin/DW/Shop.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -65,6 +65,21 @@ our $STATE_CLOSED      = 8;    # carts c
 # any other state transition is hereby considered null and void.
 
 
+# keys are the names of the various payment methods as passed by the cart widget drop-down
+# values are hashrefs with id (the integer value that is stored in the 'paymentmethod'
+# field in the db) and class (the name of the DW::Shop::Engine class)
+our %PAYMENTMETHODS = (
+    paypal => {
+        id => 1,
+        class => 'PayPal',
+    },
+    checkmoneyorder => {
+        id => 2,
+        class => 'CheckMoneyOrder',
+    },
+);
+
+
 # called to return an instance of the shop; auto-determines if we have a
 # remote user and uses that, else, just returns an anonymous shop
 sub get {
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/DW/Shop/Cart.pm
--- a/cgi-bin/DW/Shop/Cart.pm	Sun Apr 26 19:12:29 2009 +0000
+++ b/cgi-bin/DW/Shop/Cart.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -7,6 +7,7 @@
 #
 # Authors:
 #      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -61,7 +62,7 @@ sub get {
     my $dbh = LJ::get_db_writer()
         or return undef;
     my $dbcart = $dbh->selectrow_hashref(
-        qq{SELECT userid, cartid, starttime, uniq, state, cartblob, nextscan, authcode
+        qq{SELECT cartblob
            FROM shop_carts
            WHERE $sql AND state = ?
            ORDER BY starttime DESC
@@ -94,7 +95,7 @@ sub get_from_cartid {
     my $dbh = LJ::get_db_writer()
         or return undef;
     my $dbcart = $dbh->selectrow_hashref(
-        qq{SELECT userid, cartid, starttime, uniq, state, cartblob, nextscan, authcode
+        qq{SELECT cartblob
            FROM shop_carts WHERE cartid = ?},
         undef, $cartid
     );
@@ -147,6 +148,7 @@ sub new_cart {
         total     => 0.00,
         nextscan  => 0,
         authcode  => LJ::make_auth_code( 20 ),
+        paymentmethod => 0, # we don't have a payment method yet
     };
 
     # now, delete any old carts we don't need
@@ -169,17 +171,44 @@ sub new_cart {
 }
 
 
+# returns all carts that the given user has ever had
+# can pass 'finished' opt which will omit carts in the OPEN, CLOSED, or
+# CHECKOUT states
+sub get_all {
+    my ( $class, $u, %opts ) = @_;
+    $u = LJ::want_user( $u );
+
+    my $extra_sql = " AND state <> $DW::Shop::STATE_OPEN AND state <> $DW::Shop::STATE_CLOSED AND state <> $DW::Shop::STATE_CHECKOUT"
+        if $opts{finished};
+
+    my $dbh = LJ::get_db_writer()
+        or return undef;
+    my $sth = $dbh->prepare( "SELECT cartblob FROM shop_carts WHERE userid = ?$extra_sql" );
+    $sth->execute( $u->id );
+
+    my @carts = ();
+    while ( my $cart = $sth->fetchrow_hashref ) {
+        push @carts, $class->_build( thaw( $cart->{cartblob} ) );
+    }
+
+    return @carts;
+}
+
+
 # saves the current cart to the database, returns 1/0
 sub save {
     my $self = $_[0];
+
+    # we store the payment method id in the db
+    my $paymentmethod_id = $DW::Shop::PAYMENTMETHODS{$self->paymentmethod}->{id} || 0;
 
     # toss in the database
     my $dbh = LJ::get_db_writer()
         or return undef;
     $dbh->do(
-        q{REPLACE INTO shop_carts (userid, cartid, starttime, uniq, state, nextscan, authcode, cartblob)
-          VALUES (?, ?, ?, ?, ?, ?, ?, ?)},
-        undef, ( map { $self->{$_} } qw/ userid cartid starttime uniq state nextscan authcode / ), nfreeze( $self )
+        q{REPLACE INTO shop_carts (userid, cartid, starttime, uniq, state, nextscan, authcode, paymentmethod, cartblob)
+          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)},
+        undef, ( map { $self->{$_} } qw/ userid cartid starttime uniq state nextscan authcode / ), $paymentmethod_id, nfreeze( $self )
     );
 
     # bail if error
@@ -272,6 +301,20 @@ sub state {
     return $self->{state};
 }
 
+
+# get/set payment method
+sub paymentmethod {
+    my ( $self, $newpaymentmethod ) = @_;
+
+    return $self->{paymentmethod}
+        unless defined $newpaymentmethod;
+
+    $self->{paymentmethod} = $newpaymentmethod;
+    $self->save;
+
+    return $self->{paymentmethod};
+}
+
 ################################################################################
 ## read-only accessor methods
 ################################################################################
@@ -279,6 +322,7 @@ sub state {
 
 sub id       { $_[0]->{cartid}             } 
 sub userid   { $_[0]->{userid}             }
+sub starttime{ $_[0]->{starttime}          }
 sub age      { time() - $_[0]->{starttime} }
 sub items    { $_[0]->{items} ||= []       }
 sub uniq     { $_[0]->{uniq}               }
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/DW/Shop/Engine/CheckMoneyOrder.pm
--- a/cgi-bin/DW/Shop/Engine/CheckMoneyOrder.pm	Sun Apr 26 19:12:29 2009 +0000
+++ b/cgi-bin/DW/Shop/Engine/CheckMoneyOrder.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -6,6 +6,7 @@
 #
 # Authors:
 #      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -59,7 +60,7 @@ sub checkout_url {
 
     # the cart is in a good state, so just send them to the confirmation page which
     # gives them instructions on where to send it
-    return "$LJ::SITEROOT/shop/cmo_confirm";
+    return "$LJ::SITEROOT/shop/confirm?ordernum=" . $cart->ordernum;
 }
 
 
@@ -76,7 +77,26 @@ sub confirm_order {
 
     # now set it pending
     $self->cart->state( $DW::Shop::STATE_PEND_PAID );
+
+    # delete cart from memcache
+    my $u = LJ::load_userid( $self->cart->userid );
+    $u->memc_delete( 'cart' ) if LJ::isu( $u );
+
     return 2;
+}
+
+
+# cancel_order()
+#
+# cancels the order, but all it has to do is check the cart state
+sub cancel_order {
+    my $self = $_[0];
+
+    # ensure the cart is in open state
+    return $self->error( 'paypal.engbadstate' )
+        unless $self->cart->state == $DW::Shop::STATE_OPEN;
+
+    return 1;
 }
 
 
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/DW/Shop/Engine/PayPal.pm
--- a/cgi-bin/DW/Shop/Engine/PayPal.pm	Sun Apr 26 19:12:29 2009 +0000
+++ b/cgi-bin/DW/Shop/Engine/PayPal.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -125,8 +125,8 @@ sub checkout_url {
         allownote     => 0,
 
         # where PayPal can send people back to
-        cancelurl     => "$LJ::SITEROOT/shop/pp_cancel",
-        returnurl     => "$LJ::SITEROOT/shop/pp_confirm",
+        cancelurl     => "$LJ::SITEROOT/shop/cancel",
+        returnurl     => "$LJ::SITEROOT/shop/confirm",
 
         # custom data we send to reference this cart
         custom        => join( ';', ( $cart->ordernum, $cart->display_total ) ),
@@ -233,15 +233,50 @@ sub confirm_order {
     warn "Failure to save pp_trans: " . $dbh->errstr . "\n"
         if $dbh->err;
 
+    my $u = LJ::load_userid( $self->cart->userid );
+
     # if this order is Complete (i.e., we have the money) then we note that
     if ( $res->{paymentstatus} eq 'Completed' ) {
         $self->cart->state( $DW::Shop::STATE_PAID );
+
+        # delete cart from memcache
+        $u->memc_delete( 'cart' ) if LJ::isu( $u );
+
         return 1;
     }
 
     # okay, so it's pending... sad days
     $self->cart->state( $DW::Shop::STATE_PEND_PAID );
+
+    # delete cart from memcache
+    $u->memc_delete( 'cart' ) if LJ::isu( $u );
+
     return 2;
+}
+
+
+# cancel_order()
+#
+# cancels the order and doesn't send any money
+sub cancel_order {
+    my $self = $_[0];
+
+    # ensure the cart is in open state
+    return $self->error( 'paypal.engbadstate' )
+        unless $self->cart->state == $DW::Shop::STATE_OPEN;
+
+    # ensure we have db
+    my $dbh = DW::Pay::get_db_writer()
+        or return $self->temp_error( 'nodb' );
+
+    $dbh->do(
+        q{DELETE FROM pp_tokens WHERE token = ?},
+        undef, $self->token
+    );
+    return $self->error( 'dberr', errstr => $dbh->errstr )
+        if $dbh->err;
+
+    return 1;
 }
 
 
diff -r 3f7acfee7d4d -r 9cc8ea02116c cgi-bin/LJ/Setting/Display/Orders.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/LJ/Setting/Display/Orders.pm	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,37 @@
+#!/usr/bin/perl
+#
+# LJ::Setting::Display::Orders - shows a link to the user's payment history page
+#
+# Authors:
+#      Janine Costanzo <janine@netrophic.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself. For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+
+package LJ::Setting::Display::Orders;
+use base 'LJ::Setting';
+use strict;
+use warnings;
+
+sub should_render {
+    my ($class, $u) = @_;
+
+    return $u && !$u->is_community ? 1 : 0;
+}
+
+sub label {
+    my $class = shift;
+
+    return $class->ml( 'setting.display.orders.label' );
+}
+
+sub option {
+    my ($class, $u, $errs, $args) = @_;
+
+    return "<a href='$LJ::SITEROOT/shop/history'>" . $class->ml( 'setting.display.orders.option' ) . "</a>";
+}
+
+1;
diff -r 3f7acfee7d4d -r 9cc8ea02116c doc/config-local.pl.txt
--- a/doc/config-local.pl.txt	Sun Apr 26 19:12:29 2009 +0000
+++ b/doc/config-local.pl.txt	Sun Apr 26 19:47:15 2009 +0000
@@ -33,6 +33,7 @@
     $SITENAMESHORT = "YourSite";
     $SITENAMEABBREV = "YS";
     $SITECOMPANY = "YourSite's Company";
+    $SITEADDRESS = "123 Main St.<br />Somewhere, XX 12345";
 
     # MemCache information, if you have MemCache servers running
     #@MEMCACHE_SERVERS = ('hostname:port');
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/manage/settings/index.bml
--- a/htdocs/manage/settings/index.bml	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/manage/settings/index.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -121,6 +121,7 @@ body<=
                 LJ::Setting::Display::Logins
                 LJ::Setting::Display::Emails
                 LJ::Setting::Display::EmailPosts
+                LJ::Setting::Display::Orders
             )],
         },
         othersites => {
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop.bml
--- a/htdocs/shop.bml	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/shop.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -39,7 +39,9 @@ body<=
     $ret .= DW::Widget::ShopItemGroupDisplay->render( group => 'paidaccounts' );
 
     $ret .= "<div class='shopbox'>";
-    $ret .= "<p>" . BML::ml( '.sideblurb', { sitename => $LJ::SITENAMESHORT, aopts => "href='$LJ::HELPURL{paidaccountinfo}'" } ). "</p>";
+    $ret .= "<p>" . BML::ml( '.sideblurb', { sitename => $LJ::SITENAMESHORT, aopts => "href='$LJ::HELPURL{paidaccountinfo}'" } ) . "</p>";
+    $ret .= "<p>" . BML::ml( '.viewhistory', { aopts => "href='$LJ::SITEROOT/shop/history.bml'" } ). "</p>"
+        if LJ::get_remote();
     $ret .= "</div>";
 
     return $ret;
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop.bml.text
--- a/htdocs/shop.bml.text	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/shop.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -5,3 +5,5 @@
 .sideblurb=You can learn about paid accounts <a [[aopts]]>here</a>.
 
 .title=[[sitename]] Shop
+
+.viewhistory=To view your past orders, go <a [[aopts]]>here</a>.
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/cancel.bml
--- a/htdocs/shop/cancel.bml	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/shop/cancel.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -3,10 +3,10 @@
 #
 # shop/cancel.bml
 #
-# Cancels an outstanding order.
+# This page cancels the given order.
 #
 # Authors:
-#      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -16,7 +16,6 @@
 #
 
 _c?><?page
-title=>Cancel Order
 body<=
 <?_code
 {
@@ -26,8 +25,43 @@ body<=
     return BML::redirect( "$LJ::SITEROOT/" )
         unless LJ::is_enabled( 'payments' );
 
-    # FIXME: cancel order ;)
-    return "FIXME: cancel order";
+    my ( $ordernum, $token, $payerid ) = ( $GET{ordernum}, $GET{token}, $GET{PayerID} );
+    my ( $cart, $eng );
+
+    # use ordernum if we have it, otherwise use token/payerid
+    if ( $ordernum ) {
+        $cart = DW::Shop::Cart->get_from_ordernum( $ordernum );
+        return $ML{'.error.invalidordernum'}
+            unless $cart;
+
+        my $paymentmethod = $cart->paymentmethod;
+        my $paymentmethod_class = 'DW::Shop::Engine::' . $DW::Shop::PAYMENTMETHODS{$paymentmethod}->{class};
+        $eng = $paymentmethod_class->new_from_cart( $cart );
+        return $ML{'.error.invalidcart'}
+            unless $eng;
+    } else {
+        return $ML{'.error.needtoken'}
+            unless $token;
+
+        # we can assume paypal is the engine if we have a token
+        $eng = DW::Shop::Engine::PayPal->new_from_token( $token );
+        return $ML{'.error.invalidtoken'}
+            unless $eng;
+
+        $cart = $eng->cart;
+        $ordernum = $cart->ordernum;
+    }
+
+    # cart must be in open state
+    return BML::redirect( "$LJ::SITEROOT/shop/receipt?ordernum=$ordernum" )
+        unless $cart->state == $DW::Shop::STATE_OPEN;
+
+    # cancel payment and discard cart
+    if ( $eng->cancel_order ) {
+        return BML::redirect( "$LJ::SITEROOT/shop?newcart=1" );
+    }
+
+    return $ML{'.error.cantcancel'};
 }
 _code?>
 <=body
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/cancel.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/cancel.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,11 @@
+;; -*- coding: utf-8 -*-
+
+.error.cantcancel=Sorry, we could not cancel your order at this time.
+
+.error.invalidcart=Your cart is invalid.
+
+.error.invalidordernum=Your order number is invalid.
+
+.error.invalidtoken=Your token is invalid.
+
+.error.needtoken=You did not provide an order number or token.
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/checkout.bml
--- a/htdocs/shop/checkout.bml	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/shop/checkout.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -6,6 +6,7 @@
 #
 # Authors:
 #      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -18,10 +19,12 @@ body<=
 <?_code
 {
     use strict;
-    use vars qw/ %GET %POST /;
+    use vars qw/ %GET %POST $title /;
 
     return BML::redirect( "$LJ::SITEROOT/" )
         unless LJ::is_enabled( 'payments' );
+
+    $title = $ML{'.title'};
 
     # this page uses new style JS
     LJ::need_res( 'stc/shop.css' );
@@ -29,22 +32,24 @@ body<=
 
     # get their shop/cart
     my $cart = DW::Shop->get->cart;
-    return "you don't have a shopping cart"
+    return $ML{'.error.nocart'}
         unless $cart;
-    return "your cart has nothing in it"
+    return $ML{'.error.emptycart'}
         unless $cart->has_items;
 
     # FIXME: if they have a $0 cart, we don't support that yet
-    return "sorry, we don't support \$0 carts yet"
+    return $ML{'.error.zerocart'}
         if $cart->total == 0.00;
 
     # establish the engine they're trying to use
     my $eng = DW::Shop::Engine->get( $GET{method}, $cart );
-    return 'payment method not supported'
+    return $ML{'.error.invalidpaymentmethod'}
         unless $eng;
 
-    # at this point, we have $eng, and we don't care what the
-    # actual payment backend is
+    # set the payment method on the cart
+    $cart->paymentmethod( $GET{method} );
+
+    # redirect to checkout url
     my $url = $eng->checkout_url;
     return $eng->errstr
         unless $url;
@@ -52,5 +57,5 @@ body<=
 }
 _code?>
 <=body
-title=>Checkout
+title=><?_code return $title; _code?>
 page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/checkout.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/checkout.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,11 @@
+;; -*- coding: utf-8 -*-
+
+.error.emptycart=Your cart is empty.
+
+.error.invalidpaymentmethod=The payment method you chose is not currently supported.
+
+.error.nocart=You do not have a shopping cart.
+
+.error.zerocart=Sorry, we don't support $0 carts yet.
+
+.title=Check Out
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/cmo_confirm.bml
--- a/htdocs/shop/cmo_confirm.bml	Sun Apr 26 19:12:29 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-<?_c
-
-#
-# shop/cmo_confirm.bml
-#
-# For users paying by check/money order to confirm.
-#
-# Authors:
-#      Mark Smith <mark@dreamwidth.org>
-#
-# Copyright (c) 2009 by Dreamwidth Studios, LLC.
-#
-# This program is free software; you may redistribute it and/or modify it under
-# the same terms as Perl itself.  For a copy of the license, please reference
-# 'perldoc perlartistic' or 'perldoc perlgpl'.
-#
-
-_c?><?page
-title=>Confirm Your Payment
-body<=
-<?_code
-{
-    use strict;
-    use vars qw(%GET %POST);
-
-    return BML::redirect( "$LJ::SITEROOT/" )
-        unless LJ::is_enabled( 'payments' );
-
-    my $cart = DW::Shop->get->cart
-        or return 'you do not seem to have a cart';
-    my $eng = DW::Shop::Engine::CheckMoneyOrder->new_from_cart( $cart );
-    return 'sorry, invalid cart'
-        unless $eng;
-
-    my $ordernum = $eng->cart->ordernum;
-
-    # cart must be in open state
-    return BML::redirect( "$LJ::SITEROOT/shop/cmo_receipt?ordernum=$ordernum" )
-        unless $eng->cart->state == $DW::Shop::STATE_OPEN;
-
-    # if they didn't post, give them a form
-    unless ( LJ::did_post() ) {
-        my $ret = '';
-        $ret .= LJ::Widget::ShopCart->render( receipt => 1, cart => $eng->cart );
-        $ret .= "<form method='post' action='$LJ::SITEROOT/shop/cmo_confirm?ordernum=$ordernum'>\n";
-        $ret .= LJ::form_auth();
-        $ret .= "<?p Please confirm that you wish to place the above order to $LJ::SITENAME in the amount of ";
-        $ret .= "\$". $eng->cart->display_total . ' USD.  Once you confirm this order, you will need to write a ';
-        $ret .= "check or money order and mail it to: some address, somewhere. p?>";
-        $ret .= '<div style="width: 350px;"><input style="float: left;" type="submit" value="Confirm Purchase" /> ';
-        $ret .= "<div style='float: right;'><a href='/shop/cmo_cancel?ordernum=$ordernum'>Cancel Purchase</a></div></div>";
-        $ret .= '</form>';
-        return $ret;
-    }
-
-    # okay, they posted, verify the auth code and
-    return 'invalid form auth'
-        unless LJ::check_form_auth();
-
-    # and now set the state, this has been checked out...
-    $eng->cart->state( $DW::Shop::STATE_CHECKOUT );
-
-    # they want to pay us, yippee!
-    my $rv = $eng->confirm_order;
-    return $eng->errstr
-        unless $rv;
-
-    # advise them the order has been placed and to watch their email
-    if ( $rv == 2 ) {
-        return '<?p Your order has been successfully placed.  Please remember to mail your check! p?><?p ' .
-               "<a href='$LJ::SITEROOT/shop/cmo_receipt?ordernum=$ordernum'>View Receipt</a> p?>";
-    }
-}
-_code?>
-<=body
-page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/confirm.bml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/confirm.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,111 @@
+<?_c
+
+#
+# shop/confirm.bml
+#
+# The page used to confirm a user's order before we finally bill them.
+#
+# Authors:
+#      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+
+_c?><?page
+body<=
+<?_code
+{
+    use strict;
+    use vars qw(%GET %POST $title);
+
+    return BML::redirect( "$LJ::SITEROOT/" )
+        unless LJ::is_enabled( 'payments' );
+
+    $title = $ML{'.title'};
+
+    my ( $ordernum, $token, $payerid ) = ( $GET{ordernum}, $GET{token}, $GET{PayerID} );
+    my ( $cart, $eng, $paymentmethod );
+
+    # use ordernum if we have it, otherwise use token/payerid
+    if ( $ordernum ) {
+        $cart = DW::Shop::Cart->get_from_ordernum( $ordernum );
+        return $ML{'.error.invalidordernum'}
+            unless $cart;
+
+        $paymentmethod = $cart->paymentmethod;
+        my $paymentmethod_class = 'DW::Shop::Engine::' . $DW::Shop::PAYMENTMETHODS{$paymentmethod}->{class};
+        $eng = $paymentmethod_class->new_from_cart( $cart );
+        return $ML{'.error.invalidcart'}
+            unless $eng;
+    } else {
+        return $ML{'.error.needtoken'}
+            unless $token;
+
+        # we can assume paypal is the engine if we have a token
+        $eng = DW::Shop::Engine::PayPal->new_from_token( $token );
+        return $ML{'.error.invalidtoken'}
+            unless $eng;
+
+        $cart = $eng->cart;
+        $ordernum = $cart->ordernum;
+        $paymentmethod = $cart->paymentmethod;
+    }
+
+    # cart must be in open state
+    return BML::redirect( "$LJ::SITEROOT/shop/receipt?ordernum=$ordernum" )
+        unless $cart->state == $DW::Shop::STATE_OPEN;
+
+    # if they didn't post, give them a form
+    unless ( LJ::did_post() ) {
+        # set the payerid for later
+        $eng->payerid( $payerid )
+            if $payerid;
+
+        my $ret = '';
+        $ret .= LJ::Widget::ShopCart->render( receipt => 1, cart => $cart );
+        $ret .= "<form method='post' action='$LJ::SITEROOT/shop/confirm?ordernum=$ordernum'>\n";
+        $ret .= LJ::form_auth();
+        $ret .= "<?p " . BML::ml( '.confirm', { sitename => $LJ::SITENAME, total => '<strong>$' . $cart->display_total . ' USD</strong>' } ) . " p?>";
+        $ret .= "<?p $ML{\".confirm.$paymentmethod\"} p?>";
+        $ret .= "<?p " . LJ::html_submit( confirm => $ML{'.btn.confirm'} );
+        $ret .= " <a href='$LJ::SITEROOT/shop/cancel?ordernum=$ordernum'>$ML{'.btn.cancel'}</a> p?>";
+        $ret .= '</form>';
+        return $ret;
+    }
+
+    # okay, they posted, verify the auth code
+    return $ML{'error.invalidauth'}
+        unless LJ::check_form_auth();
+
+    # and now set the state, this has been checked out...
+    $cart->state( $DW::Shop::STATE_CHECKOUT );
+
+    # they want to pay us, yippee!
+    my $rv = $eng->confirm_order;
+    return $eng->errstr
+        unless $rv;
+
+    my $ret;
+
+    # advise them the order has been placed and to watch their email
+    if ( $rv == 1 ) {
+        $ret .= "<?p $ML{\".success.$paymentmethod.immediate\"} p?>";
+    } elsif ( $rv == 2 ) {
+        my $address = "<?p $LJ::SITECOMPANY<br />Order #" . $cart->id . "<br />$LJ::SITEADDRESS p?>";
+        $ret .= "<?p " . BML::ml( ".success.$paymentmethod.processing", { address => $address } ) . " p?>";
+    }
+
+    $ret .= "<?p <a href='$LJ::SITEROOT/shop/receipt?ordernum=$ordernum'>$ML{'.btn.viewreceipt'}</a><br />";
+    $ret .= "<a href='$LJ::SITEROOT/shop'>$ML{'.btn.back'}</a> p?>";
+
+    return $ret;
+}
+_code?>
+<=body
+title=><?_code return $title; _code?>
+page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/confirm.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/confirm.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,31 @@
+;; -*- coding: utf-8 -*-
+
+.btn.back=Back to Shop
+
+.btn.cancel=Cancel Payment
+
+.btn.confirm=Confirm Payment
+
+.btn.viewreceipt=View Receipt
+
+.confirm=Please confirm your payment to [[sitename]] in the amount of [[total]].
+
+.confirm.checkmoneyorder=Once confirmed, you must mail a check or money order for the above amount to us.
+
+.confirm.paypal=Once confirmed, we will process your purchase as soon as PayPal notifies us of a successful transaction.
+
+.error.invalidcart=Your cart is invalid.
+
+.error.invalidordernum=Your order number is invalid.
+
+.error.invalidtoken=Your token is invalid.
+
+.error.needtoken=You did not provide an order number or token.
+
+.success.checkmoneyorder.processing=Your order has been successfully placed. It will be processed once we receive your check or money order. Please mail it to: [[address]] p?><?p <strong>Be sure to include the apartment and order numbers!</strong>
+
+.success.paypal.immediate=Your order has been successfully placed.  Within the next few minutes PayPal should finish processing and we will activate your order.  Watch your email!
+
+.success.paypal.processing=Your order has been successfully placed, but PayPal says it will take some time for your payment to finish processing.  As soon as it does, we will email you.
+
+.title=Confirm Your Payment
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/history.bml
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/history.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,65 @@
+<?_c
+#
+# shop/history.bml
+#
+# Page that shows a user's past orders.
+#
+# Authors:
+#      Janine Costanzo <janine@netrophic.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself. For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+_c?><?page
+body<=
+<?_code
+{
+    use strict;
+    use vars qw/ %GET %POST $title /;
+
+    my $remote = LJ::get_remote()
+        or return "<?needlogin?>";
+
+    $title = $ML{'.title'};
+
+    # this page uses new style JS
+    LJ::need_res( 'stc/shop.css' );
+    LJ::set_active_resource_group( 'jquery' );
+
+    my @carts = DW::Shop::Cart->get_all( $remote, finished => 1 );
+
+    unless ( @carts ) {
+        return BML::ml( '.nocarts.extra', { aopts => "href='$LJ::SITEROOT/shop'" } )
+            if LJ::is_enabled( 'payments' );
+        return $ML{'.nocarts'};
+    }
+
+    my $ret;
+    $ret .= "<table class='shop-cart'>";
+    $ret .= "<tr><th>$ML{'.cart.header.ordernumber'}</th><th>$ML{'.cart.header.date'}</th><th>$ML{'.cart.header.total'}</th>";
+    $ret .= "<th>$ML{'.cart.header.paymentmethod'}</th><th>$ML{'.cart.header.status'}</th><th>$ML{'.cart.header.details'}</th></tr>";
+    foreach my $cart ( @carts ) {
+        my $state = $cart->state;
+        my $paymentmethod = $cart->paymentmethod;
+        my $date = DateTime->from_epoch( epoch => $cart->starttime );
+
+        $ret .= "<tr>";
+        $ret .= "<td>" . $cart->id . "</td>";
+        $ret .= "<td>" . $date->strftime( "%F %r %Z" ) . "</td>";
+        $ret .= "<td>\$" . $cart->total . " USD</td>";
+        $ret .= "<td>$ML{\"/shop/receipt.bml.cart.paymentmethod.$paymentmethod\"}</td>";
+        $ret .= "<td>$ML{\"/shop/receipt.bml.cart.status.$state\"}</td>";
+        $ret .= "<td><a href='$LJ::SITEROOT/shop/receipt?ordernum=" . $cart->ordernum . "'>$ML{'.cart.details'}</a></td>";
+        $ret .= "</tr>";
+    }
+    $ret .= "</table>";
+
+    return $ret;
+}
+_code?>
+<=body
+title=><?_code return $title; _code?>
+page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/history.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/history.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,21 @@
+;; -*- coding: utf-8 -*-
+
+.cart.details=View
+
+.cart.header.date=Date
+
+.cart.header.details=Details
+
+.cart.header.ordernumber=Order Number
+
+.cart.header.paymentmethod=Payment Method
+
+.cart.header.status=Status
+
+.cart.header.total=Total
+
+.nocarts=You have not placed any orders.
+
+.nocarts.extra=You have not placed any orders.  <a [[aopts]]>Want to purchase something?</a>
+
+.title=Order History
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/pp_confirm.bml
--- a/htdocs/shop/pp_confirm.bml	Sun Apr 26 19:12:29 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-<?_c
-
-#
-# shop/pp_confirm.bml
-#
-# The return page when we get back from PayPal.  Used to confirm a user's order
-# before we finally bill them.
-#
-# Authors:
-#      Mark Smith <mark@dreamwidth.org>
-#
-# Copyright (c) 2009 by Dreamwidth Studios, LLC.
-#
-# This program is free software; you may redistribute it and/or modify it under
-# the same terms as Perl itself.  For a copy of the license, please reference
-# 'perldoc perlartistic' or 'perldoc perlgpl'.
-#
-
-_c?><?page
-title=>Confirm Your Payment
-body<=
-<?_code
-{
-    use strict;
-    use vars qw(%GET %POST);
-
-    return BML::redirect( "$LJ::SITEROOT/" )
-        unless LJ::is_enabled( 'payments' );
-
-    my ( $token, $payerid ) = ( $GET{token}, $GET{PayerID} );
-    return 'sorry, need token at least'
-        unless $token;
-
-    my $eng = DW::Shop::Engine::PayPal->new_from_token( $GET{token} );
-    return 'sorry, invalid token'
-        unless $eng;
-
-    # cart must be in open state
-    return BML::redirect( "$LJ::SITEROOT/shop/pp_receipt?token=" . $eng->token )
-        unless $eng->cart->state == $DW::Shop::STATE_OPEN;
-
-    # if they didn't post, give them a form
-    unless ( LJ::did_post() ) {
-        # set the payerid for later
-        $eng->payerid( $payerid )
-            if $payerid;
-
-        my $ret = '';
-        $ret .= LJ::Widget::ShopCart->render( receipt => 1, cart => $eng->cart );
-        $ret .= "<form method='post' action='$LJ::SITEROOT/shop/pp_confirm?token=$token'>\n";
-        $ret .= LJ::form_auth();
-        $ret .= "<?p Please confirm your payment to $LJ::SITENAME in the amount of \$". $eng->cart->display_total . ' USD. ';
-        $ret .= 'Once confirmed, we will process your purchase as soon as PayPal notifies us of a successful transaction. p?>';
-        $ret .= '<div style="width: 350px;"><input style="float: left;" type="submit" value="Confirm Purchase" /> ';
-        $ret .= "<div style='float: right;'><a href='/shop/pp_cancel?token=$token'>Cancel Purchase</a></div></div>";
-        $ret .= '</form>';
-        return $ret;
-    }
-
-    # okay, they posted, verify the auth code and
-    return 'invalid form auth'
-        unless LJ::check_form_auth();
-
-    # and now set the state, this has been checked out...
-    $eng->cart->state( $DW::Shop::STATE_CHECKOUT );
-
-    # they want to pay us, yippee!
-    my $rv = $eng->confirm_order;
-    return $eng->errstr
-        unless $rv;
-
-    # advise them the order has been placed and to watch their email
-    if ( $rv == 1 ) {
-        return '<?p Your order has been successfully placed.  Within the next few minutes PayPal should ' .
-               'finish processing and we will activate your order.  Watch your email! p?><?p ' .
-               "<a href='$LJ::SITEROOT/shop/pp_receipt?token=$token'>View Receipt</a> p?>";
-
-    } elsif ( $rv == 2 ) {
-        return '<?p Your order has been successfully placed, but PayPal says it will take some time for ' .
-               'your payment to finish processing.  As soon as it does, we will email you. p?><?p ' .
-               "<a href='$LJ::SITEROOT/shop/pp_receipt?token=$token'>View Receipt</a> p?>";
-    }
-}
-_code?>
-<=body
-page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/receipt.bml
--- a/htdocs/shop/receipt.bml	Sun Apr 26 19:12:29 2009 +0000
+++ b/htdocs/shop/receipt.bml	Sun Apr 26 19:47:15 2009 +0000
@@ -3,11 +3,10 @@
 #
 # shop/receipt.bml
 #
-# Serves up a simple receipt for the user to view the status of the order and
-# print out and save if they want.
+# This page shows your receipt for your order.
 #
 # Authors:
-#      Mark Smith <mark@dreamwidth.org>
+#      Janine Costanzo <janine@netrophic.com>
 #
 # Copyright (c) 2009 by Dreamwidth Studios, LLC.
 #
@@ -16,53 +15,61 @@
 # 'perldoc perlartistic' or 'perldoc perlgpl'.
 #
 
-_c?><?page
-title=>Paid Account Receipt
-body<=
+_c?>
 <?_code
 {
     use strict;
-    use vars qw(%GET);
 
     return BML::redirect( "$LJ::SITEROOT/" )
         unless LJ::is_enabled( 'payments' );
 
-    my $payment_id = $GET{'payment_id'};
-    my $email = $GET{'email'};
+    my $ordernum = $GET{ordernum};
 
-    my $payment = DW::Pay::get_payment_details($payment_id);
+    my $cart = DW::Shop::Cart->get_from_ordernum( $ordernum );
+    return $ML{'.error.invalidordernum'}
+        unless $cart;
 
-    if ($payment) {
-        my $order = DW::Pay::pp_get_order_details($payment_id);
-        if ($order->{'email'} eq $GET{'email'}) {
+    my $state = $cart->state;
+    my $paymentmethod = $cart->paymentmethod;
 
-            ### FORMAT RECEIPT HERE
-            my $ret = "<p>This is your receipt for $LJ::SITENAME paid services. Please bookmark this URL for your own records.</p><table style='width: 450px'>";
-            my %status = (
-                          'pending'        => "Pending, <a href='$LJ::SITEROOT/shop/pp_confirm?token=$order->{token}'>awaiting confirmation</a>",
-                          'processing'     => 'Processing',
-                          'paid-pending'   => 'Payment is complete, thanks for your support!',
-                          'paid-completed' => 'Payment is complete, thanks for your support!', 
-                          );
-            $ret .= '<tr><th style="text-align: left">Status:</th>';
-            $ret .= '<td style="text-align: right">'.$status{$payment->{'status'}}.'</td></tr>';
-            $ret .= '<tr><th style="text-align: left">Account Type:</th>';
-            $ret .= '<td style="text-align: right">'.DW::Pay::type_name($payment->{'typeid'}).'</td></tr>';
-            $ret .= '<tr><th style="text-align: left">Duration:</th>';
-            $ret .= '<td style="text-align: right">'.($payment->{'duration'} == 99 ? 'Permanent' : $payment->{'duration'}.' months').'</td></tr>';
-            $ret .= '<tr><th style="text-align: left">Total:</th><td style="text-align: right">$'.$payment->{'amount'}.' USD</td></tr>';
-            $ret .= '</table>';
+    # cart cannot be in open, closed, or checkout state
+    return BML::redirect( "$LJ::SITEROOT/shop/cart" )
+        if $state == $DW::Shop::STATE_OPEN || $state == $DW::Shop::STATE_CLOSED || $state == $DW::Shop::STATE_CHECKOUT;
 
-            $ret .='<?p A receipt for your purchase has been emailed to you. You may log into your account at <a href="http://www.paypal.com/">www.paypal.com</a> to view details of this transaction. p?>';
+    my $title = BML::ml( '.title', { num => $cart->id } );
 
-            return $ret;
-        } else {
-            return "You are not verified to view this receipt";
+    my $ret;
+    $ret .= qq{
+        <html>
+        <head>
+        <style type='text/css'>
+        table {
+            border: 1px solid #000;
+            border-spacing: 0;
         }
-    } else {
-        return "That order could not be found.";
+        table td, table th {
+            border: 1px solid #000;
+            padding: 5px;
+        }
+        table td.total {
+            font-weight: bold;
+            text-align: right;
+        }
+        </style>
+        <title>$title</title>
+        </head>
+        <body>
+    };
+    $ret .= "<h1>$title</h1>";
+    $ret .= "<p>" . BML::ml( '.cart.status', { status => $ML{".cart.status.$state"} } ) . "<br />";
+    $ret .= BML::ml( '.cart.paymentmethod', { paymentmethod => $ML{".cart.paymentmethod.$paymentmethod"} } ) . "</p>";
+    if ( $paymentmethod eq 'checkmoneyorder' ) {
+        my $address = "<p>$LJ::SITECOMPANY<br />Order #" . $cart->id . "<br />$LJ::SITEADDRESS</p>";
+        $ret .= "<p>" . BML::ml( ".cart.paymentmethod.$paymentmethod.extra", { address => $address } ) . "</p>";
     }
+    $ret .= LJ::Widget::ShopCart->render( receipt => 1, cart => $cart );
+    $ret .= "</body></html>";
+
+    return $ret;
 }
 _code?>
-<=body
-page?>
diff -r 3f7acfee7d4d -r 9cc8ea02116c htdocs/shop/receipt.bml.text
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/receipt.bml.text	Sun Apr 26 19:47:15 2009 +0000
@@ -0,0 +1,34 @@
+;; -*- coding: utf-8 -*-
+
+.cart.status=<strong>Current Order Status:</strong> [[status]]
+
+.cart.status.1|note=This status should never be possible on the receipt page!
+.cart.status.1=Open
+
+.cart.status.2|note=This status should never be possible on the receipt page!
+.cart.status.2=Checked out
+
+.cart.status.3=Waiting for payment
+
+.cart.status.4=Payment received
+
+.cart.status.5=Processed and completed
+
+.cart.status.6=Refund approved and pending
+
+.cart.status.7=Refunded
+
+.cart.status.8|note=This status should never be possible on the receipt page!
+.cart.status.8=Closed before completion
+
+.cart.paymentmethod=<strong>Payment Method:</strong> [[paymentmethod]]
+
+.cart.paymentmethod.checkmoneyorder=Check/Money Order
+
+.cart.paymentmethod.checkmoneyorder.extra=Please mail your check or money order to: [[address]]
+
+.cart.paymentmethod.paypal=PayPal
+
+.error.invalidordernum=Your order number is invalid.
+
+.title=Order #[[num]]
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org