From 977ae07ace47748c3abaf508a09c8e5488989ed7 Mon Sep 17 00:00:00 2001 From: Mark Wells Date: Fri, 28 Jun 2013 16:33:04 -0700 Subject: [PATCH] complete rewrite, version 0.10 --- .gitignore | 6 ++ Changes | 2 + MANIFEST | 2 - Makefile.PL | 2 +- OnlineThirdPartyPayment.pm | 225 +++++++++++++++++++++++++++------------------ notes_for_module_writers | 66 ++++++++----- t/pod.t | 9 -- 7 files changed, 185 insertions(+), 127 deletions(-) create mode 100644 .gitignore delete mode 100644 t/pod.t diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9788afa --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +blib/ +*.sw? +Makefile +Makefile.old +MYMETA.yml +pm_to_blib diff --git a/Changes b/Changes index b43b667..b8826bf 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,7 @@ Revision history for Perl extension Business::OnlineThirdPartyPayment. +0.10 unrelesed + - complete rewrite 0.01 unreleased - original version; created by jeff diff --git a/MANIFEST b/MANIFEST index 2580e9d..ab566f1 100644 --- a/MANIFEST +++ b/MANIFEST @@ -4,6 +4,4 @@ MANIFEST Makefile.PL OnlineThirdPartyPayment.pm t/00load.t -t/pod.t notes_for_module_writers -META.yml Module meta-data (added by MakeMaker) diff --git a/Makefile.PL b/Makefile.PL index 53f13e8..526b4f0 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -4,7 +4,7 @@ use ExtUtils::MakeMaker; WriteMakefile( 'NAME' => 'Business::OnlineThirdPartyPayment', 'VERSION_FROM' => 'OnlineThirdPartyPayment.pm', # finds $VERSION - 'AUTHOR' => 'Jeff Finucane ', + 'AUTHOR' => 'Mark Wells ', 'PREREQ_PM' => { 'Business::OnlinePayment' => 3, 'Test::More' => 0.42, diff --git a/OnlineThirdPartyPayment.pm b/OnlineThirdPartyPayment.pm index b642ffd..8ada8d5 100644 --- a/OnlineThirdPartyPayment.pm +++ b/OnlineThirdPartyPayment.pm @@ -7,28 +7,21 @@ use base qw(Business::OnlinePayment); require 5.005; -$VERSION = '3.00_09'; +$VERSION = 0.10; $VERSION = eval $VERSION; # modperlstyle: convert the string into a number my %fields = ( - authorization => undef, - error_message => undef, - failure_status => undef, - fraud_detect => undef, # unsupported - is_success => undef, - maximum_risk => undef, # unsupported - path => undef, - port => undef, - require_avs => undef, - result_code => undef, - server => undef, - server_response => undef, - test_transaction => undef, - transaction_type => undef, - fraud_score => undef, # unsupported - fraud_transaction_id => undef, # unsupported - popup_url => undef, - collectitems => undef, + is_success => undef, + token => undef, + redirect => undef, + post_params => undef, + statustext => undef, + order_number => undef, + authorization => undef, + error_message => undef, + test_transaction => undef, + return_url => undef, + cancel_url => undef, ); sub new { @@ -58,127 +51,177 @@ sub new { return $self; } -sub reference { - my($self) = @_; - - croak("Processor subclass did not override reference function"); -} - 1; __END__ =head1 NAME -Business::OnlineThirdPartyPayment - Perl extension for third party web page based online payment processing +Business::OnlineThirdPartyPayment - Common interface to browser-based +online payment processors =head1 SYNOPSIS use Business::OnlineThirdPartyPayment; + use CGI; - my $transaction = - new Business::OnlineThirdPartyPayment($processor, %processor_info); - $transaction->content( - type => 'CC', - action => 'Authorization Only', - amount => '49.95', - reference => '3735928559', - ); - $transaction->submit(); - - if($transaction->is_success()) { - print "Success! Redirect browser to ". $transaction->popup_url(); + my %processor_info = ( + 'return_url' => 'https://www.example.com/payment/return', + 'cancel_url' => 'https://www.example.com/payment/cancel', + # ... other processor-specific values + ); + + my $txn = Business::OnlineThirdPartyPayment->new('MyBank', %processor_info); + # start a payment + $txn->create( customer_id => 4030, + invoice_number => 10318, + amount => 49.95, + currency => 'CAD', + description => 'Internet Services', + ); + + if ( $txn->is_success} ) { + store_payment_id($txn->token); # keep it somewhere + print CGI->redirect( $txn->redirect ); } else { - print "Card was rejected: ", $transaction->error_message(), "\n"; + die $txn->error_message; } - #when the provider calls us back via HTTP - use CGI; - my $cgi = new CGI; #initialized to current page + ... + # then, at /payment/return... + my $txn = Business::OnlineThirdPartyPayment->new('MyBank', %processor_info); + $txn->token( get_payment_id($cgi) ); # probably from a session cookie + my %params = $cgi->Vars; + + $txn->execute(%params); - my $reference = $transaction->reference($cgi->Vars); - $transaction->content( - type => 'CC', - action => 'Post Authorization', - reference => $reference, - ); - $transaction->submit(); - if($transaction->is_success()) { - print "Card processed scucessfully: ", $transaction->authorization(), "\n"; + if ( $txn->is_success ) { + store_payment_success($txn->order_number, $txn->authorization); + print "Your payment was successful!\n"; } else { - print "Card was rejected: ", $transaction->error_message(), "\n"; + store_payment_failure($txn->order_number, $txn->error_message); + print "Your payment failed.\n"; } - - =head1 DESCRIPTION Business::OnlineThirdPartyPayment is a generic module for processing payments through online credit card processors, electronic cash systems, etc. through -which the payors web browser is redirected. It is a subclass of -L +which the payer's web browser is redirected. + +=head1 METHODS -=head1 METHODS AND FUNCTIONS +=head2 new($module, %processor_options) -=head2 new($processor, %processor_options); +Class method. Create a Business::OnlineThirdPartyPayment object. +$module is required, and defines the gateway module to use. The +other options are processor-specific, but should always include: -Create a new Business::OnlineThirdPartyPayment object, $processor is required, -and defines the online processor to use. If necessary, processor -options can be specified, currently supported options are 'Server', -'Port', and 'Path', which specify how to find the online processor -(https://server:port/path), but individual processor modules should -supply reasonable defaults for this information, override the defaults -only if absolutely necessary (especially path), as the processor -module was probably written with a specific target script in mind. +=over 4 + +=item return_url - The callback URL to redirect the user to after completing +a payment. + +=item cancel_url - The URL to redirect the user to if they choose not to +complete the payment. + +=back -=head2 content(%content); +=head2 create(%content) -The information necessary for the transaction, this tends to vary a -little depending on the processor, so we have chosen to use a system -which defines specific fields in the frontend which get mapped to the -correct fields in the backend. Currently defined fields are: +Tell the gateway module to start a transaction. This may involve +communicating with the gateway, or it may happen entirely locally. +%content is a hash containing some of the following: -=head3 PROCESSOR FIELDS +=head3 TRANSACTION FIELDS =over 4 -=item * reference +=item amount - The amount of the transaction, as a decimal number. Required. + +=item description - A description of the purchase. Required. + +=item invoice_number - Your invoice number, if this payment is associated +with a specific invoice. + +=item currency - Currency, specified as an ISO 4217 three-letter code, such +as USD, CAD, EUR, AUD, DKK, GBP, JPY, NZD, etc. + +=item customer_id - The customer number, if any. + +=item email - Customer's email address. -A unique reference for this transaction. +=item customer_ip - IP address from which the transaction originated. =back For additional fields, see L and the specific processor module used. -=head2 submit(); +C sets properties of the transaction object to indicate the +result. These will always include: -Submit the transaction to the processor for completion +=over 4 + +=item is_success - 1 if the transaction was created successfully, 0 +otherwise. + +=item error_message - a text description of any error that occurred. +Since the payer hasn't provided a payment account or any other +information at this stage, this is probably not meaningful to them. + +=item token - a reference string that will be used to identify +this transaction later. If is_success is true, you I store this +value in your merchant system and pass it to execute() later. Merchant +systems should allow at least 256 characters for this string. + +=item statustext - a freeform text field for any state information +that the gateway needs to receive to complete the transaction. -=head2 is_success(); +=item redirect - a URL to redirect the payer to. -Returns true if the transaction was submitted successfully, false if -it failed (or undef if it has not been submitted yet). +=item post_params - the content of an HTTP POST request to send to the +URL. Since HTTP redirects can't include POST content, in this case the +front-end system must provide another way (a self-submitting form) to +induce the purchaser's browser to make the request. -=head2 test_transaction(); +=back + +=head2 execute(%params) + +Complete the transaction. This should be called from the web server +(from 'return_url'). %params must contain any query parameters passed +in the redirect, and token should be set on the object. -Most processors provide a test mode, where submitted transactions will -not actually be charged or added to your batch, calling this function -with a true argument will turn that mode on if the processor supports -it, or generate a fatal error if the processor does not support a test -mode (which is probably better than accidentally making real charges). +execute() will set the transaction fields to indicate the result: + +=over 4 + +=item is_success: 1 if the payment was successful, 0 if it was not. + +=item error_message: An error message describing the reason for failure. +Unlike the message returned from C, this probably is meaningful +to the user, though the gateway may already have shown it to them. + +=item order_number: The transaction ID number assigned by the processor. + +=item authorization: The credit card or other authorization number +returned by the gateway. This may be needed to refund the payment or +for other customer service issues. + +=back + +If C is true, the merchant system should record this as +a successful payment and apply it to the customer's account. =head1 AUTHOR -Jeff Finucane +Mark Wells , based in part on code by Jeff Finucane. =head1 COPYRIGHT -Partially based on code from Business::OnlinePayment. - Copyright (c) 1999-2004 Jason Kohles. -Copyright (c) 2004 Ivan Kohler -Copyright (c) 2007-2009 Freeside Internet Services, Inc. +Copyright (c) 2007-2013 Freeside Internet Services, Inc. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. @@ -193,6 +236,4 @@ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. L, http://420.am/business-onlinepayment/ -For verification of credit card checksums, see L. - =cut diff --git a/notes_for_module_writers b/notes_for_module_writers index 57bd098..91c1acb 100644 --- a/notes_for_module_writers +++ b/notes_for_module_writers @@ -7,27 +7,47 @@ NOTE: You should read the notes in Business::OnlinePayment first. Create a subclass of Business::OnlineThirdPartyPayment called -Business::OnlinePayment::(processor name). You should override at least -the set_defaults, submit, and reference methods. - -See Business::OnlineThirdPartyPayment::Interswitchng as an example. - -submit: - This method functions with both a "Authorize Only" and "Post Authorization" -action. The former must, at a minumum, ensure that popup_url will return -an appropriate value when submit also sets is_successful. - - Nothing has happened at this point other than verifying the data is valid -and providing the caller with a redirection url. - - When called with "Post Authorization" steps are taken to verify that funds -have been authorized by the processor. - -reference: - This method is called with a hash of key/value pairs, typically as the -result of a HTTP GET or POST, but by whatever mechanism the processor provides. -The return value must be the unique reference provided to the "Authorize Only" -submit. Since the usage consists of submit(action=>'Authorize Only'), -reference(key=>value...), submit(action=>'Post Authorization'), this may be -the appropriate time to set the state of the object for some processors. +Business::OnlineThirdPartyPayment::(processor name). You should override at +least the set_defaults, create, and execute methods. + +See Business::OnlineThirdPartyPayment::PayPal as an example. + +create(%content): +This method's primary functions are: +- to determine/create the URL the purchaser must visit to make payment, and +- to assign a token to the transaction. + +%content will always include 'amount' (the amount of the payment) and +'description' (a text description of the product being purchased). + +If the processor requires the merchant to initiate the transaction (e.g. +PayPal), then create() should do that, and store the transaction ID assigned +by the processor in 'token'. Otherwise, create() can simply assign a +semi-random, unique string to 'token', and additionally pass it to the +processor as the order number or other identifier. + +The URL the purchaser needs to visit should be assigned to the 'redirect' +property of the object. If the processor expects to receive the transaction +parameters from the purchaser's browser in the form of a POST request, +create() can store them in a hashref in the 'post_params' property. + +execute(%params): +This method's primary function is to determine whether the transaction +was successful. The 'token' property should already be set to whatever it +was after the initial create(). %params is everything passed back by the +processor to the callback URL. + +If the merchant is required to confirm or "capture" the payment after +authorization, execute() should do that. (PayPal again.) Other processors +(see B:OTP:FCMB for an example) provide a separate API to query the +transaction status; execute() should do that. In either case, it should +set the 'is_success' property to 0 or 1, and set 'error_message' if the +payment failed, or 'order_number' and 'authorization' if it succeeded. + +set_defaults(%arguments): +This is called from the constructor. The build_subs() method of +Business::OnlinePayment is available for creating accessor methods. +In general, modules should look for the merchant ID in 'username', any +password or shared secret in 'password', and the callback URL in +'return_url'. diff --git a/t/pod.t b/t/pod.t deleted file mode 100644 index 2c9935c..0000000 --- a/t/pod.t +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use Test::More; - -eval "use Test::Pod 1.00"; -plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; -all_pod_files_ok(); -- 2.11.0