318 lines
8.9 KiB
Perl
318 lines
8.9 KiB
Perl
# ====================================================================
|
|
# Gossamer Threads Module Library - http://gossamer-threads.com/
|
|
#
|
|
# GT::Payment::Remote::2CheckOut
|
|
# Author: Jason Rhinelander
|
|
# CVS Info : 087,071,086,086,085
|
|
# $Id: 2CheckOut.pm,v 1.5 2006/08/22 20:39:04 brewt Exp $
|
|
#
|
|
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
|
# ====================================================================
|
|
#
|
|
# Description:
|
|
# 2CheckOut payment processing.
|
|
#
|
|
|
|
package GT::Payment::Remote::2CheckOut;
|
|
use strict;
|
|
use Carp;
|
|
use GT::MD5 'md5_hex';
|
|
require Exporter;
|
|
use vars qw/@EXPORT_OK/;
|
|
@EXPORT_OK = qw/process/;
|
|
|
|
sub process {
|
|
# -----------------------------------------------------------------------------
|
|
shift if $_[0] and UNIVERSAL::isa($_[0], __PACKAGE__);
|
|
|
|
my %opts = @_;
|
|
$opts{param} and UNIVERSAL::isa($opts{param}, 'GT::CGI') or croak 'Usage: ->process(param => $gtcgi, ...)';
|
|
my $in = $opts{param};
|
|
|
|
ref $opts{on_valid} eq 'CODE'
|
|
or croak 'Usage: ->process(on_valid => \&CODEREF, ...)';
|
|
|
|
defined $opts{password} and length $opts{password} or croak 'Usage: ->process(password => "password", ...)';
|
|
defined $opts{sellerid} and length $opts{sellerid} or croak 'Usage: ->process(sellerid => "sellerid", ...)';
|
|
|
|
$opts{password} eq 'tango' and croak 'Usage: ->process(password => "something other than \'tango\'", ...)';
|
|
|
|
my $order_number = $in->param('order_number');
|
|
|
|
# Check that the "secret word" (password) combined with the other information
|
|
# actually checks out.
|
|
my $str = $opts{password} . $opts{sellerid} . $order_number . $in->param('total');
|
|
my $md5 = md5_hex($str);
|
|
|
|
if (lc $md5 eq lc $in->param('key')) {
|
|
$opts{on_valid}->();
|
|
}
|
|
# If demo mode is enabled, then the order number is set to 1 in the md5:
|
|
# https://www.2checkout.com/documentation/UsersGuide2/chapter6/md5-hash.html
|
|
elsif ($opts{demo}) {
|
|
$str = $opts{password} . $opts{sellerid} . 1 . $in->param('total');
|
|
$md5 = md5_hex($str);
|
|
|
|
if (lc $md5 eq lc $in->param('key')) {
|
|
$opts{on_valid}->();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
GT::Payment::Remote::2CheckOut - 2CheckOut payment handling
|
|
|
|
=head1 CAVEATS
|
|
|
|
2CheckOut has a pretty weak automated payment system - the security of the
|
|
entire automated payment process hinges on your "Secret Word" (Admin -> Account
|
|
Details -> Return -> Secret Word (near the bottom of the page)) - without it,
|
|
there is no security at all. Another weakness in the system is that if your
|
|
server is not reachable for whatever reason, the payment information would be
|
|
lost. Payment providers like 2CheckOut and WorldPay would do well to learn
|
|
from payment systems like that of PayPal - whatever can be said about other
|
|
aspects of PayPal, they do have one of the nicest payment systems around - both
|
|
from a developer and user's point of view.
|
|
|
|
Because of the security issue with not using the "Secret Word", this module
|
|
requires that the secret word be used, even if other 2CheckOut systems may not.
|
|
Additionally, the default secret word of "tango" is not allowed.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use GT::Payment::Remote::2CheckOut;
|
|
use GT::CGI;
|
|
|
|
my $in = new GT::CGI;
|
|
|
|
GT::Payment::Remote::2CheckOut->process(
|
|
param => $in,
|
|
|
|
on_valid => \&valid,
|
|
|
|
sellerid => "1234",
|
|
password => "Some Good Secret Word"
|
|
);
|
|
|
|
sub valid {
|
|
# Update database - the payment has been made successfully.
|
|
}
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This module is designed to handle 2CheckOut payment processing.
|
|
|
|
=head1 REQUIREMENTS
|
|
|
|
GT::CGI and GT::MD5.
|
|
|
|
=head1 FUNCTIONS
|
|
|
|
This module has only one function: process() does the work of actually
|
|
figuring out what to do with a postback.
|
|
|
|
=head2 process
|
|
|
|
process() is the only function provided by this module. It can be called as
|
|
either a function or class method, and takes a hash (not hash reference) of
|
|
arguments as described below.
|
|
|
|
process() should be called for 2CheckOut initiated postbacks. This can be set
|
|
up in your main .cgi by looking for 2CheckOut-specific CGI parameters
|
|
('cart_order_id' is a good one to look for) or by making a seperate .cgi file
|
|
exclusively for handling 2CheckOut postbacks.
|
|
|
|
Additionally, it is strongly advised that database connection, authenticate,
|
|
etc. be performed before calling process() to ensure that the payment is
|
|
recorded successfully. 2CheckOut will not attempt to repost the form data if
|
|
your script produces an error, and the error will be shown to the customer.
|
|
|
|
=over 4
|
|
|
|
=item param
|
|
|
|
param takes a GT::CGI object from which 2CheckOut postback variables are read.
|
|
|
|
=item on_valid
|
|
|
|
on_valid takes a code reference as value. The code reference will be called
|
|
when a successful payment has been made. Inside this code reference you are
|
|
responsible for setting a "paid" status for the order in question. The
|
|
C<cart_order_id> CGI variable will have whatever cart_order_id you provided.
|
|
|
|
=item sellerid
|
|
|
|
This should be passed to seller number. This is needed, along with the
|
|
password field below, to verify that the posted payment is a genuine 2CheckOut
|
|
payment.
|
|
|
|
=item password
|
|
|
|
This is a "Secret Word" that the admin must set in the 2CheckOut admin area
|
|
(under Look & Feel -> Secret Word). This field must be set in the admin, and
|
|
passed in here. Note that the default value, "tango", is not allowed. Without
|
|
this password, 2CheckOut postbacks should not be considered secure.
|
|
|
|
=item demo
|
|
|
|
Whether or not to initiate and accept demo transactions.
|
|
|
|
=back
|
|
|
|
=head1 INSTRUCTIONS
|
|
|
|
To implement 2CheckOut payment processing, there are a number of steps required
|
|
in addition to this module. Basically, this module handles only the postback
|
|
stage of the 2CheckOut payment process.
|
|
|
|
=head2 Directing customers to 2CheckOut
|
|
|
|
This is done by creating a web form containing the following variables. Your
|
|
form, first of all, should post to
|
|
C<https://www.2checkout.com/2co/buyer/purchase>. See
|
|
C<https://www.2checkout.com/documentation/UsersGuide2/third_party_carts/2co-system-parameters.html>
|
|
for a complete and up-to-date list of parameters that can be passed to 2CheckOut.
|
|
|
|
Required fields are as follows:
|
|
|
|
=over 4
|
|
|
|
=item * sid
|
|
|
|
Your 2CheckOut account number
|
|
|
|
=item * total
|
|
|
|
The total amount to be billed, in DD.CC format.
|
|
|
|
=item * cart_order_id
|
|
|
|
A unique order id, which you should store to track the payment.
|
|
|
|
=back
|
|
|
|
The following parameters *may* be passed in, and will be available in the
|
|
postback:
|
|
|
|
=over 4
|
|
|
|
=item * card_holder_name
|
|
|
|
=item * street_address
|
|
|
|
=item * city
|
|
|
|
=item * state
|
|
|
|
=item * zip
|
|
|
|
=item * country
|
|
|
|
=item * phone
|
|
|
|
The card holder's details.
|
|
|
|
=item * email
|
|
|
|
The card holder's email address.
|
|
|
|
=item * ship_name
|
|
|
|
=item * ship_street_address
|
|
|
|
=item * ship_city
|
|
|
|
=item * ship_state
|
|
|
|
=item * ship_zip
|
|
|
|
=item * ship_country
|
|
|
|
Shipping info - however, according to 2CheckOut, you must indicate that you
|
|
want to take that you want to take down a seperate shipping and billing address
|
|
on the L<Shipping Details page|https://sellers.2checkout.com/cgi-bin/sellersarea/shipdetails.2c>.
|
|
|
|
=item * demo
|
|
|
|
Should be set to 'Y' if you want demo mode, omitted for regular transactions.
|
|
|
|
=back
|
|
|
|
In the postback CGI, you'll get back all of the billing and shipping variables
|
|
listed above, plus:
|
|
|
|
=over 4
|
|
|
|
=item * order_number
|
|
|
|
2CheckOut order number
|
|
|
|
=item * cart_order_id
|
|
|
|
=item * cart_id
|
|
|
|
Your order number, passed back. Both variables are the same.
|
|
|
|
=back
|
|
|
|
=head2 Postback
|
|
|
|
Before 2CheckOut postback notification can occur, you must set up the postback
|
|
(in 2CheckOut terminology, "Routine"). This can be set from the Admin ->
|
|
Shopping Cart -> Cart Details. You need to enable the payment routine, and
|
|
set it to a CGI that you manage.
|
|
|
|
=head2 Putting it all together
|
|
|
|
The typical way to implement all of this is as follows:
|
|
|
|
=over 4
|
|
|
|
=item 1 Get necessary merchant information (sid and secret keyword)
|
|
|
|
=item 2 Once the customer has selected what to purchase, generate a
|
|
cart_order_id (a random MD5 hex string works well), and store it somewhere
|
|
(i.e. in the database).
|
|
|
|
=item 3 Make a form with all the necessary fields that
|
|
L<submits to 2CheckOut|/"Directing customers to 2CheckOut">.
|
|
|
|
=item 4 Set up the L<C<on_valid>|/"on_valid"> callback. If using a dedicated
|
|
CGI script for 2CheckOut callbacks, it should just call process(); otherwise,
|
|
check for the CGI parameter 'cart_order_id' and if present, call process().
|
|
|
|
=item 5 For a valid payment, do whatever you need to do for a valid payment,
|
|
and store some record of the payment having been made (storing at least the
|
|
cart_order_id and the order_number is strongly recommended). Use the CGI
|
|
parameter 'cart_order_id' to locate the order (i.e. in the database).
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<http://www.2checkout.com> - 2CheckOut website.
|
|
|
|
L<http://www.support.2checkout.com/deskpro/faq.php> - 2CheckOut knowledgebase
|
|
|
|
=head1 MAINTAINER
|
|
|
|
Jason Rhinelander
|
|
|
|
=head1 COPYRIGHT
|
|
|
|
Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
|
http://www.gossamer-threads.com/
|
|
|
|
=head1 VERSION
|
|
|
|
Revision: $Id: 2CheckOut.pm,v 1.5 2006/08/22 20:39:04 brewt Exp $
|
|
|
|
=cut
|