[dw-free] Add PayPal 'Buy Now' button support to enable credit card purchases without a PayPal accou
[commit: http://hg.dwscoalition.org/dw-free/rev/c4f5f857fb42]
Add PayPal 'Buy Now' button support to enable credit card purchases without
a PayPal account.
Patch by
mark.
Files modified:
Add PayPal 'Buy Now' button support to enable credit card purchases without
a PayPal account.
Patch by
Files modified:
- bin/upgrading/en.dat
- bin/upgrading/update-db-general.pl
- cgi-bin/DW/Shop.pm
- cgi-bin/DW/Shop/Engine.pm
- cgi-bin/DW/Shop/Engine/CreditCardPP.pm
- cgi-bin/DW/Shop/Engine/PayPal.pm
- cgi-bin/LJ/Widget/ShopCart.pm
- htdocs/shop/creditcard.bml
- htdocs/shop/creditcard.bml.text
- htdocs/shop/receipt.bml.text
--------------------------------------------------------------------------------
diff -r 444427ab0545 -r c4f5f857fb42 bin/upgrading/en.dat
--- a/bin/upgrading/en.dat Wed May 13 16:26:50 2009 +0000
+++ b/bin/upgrading/en.dat Fri May 15 00:46:43 2009 +0000
@@ -4457,7 +4457,9 @@ widget.shopcart.paymentmethod=Payment Me
widget.shopcart.paymentmethod.checkmoneyorder=Check/Money Order
-widget.shopcart.paymentmethod.paypal=PayPal/Credit Card
+widget.shopcart.paymentmethod.creditcardpp=Credit Card
+
+widget.shopcart.paymentmethod.paypal=PayPal Account
widget.shopcart.total=Total:
diff -r 444427ab0545 -r c4f5f857fb42 bin/upgrading/update-db-general.pl
--- a/bin/upgrading/update-db-general.pl Wed May 13 16:26:50 2009 +0000
+++ b/bin/upgrading/update-db-general.pl Fri May 15 00:46:43 2009 +0000
@@ -3225,6 +3225,7 @@ register_tablecreate('pp_log', <<'EOC');
register_tablecreate('pp_log', <<'EOC');
CREATE TABLE pp_log (
ppid int unsigned not null,
+ ip varchar(15) not null,
transtime int unsigned not null,
req_content text not null,
res_content text not null,
@@ -4072,6 +4073,11 @@ register_alter(sub {
q{ALTER TABLE shop_carts ADD COLUMN email VARCHAR(255) AFTER userid} );
}
+ unless ( column_type( 'pp_log', 'ip' ) =~ /varcahr/ ) {
+ do_alter( 'pp_log',
+ q{ALTER TABLE pp_log ADD COLUMN ip VARCHAR(15) NOT NULL AFTER ppid} );
+ }
+
});
diff -r 444427ab0545 -r c4f5f857fb42 cgi-bin/DW/Shop.pm
--- a/cgi-bin/DW/Shop.pm Wed May 13 16:26:50 2009 +0000
+++ b/cgi-bin/DW/Shop.pm Fri May 15 00:46:43 2009 +0000
@@ -33,6 +33,7 @@ our $STATE_PEND_REFUND = 6; # refund
our $STATE_PEND_REFUND = 6; # refund is approved but unissued
our $STATE_REFUNDED = 7; # we have refunded this cart and reversed it
our $STATE_CLOSED = 8; # carts can go from OPEN -> CLOSED
+our $STATE_DECLINED = 9; # payment entity declined the fundage
# documentation of valid state transitions...
#
@@ -62,6 +63,9 @@ our $STATE_CLOSED = 8; # carts c
# cart. i.e., it hasn't been touched in a while so we
# decide the user isn't coming back.
#
+# PEND_PAID -> DECLINED happens when we try to capture funds from a remote
+# entity and they decline for some reason.
+#
# any other state transition is hereby considered null and void.
@@ -76,6 +80,10 @@ our %PAYMENTMETHODS = (
checkmoneyorder => {
id => 2,
class => 'CheckMoneyOrder',
+ },
+ creditcardpp => {
+ id => 3,
+ class => 'CreditCardPP',
},
);
diff -r 444427ab0545 -r c4f5f857fb42 cgi-bin/DW/Shop/Engine.pm
--- a/cgi-bin/DW/Shop/Engine.pm Wed May 13 16:26:50 2009 +0000
+++ b/cgi-bin/DW/Shop/Engine.pm Fri May 15 00:46:43 2009 +0000
@@ -18,6 +18,7 @@ package DW::Shop::Engine;
use strict;
use DW::Shop::Engine::CheckMoneyOrder;
+use DW::Shop::Engine::CreditCardPP;
use DW::Shop::Engine::PayPal;
# get( $method, $cart )
@@ -25,6 +26,7 @@ use DW::Shop::Engine::PayPal;
# returns the proper subclass for the given payment method, if one exists
sub get {
return DW::Shop::Engine::PayPal->new( $_[2] ) if $_[1] eq 'paypal';
+ return DW::Shop::Engine::CreditCardPP->new( $_[2] ) if $_[1] eq 'creditcardpp';
return DW::Shop::Engine::CheckMoneyOrder->new( $_[2] ) if $_[1] eq 'checkmoneyorder';
warn "Payment method '$_[1]' not supported.\n";
diff -r 444427ab0545 -r c4f5f857fb42 cgi-bin/DW/Shop/Engine/CreditCardPP.pm
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Shop/Engine/CreditCardPP.pm Fri May 15 00:46:43 2009 +0000
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+#
+# DW::Shop::Engine::CreditCardPP
+#
+# This is a very simple payment method, it generates one of those fancy PayPal
+# buttons which the user can use to pay.
+#
+# 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'.
+#
+
+package DW::Shop::Engine::CreditCardPP;
+
+use strict;
+use Carp qw/ croak confess /;
+use Digest::MD5 qw/ md5_hex /;
+use Storable qw/ nfreeze thaw /;
+
+use base qw/ DW::Shop::Engine /;
+
+
+# new( $cart )
+#
+# instantiates a new PayPal engine for the given cart
+sub new {
+ return bless { cart => $_[1] }, $_[0];
+}
+
+
+# checkout_url()
+#
+# this is simple, send them to the page for entering their credit card information
+sub checkout_url {
+ my $self = $_[0];
+
+ # make sure that the cart contains something that costs something. since
+ # this check should have been done above, we die hardcore here.
+ my $cart = $self->cart;
+ die "Constraints not met: cart && cart->has_items && cart->has_total > 0.00.\n"
+ unless $cart && $cart->has_items && $cart->total > 0.00;
+
+ # and, just in case something terrible happens, make sure our state is good
+ die "Cart not in valid state!\n"
+ unless $cart->state == $DW::Shop::STATE_OPEN;
+
+ # return URL to cc entry
+ return "$LJ::SITEROOT/shop/creditcard";
+}
+
+
+# accessors
+sub cart { $_[0]->{cart} }
+
+
+1;
diff -r 444427ab0545 -r c4f5f857fb42 cgi-bin/DW/Shop/Engine/PayPal.pm
--- a/cgi-bin/DW/Shop/Engine/PayPal.pm Wed May 13 16:26:50 2009 +0000
+++ b/cgi-bin/DW/Shop/Engine/PayPal.pm Fri May 15 00:46:43 2009 +0000
@@ -372,9 +372,9 @@ sub _pp_req {
if ( ref $self && ( my $ppid = $self->ppid ) ) {
if ( my $dbh = DW::Pay::get_db_writer() ) {
$dbh->do( q{
- INSERT INTO pp_log (ppid, transtime, req_content, res_content)
- VALUES (?, UNIX_TIMESTAMP(), ?, ?)
- }, undef, $ppid, $reqct, $res->content );
+ INSERT INTO pp_log (ppid, ip, transtime, req_content, res_content)
+ VALUES (?, ?, UNIX_TIMESTAMP(), ?, ?)
+ }, undef, $ppid, BML::get_remote_ip(), $reqct, $res->content );
warn $dbh->errstr
if $dbh->err;
}
@@ -395,12 +395,31 @@ sub process_ipn {
my $dbh = DW::Pay::get_db_writer()
or die "failed, please retry later\n";
$dbh->do(
- q{INSERT INTO pp_log (ppid, transtime, req_content, res_content)
- VALUES (0, UNIX_TIMESTAMP(), ?, '')},
- undef, nfreeze( $form )
+ q{INSERT INTO pp_log (ppid, ip, transtime, req_content, res_content)
+ VALUES (0, ?, UNIX_TIMESTAMP(), ?, '')},
+ undef, BML::get_remote_ip(), nfreeze( $form )
);
die "failed to insert\n"
if $dbh->err;
+
+ # if this is a confirmation of a payment from a CC/other button type payment, then
+ # mark the item as being paid
+ if ( $form->{payment_status} eq 'Completed' && $form->{transaction_subject} =~ /Order #(\d+)$/ ) {
+ my $cart = DW::Shop::Cart->get_from_cartid( $1 );
+
+ # we must have a cart, and it must be in the right state and
+ # actually a credit card cart, and the price must match to prevent
+ # someone trying to spoof our button
+ return 1
+ unless $cart &&
+ $cart->state == $DW::Shop::STATE_PEND_PAID &&
+ $cart->paymentmethod eq 'creditcardpp';
+ $cart->display_total == $form->{payment_gross};
+
+ # looks good, mark it paid
+ $cart->paymentmethod( 'creditcardpp' );
+ $cart->state( $DW::Shop::STATE_PAID );
+ }
return 1;
}
diff -r 444427ab0545 -r c4f5f857fb42 cgi-bin/LJ/Widget/ShopCart.pm
--- a/cgi-bin/LJ/Widget/ShopCart.pm Wed May 13 16:26:50 2009 +0000
+++ b/cgi-bin/LJ/Widget/ShopCart.pm Fri May 15 00:46:43 2009 +0000
@@ -99,7 +99,10 @@ sub render_body {
$ret .= "<p>" . $class->html_submit( removeselected => $class->ml( 'widget.shopcart.btn.removeselected' ) ) . " ";
$ret .= $class->html_submit( discard => $class->ml( 'widget.shopcart.btn.discard' ) ) . "</p>";
- my @paypal_option = ( paypal => $class->ml( 'widget.shopcart.paymentmethod.paypal' ) )
+ my @paypal_option = (
+ paypal => $class->ml( 'widget.shopcart.paymentmethod.paypal' ),
+ creditcardpp => $class->ml( 'widget.shopcart.paymentmethod.creditcardpp' ),
+ )
if keys %LJ::PAYPAL_CONFIG;
$ret .= "<p>" . $class->ml( 'widget.shopcart.paymentmethod' ) . " ";
$ret .= $class->html_select(
@@ -124,12 +127,8 @@ sub handle_post {
my ( $class, $post, %opts ) = @_;
# check out
- if ( $post->{checkout} ) {
- my $method = 'paypal';
- $method = 'checkmoneyorder' if $post->{paymentmethod} eq 'checkmoneyorder' || !keys %LJ::PAYPAL_CONFIG;
-
- return BML::redirect( "$LJ::SITEROOT/shop/checkout?method=$method" );
- }
+ return BML::redirect( "$LJ::SITEROOT/shop/checkout?method=$post->{paymentmethod}" )
+ if $post->{checkout};
# remove selected items
if ( $post->{removeselected} ) {
diff -r 444427ab0545 -r c4f5f857fb42 htdocs/shop/creditcard.bml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/creditcard.bml Fri May 15 00:46:43 2009 +0000
@@ -0,0 +1,94 @@
+<?_c
+#
+# shop/creditcard.bml
+#
+# Generates HTML for a PayPal "Buy Now" button... maybe in the future this will
+# do something far more amazing, with super robot powers.
+#
+# 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
+body<=
+<?_code
+{
+ use strict;
+ 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' );
+ LJ::set_active_resource_group( 'jquery' );
+
+ # get their shop/cart
+ my $cart = DW::Shop->get->cart;
+ return $ML{'.error.nocart'}
+ unless $cart;
+ return $ML{'.error.emptycart'}
+ unless $cart->has_items;
+
+ # if state is NOT open, then just redirect them to the wait page,
+ # which will do the Right Thing. this typically is used in the case
+ # that the user double clicks on the form, or hits back and clicks
+ # submit again...
+ return BML::redirect( "$LJ::SITEROOT/shop/receipt?ordernum=" . $cart->ordernum )
+ unless $cart->state == $DW::Shop::STATE_OPEN;
+
+ # FIXME: if they have a $0 cart, we don't support that yet
+ return $ML{'.error.zerocart'}
+ if $cart->total == 0.00;
+
+ # looks good, set the payment method and state
+ $cart->paymentmethod( 'creditcardpp' );
+ $cart->state( $DW::Shop::STATE_PEND_PAID );
+
+ # values we need
+ my $cartid = $cart->id;
+ my $cost = $cart->display_total;
+
+ # okay, render out the button :-)
+ return qq|
+<?p <?_ml .info _ml?> p?>
+
+<form action="$LJ::PAYPAL_CONFIG{cc_url}" method="post">
+
+<!-- Identify your business so that you can collect the payments. -->
+<input type="hidden" name="business" value="$LJ::PAYPAL_CONFIG{account}" />
+
+<!-- Specify a Buy Now button. -->
+<input type="hidden" name="cmd" value="_xclick" />
+<input type="hidden" name="bn" value="${LJ::SITENAMESHORT}_BuyNow_WPS_US" />
+<input type="hidden" name="notify_url" value="$LJ::SITEROOT/shop/pp_notify" />
+
+<!-- Specify details about the item that buyers will purchase. -->
+<input type="hidden" name="item_name" value="$LJ::SITECOMPANY Order #$cartid" />
+<input type="hidden" name="amount" value="$cost" />
+<input type="hidden" name="currency_code" value="USD" />
+<input type="hidden" name="invoice" value="$cartid" />
+
+<!-- Other configuration -->
+<input type="hidden" name="no_shipping" value="1" />
+<input type="hidden" name="no_note" value="1" />
+
+<!-- Display the payment button. -->
+<input type="image" name="submit" border="0" src="https://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif"
+ alt="PayPal - The safer, easier way to pay online" />
+<img alt="" border="0" width="1" height="1" src="https://www.paypal.com/en_US/i/scr/pixel.gif" />
+
+</form>
+ |;
+}
+_code?>
+<=body
+title=><?_code return $title; _code?>
+page?>
diff -r 444427ab0545 -r c4f5f857fb42 htdocs/shop/creditcard.bml.text
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/htdocs/shop/creditcard.bml.text Fri May 15 00:46:43 2009 +0000
@@ -0,0 +1,15 @@
+;; -*- coding: utf-8 -*-
+
+.error.badcartstate=Your shopping cart has already been checked out. If your payment failed, you will need to build a new shopping cart to try again. We're sorry for the inconvenience.
+
+.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.
+
+.info=If you'd like to use a credit card to purchase your account, use the button below. This will take you to PayPal, our merchant processor. You won't need a PayPal account to use this payment option.
+
+.title=Purchase by Credit Card
diff -r 444427ab0545 -r c4f5f857fb42 htdocs/shop/receipt.bml.text
--- a/htdocs/shop/receipt.bml.text Wed May 13 16:26:50 2009 +0000
+++ b/htdocs/shop/receipt.bml.text Fri May 15 00:46:43 2009 +0000
@@ -27,7 +27,9 @@
.cart.paymentmethod.checkmoneyorder.extra=Please make your check or money order out to: [[sitecompany]]</p><p>Mail it to: [[address]]
-.cart.paymentmethod.paypal=PayPal
+.cart.paymentmethod.creditcardpp=Credit Card
+
+.cart.paymentmethod.paypal=PayPal Account
.error.invalidordernum=Your order number is invalid.
--------------------------------------------------------------------------------
