Initial git commit of release 0.62

This commit is contained in:
Daniel Hembree 2009-12-02 08:44:18 -08:00 committed by Danny Hembree
commit 925e625e76
30 changed files with 5489 additions and 0 deletions

178
Changes Normal file
View file

@ -0,0 +1,178 @@
Revision history for Perl extension Business::PayPal::API.
Release 0.62 - last scottw release
----------------------------
revision 1.24
date: 2009/07/28 18:00:58; author: scott; state: Exp; lines: +44 -4
- getFields() returns an array ref if multiple values are returned
from PayPal, otherwise it behaves as it always did, returning a
non-ref scalar.
- BillingAgreement information returned in ExpressCheckout
- Fix GetTransactionDetails 'PII_SalesTax' parameter
- Fix GetTransactionDetails to return multiple PaymentItems
- Fix MassPay to use the correct UniqueID parameter
- Add DoReferenceTransaction to RecurringPayments API
- Lots of credit given to great folks with patches in API docs
Thanks everyone! I'm handing this module off to Danny Hembree for
maintenance now.
Release 0.61
----------------------------
revision 1.23
date: 2008/05/05 15:10:40; author: scott; state: Exp; lines: +37 -5
- timeout setting available
- additional troubleshooting documentation (auth error handling, URL munging)
----------------------------
revision 1.4 [DirectPayments.pm]
date: 2008/05/05 15:11:14; author: scott; state: Exp; lines: +34 -2
- add shipto parameters
----------------------------
revision 1.12 [ExpressCheckout.pm]
date: 2008/05/05 15:11:51; author: scott; state: Exp; lines: +11 -3
- MaxAmount parameter take currencyID attribute (Sandbox)
----------------------------
revision 1.1 [RecurringPayments.pm]
date: 2008/05/05 15:15:04; author: scott; state: Exp;
- add base for RecurringPayments (not working yet)
Release 0.51
----------------------------
revision 1.22
date: 2007/09/27 20:32:31; author: scott; state: Exp; lines: +18 -6
- [API.pm] add three-token signature testing URI (Oliver Ready)
- [DirectPayments.pm] add ShippingTotal field (patch: Michael Hendricks)
- [VoidRequest.pm] documentation fixes for method call (Oliver Ready)
- [ReauthorizationRequest.pm] documentation fixes for method call (Oliver Ready)
Release 0.51
----------------------------
revision 1.10
date: 2007/08/29 20:56:42; author: scott; state: Exp; lines: +4 -2
- fix PayerStatus location in DOM, add AddressStatus element (patch: Michael Hendricks)
Release 0.50
----------------------------
revision 1.20
date: 2007/05/21 21:59:33; author: scott; state: Exp; lines: +5 -5
- minor doc changes
- new Mass Pay API module included
----------------------------
Release 0.41
----------------------------
revision 1.19
date: 2007/05/15 20:46:56; author: scott; state: Exp; lines: +34 -12
- some minor documentation additions
Release 0.40
----------------------------
revision 1.18
date: 2006/10/06 17:53:44; author: scott; state: Exp; lines: +2 -2
- version bump
----------------------------
revision 1.17
date: 2006/10/06 17:49:50; author: scott; state: Exp; lines: +42 -38
- using PayPal API version 2.0
- note about SSL requirements of LWP
- minor documentation cleanup
- DirectPayments tests added
- New modules (with tests!) from Danny Hembree:
AuthorizationRequest.pm
CaptureRequest.pm
ReauthorizationRequest.pm
VoidRequest.pm
----------------------------
revision 1.16
date: 2006/07/06 15:25:21; author: scott; state: Exp; lines: +9 -2
- add acknowledgement and pointers to example code from Andy Spiegl and others
Release 0.33
----------------------------
revision 1.15
date: 2006/07/05 18:05:39; author: scott; state: Exp; lines: +2 -2
- fix documentation regarding SetExpressCheckout (returns a hash, not
a scalar). If you were following the code in the SYNOPSIS for
ExpressCheckout and getting a token with '3/8' or '4/8', you should
change the $token to %response, since SetExpressCheckout() returns a
hash (big thanks to Andy Spiegl for finding this).
Release 0.32
----------------------------
revision 1.14
date: 2006/07/03 15:46:24; author: scott; state: Exp; lines: +16 -1
- acknowledgements section
----------------------------
revision 1.13
date: 2006/07/03 15:40:16; author: scott; state: Exp; lines: +11 -2
- fix for 3-token auth
Release 0.31
----------------------------
revision 1.12
date: 2006/06/29 02:36:24; author: scott; state: Exp; lines: +2 -2
- fix StateOrProvince typo [rt.cpan.org #20176]
Release 0.30
----------------------------
revision 1.11
date: 2006/04/18 16:23:18; author: scott; state: Exp; lines: +2 -2
- added DirectPayments API by Daniel Hembree. Thanks Daniel!
Release 0.23
----------------------------
revision 1.10
date: 2006/04/04 19:29:08; author: scott; state: Exp; lines: +12 -5
- typo fix
- wrap soap call in eval for safety (if ssl neg. fails w/ paypal, it croaks)
- version bump
Release 0.22
----------------------------
revision 1.9
date: 2006/03/28 18:05:03; author: scott; state: Exp; lines: +54 -44
- documentation updates
Release 0.21
----------------------------
revision 1.8
date: 2006/03/24 17:12:59; author: scott; state: Exp; lines: +14 -4
- fix fault printing
- fix getFieldsList record population
Release 0.20
----------------------------
revision 1.7
date: 2006/03/23 17:28:10; author: scott; state: Exp; lines: +90 -9
- allow subclass methods to be imported into API.pm namespace
----------------------------
revision 1.6
date: 2006/03/22 23:20:09; author: scott; state: Exp; lines: +2 -2
- version bump
----------------------------
revision 1.5
date: 2006/03/22 23:19:03; author: scott; state: Exp; lines: +40 -13
- add GetTransactionDetails API
- add TransactionSearch API
- getFieldList() for API.pm
----------------------------
revision 1.4
date: 2006/03/21 22:15:11; author: scott; state: Exp; lines: +2 -2
- bump version
----------------------------
revision 1.3
date: 2006/03/21 22:05:19; author: scott; state: Exp; lines: +55 -31
- minor debugging changes
- documentation update for developers
- note about using IO::Socket::SSL (don't)
----------------------------
revision 1.2
date: 2006/03/16 23:33:49; author: scott; state: Exp; lines: +184 -26
- initial checkin of API and subclasses
- all tests working, documentation done
----------------------------
revision 1.1
date: 2006/03/15 23:33:53; author: scott; state: Exp;
- moved from Business::PayPal::WPP::ExpressCheckout
- uses API.pm for authorization/authentication and other common functions
=============================================================================

30
MANIFEST Normal file
View file

@ -0,0 +1,30 @@
Changes
Makefile.PL
MANIFEST
README
eg/paypal-checkout-example.pl
lib/Business/PayPal/API.pm
lib/Business/PayPal/API/AuthorizationRequest.pm
lib/Business/PayPal/API/CaptureRequest.pm
lib/Business/PayPal/API/DirectPayments.pm
lib/Business/PayPal/API/ExpressCheckout.pm
lib/Business/PayPal/API/GetTransactionDetails.pm
lib/Business/PayPal/API/MassPay.pm
lib/Business/PayPal/API/ReauthorizationRequest.pm
lib/Business/PayPal/API/RecurringPayments.pm
lib/Business/PayPal/API/RefundTransaction.pm
lib/Business/PayPal/API/TransactionSearch.pm
lib/Business/PayPal/API/VoidRequest.pm
t/API.pl
t/Business-PayPal-API.t
t/DirectPayments.t
t/ExpressCheckout.t
t/ExpressOrder.t
t/GetTransactionDetails.t
t/MassPay.t
t/RecurringPayments.t
t/RefundTransaction.t
t/TransactionSearch.t
auth.sample.3token
auth.sample.cert
META.yml

11
META.yml Normal file
View file

@ -0,0 +1,11 @@
# http://module-build.sourceforge.net/META-spec.html
#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
name: Business-PayPal-API
version: 0.62
version_from: lib/Business/PayPal/API.pm
installdirs: site
requires:
SOAP::Lite: 0.67
distribution_type: module
generated_by: ExtUtils::MakeMaker version 6.30

12
Makefile.PL Normal file
View file

@ -0,0 +1,12 @@
use 5.008001;
use ExtUtils::MakeMaker;
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
# the contents of the Makefile that is written.
WriteMakefile(
NAME => 'Business::PayPal::API',
VERSION_FROM => 'lib/Business/PayPal/API.pm',
PREREQ_PM => { SOAP::Lite => 0.67 },
($] >= 5.005 ?
(ABSTRACT_FROM => 'lib/Business/PayPal/API.pm',
AUTHOR => 'Scott Wiersdorf <scott@perlcode.org>') : ()),
);

62
README Normal file
View file

@ -0,0 +1,62 @@
Business-PayPal-API version 0.62
================================
INSTALLATION
To install this module type the following:
perl Makefile.PL
make
WPP_TEST=auth.txt make test
make install
Please notice that this module requires you have several things before
you can test it:
- a sandbox personal PayPal account
- a sandbox business PayPal account
- API credentials (either a certificate or signature)
- auth.txt, which contains your API credentials
Acquiring these things is your job, not mine. Read PayPal's
and this module's documentation to learn more.
If you do not set the WPP_TEST environment variable, sandbox tests
will be skipped.
The format of the authentication tokens file defined by WPP_TEST may
be found in the Business::PayPal::API documentation under
"TESTING". Sample auth.txt files may be found in 'auth.sample.3token'
and 'auth.sample.cert' in this distribution.
USAGE
use Business::PayPal::API qw( ExpressCheckout );
my $pp = new Business::PayPal::API( Username => 'my_api.username.tld',
Password => 'API_PASSWORD',
CertFile => '/path/to/cert.pem',
KeyFile => '/path/to/key.pem', );
...
my %details = $pp->GetExpressCheckoutDetails( $token );
DEPENDENCIES
This module requires these other modules and libraries:
SOAP::Lite 0.67 or later.
Crypt::SSLeay required for certificate auth
COPYRIGHT AND LICENCE
This package except those modules listed below are copyright (C) 2006
by Scott Wiersdorf
AuthorizationRequest, CaptureRequest, DirectPayments,
ReauthorizationRequest, and VoidRequest modules are copyright (C) 2006
by Daniel Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.

3
auth.sample.3token Normal file
View file

@ -0,0 +1,3 @@
Username = test1_api.mydomain.tld
Password = XXXXXXXXXXXXXXXX
Signature = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

4
auth.sample.cert Normal file
View file

@ -0,0 +1,4 @@
Username = test1_api.mydomain.tld
Password = myapipassword
CertFile = /www/var/cert_key_pem.txt
KeyFile = /www/var/cert_key_pem.txt

View file

@ -0,0 +1,784 @@
#!/usr/bin/perl
#
# paypal-checkout -- Interface zu PayPals ExpressCheckout SOAP-API
#
############################################
#
# Copyright (C) 2006 by Andy Spiegl, KasCada
#
# This perl script is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself, either Perl version 5.8.6 or,
# at your option, any later version of Perl 5 you may have available.
#
############################################
#
# History:
#
# v0.1 2005-12-09: erste funktionierende Version
#
############################################
my $VERSION = "0.1";
############################################
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use Business::PayPal::API::ExpressCheckout;
use Data::Dumper;
# security for shell calls:
$ENV{'PATH'} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
############################################
# globale VARIABLEN
############################################
my $debug = 0;
my $errors_occurred = 0;
# some self detection
my $self = $0; $self =~ s|.*/||;
my $hostname = `/bin/hostname -f`;
chomp $hostname;
$hostname="none" unless $hostname;
my $TMPDIR = $ENV{'TEMP'} || $ENV{'TMPDIR'} || "/tmp";
############################################
# konfigurierbare VARIABLEN
############################################
# unser PayPal-Username, PayPal-Passwort und PayPal-Signature
# werden über das Environment oder als Parameter übergeben.
my $pp_username = $ENV{'PP_USERNAME'} if $ENV{'PP_USERNAME'};
my $pp_password = $ENV{'PP_PASSWORD'} if $ENV{'PP_PASSWORD'};
my $pp_signature = $ENV{'PP_SIGNATURE'} if $ENV{'PP_SIGNATURE'};
############################################
# command line options
############################################
# option defaults
my $showhelp = 0;
my $showmanpage = 0;
my $showversion = 0;
my $step = 0;
my ($OrderTotal, $InvoiceID, $BuyerEmail, $OrderDescription);
my $ReturnURL='http://blafaselfoo.sonst.was/paypal/return';
my $CancelURL='http://blafaselfoo.sonst.was/paypal/cancel';
my $PageStyle = '';
my $cpp_header_image = '';
my $cpp_header_border_color = '';
my $cpp_header_back_color = '';
my $cpp_payflow_color = '';
my $Token;
my $PayerID;
GetOptions(
"help|usage" => \$showhelp, # show usage
"manpage" => \$showmanpage, # show manpage
"version" => \$showversion, # show programm version
"debug+" => \$debug, # (incremental option)
"username=s" => \$pp_username, # 3-token PayPal-Zugangsdaten
"password=s" => \$pp_password, # 3-token PayPal-Zugangsdaten
"signature=s" => \$pp_signature, # 3-token PayPal-Zugangsdaten
"step=i" => \$step, # wievielter Schritt des Zahlungsvorgangs
"OrderTotal=s" => \$OrderTotal, # Betrag in Euro
"OrderDescription=s" => \$OrderDescription, # 127 Zeichen Beschreibung
"InvoiceID=s" => \$InvoiceID, # eindeutige Rechnungs-ID
"BuyerEmail=s" => \$BuyerEmail, # E-Mail des Kunden
"ReturnURL=s" => \$ReturnURL, # redirect-URL nach Kauf
"CancelURL=s" => \$CancelURL, # redirect-URL bei Abbruch
"PageStyle=s" => \$PageStyle,
"cpp_header_image=s" => \$cpp_header_image,
"cpp_header_border_color=s" => \$cpp_header_border_color,
"cpp_header_back_color=s" => \$cpp_header_back_color,
"cpp_payflow_color=s" => \$cpp_payflow_color,
"Token=s" => \$Token, # PayPal-Token
"PayerID=s" => \$PayerID, # PayPal-PayerID
) or pod2usage(-exitstatus => 1, -verbose => 0);
# turn off buffering (sinnvoll für Debugging)
$| = 1 if $debug;
# are there more arguments?
if ($#ARGV >= 0)
{
pod2usage(-message => "ERROR: unknown arguments \"@ARGV\".\n",
-exitstatus => 2,
-verbose => 0
);
}
pod2usage(-exitstatus => 0, -verbose => 1) if $showhelp;
pod2usage(-exitstatus => 0, -verbose => 2) if $showmanpage;
if ($showversion)
{ print "$self - Version: $VERSION\n"; exit; }
if ($debug)
{
print "DEBUG-Modus($debug): schalte $self in Debugmodus.\n";
}
############################################
# Hauptprogramm
############################################
print "Starte $self (v$VERSION)\n" if $debug;
# ohne Zugangsdaten können wir gleich aufhören
if (not ($pp_username and $pp_password and $pp_signature))
{
&error_exit("Environment-Variablen PP_USER, PP_PASS und PP_SIGNATURE müssen gesetzt sein oder per Parameter angegeben werden.", 5);
}
&print_debug("PayPal-Username: $pp_username", 1);
&print_debug("PayPal-Passwort: $pp_password", 1);
&print_debug("PayPal-Signatur: $pp_signature", 1);
# Authentifizierungsdaten an API-Modul übergeben
# see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::ExpressCheckout
(
Username => $pp_username,
Password => $pp_password,
Signature => $pp_signature,
sandbox => 0
);
if ($debug >= 2)
{
$Business::PayPal::API::Debug = 1;
}
# Zahlungsvorgang Schritt 1
if ($step == 1)
{
# Parameter prüfen
##################
if (not $OrderTotal)
{
&error_exit("OrderTotal fehlt.", 11);
}
&print_debug("OrderTotal: $OrderTotal:", 1);
if (not $OrderDescription)
{
&error_exit("OrderDescription fehlt.", 12);
}
if (length($OrderDescription) > 127)
{
&print_debug("Achtung, kürze zu lange OrderDescription auf 127 Zeichen.", 1);
$OrderDescription = substr($OrderDescription, 1, 127);
}
&print_debug("OrderDescription: $OrderDescription", 1);
if (not $InvoiceID)
{
&error_exit("InvoiceID fehlt.", 13);
}
&print_debug("InvoiceID: $InvoiceID:", 1);
if (not $BuyerEmail)
{
&error_exit("BuyerEmail fehlt.", 14);
}
&print_debug("BuyerEmail: $BuyerEmail:", 1);
if (not $ReturnURL)
{
&error_exit("ReturnURL nicht angegeben.", 15);
}
&print_debug("ReturnURL: $ReturnURL:", 1);
if (not $CancelURL)
{
&error_exit("CancelURL nicht angegeben.", 16);
}
&print_debug("CancelURL: $CancelURL:", 1);
# und jetzt abschicken
######################
my %response = $pp->SetExpressCheckout
( OrderTotal => $OrderTotal,
MaxAmount => $OrderTotal, # es fällt keine Steuer und kein Shipping an
currencyID => 'EUR',
InvoiceID => $InvoiceID,
NoShipping => 1,
LocaleCode => 'de_DE',
BuyerEmail => $BuyerEmail,
OrderDescription => $OrderDescription,
ReturnURL => $ReturnURL,
CancelURL => $CancelURL,
PageStyle => $PageStyle,
'cpp-header-image' => $cpp_header_image,
'cpp-header-border-color' => $cpp_header_border_color,
'cpp-header-back-color' => $cpp_header_back_color,
'cpp-payflow-color' => $cpp_payflow_color,
);
if ($debug >= 2)
{
print "----SetExpressCheckout---------------\n";
print Data::Dumper->Dump([\%response], [qw(response)]);
print "-------------------------------------\n";
}
# hat's geklappt?
if ($response{'Ack'} ne "Success")
{
&error_exit("PayPal hat \"". $response{'Ack'} ."\" gemeldet: (" .
$response{'Errors'}[0]->{'ErrorCode'} .") ".
$response{'Errors'}[0]->{'LongMessage'} ." (CorrelationID: ".
$response{'CorrelationID'} .")",
18
);
}
my $token = $response{'Token'};
print "Token: $token\n";
print "Redirect: https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=$token\n";
foreach my $field (keys %response)
{
next if $field =~ /^Token|Version|Build|Ack$/;
print $field, ": ", $response{$field}, "\n";
}
}
# Zahlungsvorgang Schritt 2
elsif ($step == 2)
{
# Parameter prüfen
##################
if (not $Token)
{
&error_exit("Token muss angegeben werden.", 20);
}
&print_debug("Token: $Token:", 1);
# Kunden Checkout Details von PayPal abholen
############################################
my %details = $pp->GetExpressCheckoutDetails($Token);
if ($debug >= 2)
{
print "------GetExpressCheckoutDetails---------\n";
print Data::Dumper->Dump([\%details], [qw(details)]);
print "----------------------------------------\n";
}
# hat's geklappt?
if ($details{Ack} ne "Success")
{
&error_exit("PayPal hat \"". $details{'Ack'} ."\" gemeldet: (" .
$details{'Errors'}[0]->{'ErrorCode'} .") ".
$details{'Errors'}[0]->{'LongMessage'} ." (CorrelationID: ".
$details{'CorrelationID'} .")",
28
);
}
# als erstes die PayerID ausgeben
my $PayerID = "(noch unbekannt)";
$PayerID = $details{PayerID} if $details{PayerID};
print "PayerID: $PayerID\n";
foreach my $field (keys %details)
{
next if $field =~ /^PayerID|Token|Version|Build|Ack$/;
print $field, ": ", $details{$field}, "\n";
}
}
# Zahlungsvorgang Schritt 3
elsif ($step == 3)
{
# Parameter prüfen
##################
if (not $OrderTotal)
{
&error_exit("OrderTotal fehlt.", 6);
}
&print_debug("OrderTotal: $OrderTotal:", 1);
if (not $Token)
{
&error_exit("Token muss angegeben werden.", 30);
}
&print_debug("Token: $Token:", 1);
if (not $PayerID)
{
&error_exit("PayerID muss angegeben werden.", 31);
}
&print_debug("PayerID: $PayerID:", 1);
# PayPal zur Ausführung der Zahlung auffordern
##############################################
my %payinfo = $pp->DoExpressCheckoutPayment
(
Token => $Token,
PaymentAction => 'Sale',
PayerID => $PayerID,
currencyID => 'EUR',
OrderTotal => $OrderTotal,
);
if ($debug >= 2)
{
print "----DoExpressCheckoutPayment---------------\n";
print Data::Dumper->Dump([\%payinfo], [qw(payinfo)]);
print "-------------------------------------------\n";
}
# hat's geklappt?
if ($payinfo{'Ack'} ne "Success")
{
&error_exit("PayPal hat \"". $payinfo{'Ack'} ."\" gemeldet: (" .
$payinfo{'Errors'}[0]->{'ErrorCode'} .") ".
$payinfo{'Errors'}[0]->{'LongMessage'} ." (CorrelationID: ".
$payinfo{'CorrelationID'} .")",
38
);
}
foreach my $field (keys %payinfo)
{
next if $field =~ /^PayerID|Token|Version|Build|Ack$/;
print $field, ": ", $payinfo{$field}, "\n";
}
}
else
{
print "Parameter \"step\" muss zwischen 1 und 3 liegen.\n";
}
&cleanup_and_exit();
############################################
# Hilfsroutinen
############################################
sub print_error
{
my ($text) = @_;
print STDERR "ERROR: ". $text ."\n";
$errors_occurred++;
if ($errors_occurred > 10)
{
print STDERR "ERROR: Zu viele Fehler ($errors_occurred) aufgetreten -> Abbruch\n";
&cleanup_and_exit();
}
}
sub print_debug
{
my ($text, $debug_level) = @_;
$debug_level = 0 unless $debug_level;
if ($debug >= $debug_level)
{
print "DEBUG($debug_level): ". $text ."\n";
}
}
sub error_exit
{
my ($text, $exitcode) = @_;
&print_error($text);
&cleanup_and_exit($exitcode);
}
# nötige Aufräumarbeiten am Ende
sub cleanup
{
&print_debug("cleanup done.", 1);
}
# Exitcode als optionaler Parameter
sub cleanup_and_exit
{
my ($exitcode) = @_;
$exitcode = 0 unless $exitcode;
&cleanup();
if ($errors_occurred)
{
&print_debug("Fertig, aber es sind $errors_occurred Fehler aufgetreten.\n", 1);
exit 100+$errors_occurred unless $exitcode;
}
&print_debug("$self (v$VERSION) beendet.\n", 1);
exit $exitcode;
}
#----------------------------------------------------------------------------
# Doku
#----------------------------------------------------------------------------
__END__
=head1 NAME
paypal-checkout -- Interface zu PayPals ExpressCheckout SOAP-API
=head1 SYNOPSIS
C<paypal-checkout> [--help|--usage] [--version] [--manpage] [--debug]
[--username] [--password] [--signature]
[--step]
[--OrderTotal] [--OrderDescription] [--InvoiceID] [--BuyerEmail]
[--Token]
=head1 DESCRIPTION
B<paypal-checkout> ist ein (mehr oder weniger) komfortables Interface zu
PayPals ExpressCheckout SOAP-API, um Zahlungen von Kunden per PayPal
entgegenzunehmen. Der Kunde wird dafür zu der PayPal-Webseite
weitergeleitet, wo er die Zahlung bestätigen muss und dann zur
ursprünglichen Website (also unserer) zurückgeleitet wird, um den Vorgang
abzuschliessen.
Der Ablauf ist folgender:
B<Schritt 1>
paypal-checkout --step 1 \
--OrderTotal '1.23' \
--InvoiceID 'Rechnung12346' \
--OrderDescription '127 bytes to describe the order' \
--ReturnURL 'http://blafaselfoo.sonst.was/paypal/return' \
--CancelURL 'http://blafaselfoo.sonst.was/paypal/cancel'
--BuyerEmail 'kunde@seineemailadresse.de' \
Als Antwort kommt dann z.B.:
Token: EC-15K077519T503945L
Redirect: https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-15K077519T503945L
Timestamp: 2006-07-04T18:06:15Z
CorrelationID: 5edc524d89b9d
Der Kunde wird von PayPal nach seiner Zahlungsbestätigung dann auf die
folgende URL zurückgeleitet:
http://blafaselfoo.sonst.was/paypal/return?token=EC-15K077519T503945L&PayerID=...
Oder wenn er den "Abbrechen"-Knopf drückt hierhin:
http://blafaselfoo.sonst.was/paypal/cancel?token=EC-15K077519T503945L
[NB: Falls schon ein '?' in der URL vorkommt, wird '&token=...' angehängt]
PayPal akzeptiert auch noch diese Parameter zur Gestaltung der Webseite mit
unserem Firmenlayout:
PageStyle
cpp_header_image
cpp_header_border_color
cpp_header_back_color
cpp_payflow_color
B<Schritt 2>
Nun können wir uns die Kundendaten von Paypal abholen:
paypal-checkout --step 2 \
--Token 'EC-15K077519T503945L'
Als Antwort kommt dann z.B.:
PayerID: XXXXXXXXXXX
FirstName: Heinz-Otto
LastName: Meier
Payer: kunde@seineemailadresse.de
InvoiceID: Rechnung12346
Timestamp: 2006-07-04T16:30:43Z
CorrelationID: f585a8a8426b1
Weitere mögliche Felder sind:
ContactPhone
PayerStatus
PayerBusiness
Name
Street1
Street2
CityName
StateOrProvince
PostalCode
Country
"PayerID" ist immer in der ersten Zeile (da diese ID für den 3.Schritt
benötigt wird), danach folgen optional alle weiteren Felder, die PayPal
über diesen Kunden bekannt gibt.
B<Schritt 3>
Und schließlich müssen wir noch die Zahlung endgültig durchführen. Dabei
muss die PayerID (s. 2.Schritt) und der Betrag (der auch anders als im
1.Schritt sein darf) nochmals angegeben werden:
paypal-checkout --step 3 \
--Token EC-15K077519T503945L \
--PayerID XXXXXXXXXXX \
--OrderTotal '1.23' \
PayPal akzeptiert auch noch diese (momentan nicht implementierten) Parameter:
OrderDescription
ItemTotal
ShippingTotal
HandlingTotal
TaxTotal
InvoiceID
ButtonSource
(An identification code for use by third-party applications to identify transactions.)
NotifyURL
(Your URL for receiving Instant Payment Notification (IPN) about this transaction.
NOTE: If you do not specify NotifyURL in the request, the notification
URL from your Merchant Profile is used, if one exists.)
PDI_Name, PDI_Amount, PDI_Number, PDI_Quantity, PDI_Tax
(PDI=PaymentDetailsItem)
Als Antwort kommt dann z.B.:
TaxAmount: 0.00
PaymentType: instant
PaymentStatus: Completed
PendingReason: none
Timestamp: 2006-07-04T16:51:31Z
GrossAmount: 0.12
CorrelationID: ec073855c7f6
TransactionID: 4BP770794S779432R
TransactionType: express-checkout
PaymentDate: 2006-07-04T16:51:30Z
Weitere mögliche Felder sind:
FeeAmount
SettleAmount
TaxAmount
ExchangeRate
=head1 OPTIONS
Alle Optionen können mit einem eindeutigen Anfang abgekürzt werden.
=over 3
=item B<--debug>
Debugmeldungen ausgeben (kann mehrfach angegeben werden, um detailliertere Informationen zu sehen).
=item B<--help>, B<--usage>
Syntax anzeigen
=item B<--manpage>
Die komplette Manpage anzeigen
=item B<--version>
Programmversion anzeigen
=back
=head3 Optionen für Schritt 1
=over 3
=item B<--OrderTotal>
Abzubuchender Betrag in Euro ohne Währungssymbol. Dezimalpunkt ist ein
Punkt. Kommas werden als Tausenderpunkte interpretiert. Maximal zulässig
sind 10000 US Dollar.
Da in unserem Fall keine Steuer und kein Shipping mehr dazukommen wird
dieser Betrag auch als C<MaxAmount> an PayPal übergeben, so dass er dem
Kunden auf der PayPal-Seite als endgültiger Betrag angezeigt wird. Leider
funktioniert das nicht. Der Kunde sieht auf der PayPal-Seite keinen Betrag!
=item B<--OrderDescription>
Beschreibender Text zur Zahlung, die dem Kunden auf der PayPal-Seite
angezeigt wird. Für unsere Buchhaltung sollten hier zumindest KundenNummer
und Rechnungsnummer angegeben sein. Auch der Betrag wäre hier wohl
wünschenswert, da der Kunden auf der PayPal-Seite den Betrag nicht
angezeigt bekommt! (Warum wohl?)
=item B<--InvoiceID>
Unsere (eindeutige) Rechnungs-ID.
=item B<--BuyerEmail>
PayPal beschreibt diesen Parameter so:
Email address of the buyer as entered during checkout. PayPal uses this
value to pre-fill the PayPal membership sign-up portion of the PayPal
login page.
Character length and limit: 127 single-byte alphanumeric characters
=item B<--ReturnURL>
Nach der Zahlungsbestätigung wird der Kunde zu dieser URL weitergeleitet.
=item B<--CancelURL>
In dem Fall, dass der Kunde die Zahlungsbestätigung abbricht, wird er zu
dieser URL weitergeleitet.
=item B<--PageStyle>
PayPal beschreibt diesen Parameter so:
Sets the Custom Payment Page Style for payment pages associated with this
button/link. PageStyle corresponds to the HTML variable page_style for
customizing payment pages. The value is the same as the Page Style Name
you chose when adding or editing the page style from the Profile subtab
of the My Account tab of your PayPal account. Character length and
limitations: 30 single-byte alphabetic characters.
=item B<--cpp-header-image>
PayPal beschreibt diesen Parameter so:
A URL for the image you want to appear at the top left of the payment
page. The image has a maximum size of 750 pixels wide by 90 pixels
high. PayPal recommends that you provide an image that is stored on a
secure (https) server. Character length and limitations: 127
=item B<--cpp-header-border-color>
PayPal beschreibt diesen Parameter so:
Sets the border color around the header of the payment page. The border
is a 2-pixel perimeter around the header space, which is 750 pixels wide
by 90 pixels high. Character length and limitations: Six character HTML
hexadecimal color code in ASCII
=item B<--cpp-header-back-color>
PayPal beschreibt diesen Parameter so:
Sets the background color for the header of the payment page.
Character length and limitation: Six character HTML hexadecimal color
code in ASCII
=item B<--cpp-payflow-color>
PayPal beschreibt diesen Parameter so:
Sets the background color for the payment page.
Character length and limitation: Six character HTML hexadecimal color
code in ASCII
=back
=head3 Optionen für Schritt 2
=over 3
=item B<--Token>
Zur Identifikation des Zahlungsvorgangs muss das Token aus Schritt 1 an
PayPal übergeben werden.
=back
=head3 Optionen für Schritt 3
=over 3
=item B<--OrderTotal>
Abzubuchender Betrag in Euro ohne Währungssymbol. Dezimalpunkt ist ein
Punkt. Kommas werden als Tausenderpunkte interpretiert. Maximal zulässig
sind 10000 US Dollar. Der Betrag darf den Betrag aus Schritt 1 nicht
übersteigen, aber PayPal akzeptiert trotzdem einen höheren Betrag und bucht
ihn auch brav ab! Das lädt ja direkt zum Betrug ein! Allerdings bekommt
der Kunde danach ja noch eine Bestätigung per E-Mail, in der der richtige
Betrag steht.
=item B<--Token>
Zur Identifikation des Zahlungsvorgangs muss das Token aus Schritt 1 an
PayPal übergeben werden.
=item B<--PayerID>
Zur Identifikation des Zahlungsvorgangs muss die PayerID aus Schritt 2 an
PayPal übergeben werden.
=back
=head1 EXITCODES
B<0> Alles bestens
Alles andere bedeutet nichts Gutes.
=head1 BUGS
Ich habe folgendes seltsame Verhalten festgestellt:
Wenn ein Kunde _nach_ der Bezahlung nochmal die PayPal-Seite mit der
Zahlungsaufforderung aufruft und dort dann auf "Zurück zur Kaufabwicklung
des Händlers" klickt, wird er zu dieser URL umgeleitet:
http://blafaselfoo.sonst.was/paypal/cancel?submit.x=Zur%C3%BCck+zur+Kaufabwicklung+des+H%C3%A4ndlers&form_charset=UTF-8
=head1 SEE ALSO
L<SOAP::Lite>, L<Business::PayPal::API>, L<Business::PayPal::API::ExpressCheckout>,
L<https://www.paypal.com/IntegrationCenter/ic_expresscheckout.html>,
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Dr. Andy Spiegl E<lt>paypalcheckout.Spiegl@kascada.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Andy Spiegl
This perl script is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
=cut

800
lib/Business/PayPal/API.pm Normal file
View file

@ -0,0 +1,800 @@
package Business::PayPal::API;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67; # +trace => 'all';
use Carp qw(carp);
our $VERSION = '0.62';
our $CVS_VERSION = '$Id: API.pm,v 1.24 2009/07/28 18:00:58 scott Exp $';
our $Debug = 0;
## NOTE: This package exists only until I can figure out how to use
## NOTE: SOAP::Lite's WSDL support for complex types and importing
## NOTE: type definitions, at which point this module will become much
## NOTE: smaller (or non-existent).
sub C_api_sandbox () { 'https://api.sandbox.paypal.com/2.0/' }
sub C_api_sandbox_3t () { 'https://api-3t.sandbox.paypal.com/2.0/' }
sub C_api_live () { 'https://api.paypal.com/2.0/' }
sub C_api_live_3t () { 'https://api-3t.paypal.com/2.0/' }
sub C_xmlns_pp () { 'urn:ebay:api:PayPalAPI' }
sub C_xmlns_ebay () { 'urn:ebay:apis:eBLBaseComponents' }
sub C_version () { '3.0' } ## 3.0 adds RecurringPayments
## this is an inside-out object. Make sure you 'delete' additional
## members in DESTROY() as you add them.
my %Soap;
my %Header;
my %H_PKCS12File; ## path to certificate file (pkc12)
my %H_PKCS12Password; ## password for certificate file (pkc12)
my %H_CertFile; ## PEM certificate
my %H_KeyFile; ## PEM private key
sub import {
my $self = shift;
my @modules = @_;
for my $module ( @modules ) {
eval( "use Business::PayPal::API::$module;" );
if( $@ ) {
warn $@;
next;
}
## import 'exported' subroutines into our namespace
no strict 'refs';
for my $sub ( @{"Business::PayPal::API::" . $module . "::EXPORT_OK"} ) {
*{"Business::PayPal::API::" . $sub} = *{"Business::PayPal::API::" . $module . "::" . $sub};
}
}
}
sub new {
my $class = shift;
my %args = @_;
my $self = bless \(my $fake), $class;
## if you add new args, be sure to update the test file's @variables array
$args{Username} ||= '';
$args{Password} ||= '';
$args{Signature} ||= '';
$args{Subject} ||= '';
$args{sandbox} = 1 unless exists $args{sandbox};
$args{timeout} ||= 0;
$H_PKCS12File{$self} = $args{PKCS12File} || '';
$H_PKCS12Password{$self} = $args{PKCS12Password} || '';
$H_CertFile{$self} = $args{CertFile} || '';
$H_KeyFile{$self} = $args{KeyFile} || '';
my $proxy = ($args{sandbox}
? ($args{Signature}
? C_api_sandbox_3t
: C_api_sandbox)
: ($args{Signature}
? C_api_live_3t
: C_api_live)
);
$Soap{$self} = SOAP::Lite->proxy( $proxy, timeout => $args{timeout} )->uri( C_xmlns_pp );
$Header{$self} = SOAP::Header
->name( RequesterCredentials => \SOAP::Header->value
( SOAP::Data->name( Credentials => \SOAP::Data->value
( SOAP::Data->name( Username => $args{Username} )->type(''),
SOAP::Data->name( Password => $args{Password} )->type(''),
SOAP::Data->name( Signature => $args{Signature} )->type(''),
SOAP::Data->name( Subject => $args{Subject} )->type(''),
),
)->attr( {xmlns => C_xmlns_ebay} )
)
)->attr( {xmlns => C_xmlns_pp} )->mustUnderstand(1);
return $self;
}
sub DESTROY {
my $self = $_[0];
delete $Soap{$self};
delete $Header{$self};
delete $H_PKCS12File{$self};
delete $H_PKCS12Password{$self};
delete $H_CertFile{$self};
delete $H_KeyFile{$self};
my $super = $self->can("SUPER::DESTROY");
goto &$super if $super;
}
sub version_req {
return SOAP::Data->name( Version => C_version )
->type('xs:string')->attr( {xmlns => C_xmlns_ebay} );
}
sub doCall {
my $self = shift;
my $method_name = shift;
my $request = shift;
my $method = SOAP::Data->name( $method_name )->attr( {xmlns => C_xmlns_pp} );
my $som;
{
$H_PKCS12File{$self} and local $ENV{HTTPS_PKCS12_FILE} = $H_PKCS12File{$self};
$H_PKCS12Password{$self} and local $ENV{HTTPS_PKCS12_PASSWORD} = $H_PKCS12Password{$self};
$H_CertFile{$self} and local $ENV{HTTPS_CERT_FILE} = $H_CertFile{$self};
$H_KeyFile{$self} and local $ENV{HTTPS_KEY_FILE} = $H_KeyFile{$self};
if( $Debug ) {
print STDERR SOAP::Serializer->envelope(method => $method,
$Header{$self}, $request), "\n";
}
# $Soap{$self}->readable( $Debug );
# $Soap{$self}->outputxml( $Debug );
no warnings 'redefine';
local *SOAP::Deserializer::typecast = sub {shift; return shift};
eval {
$som = $Soap{$self}->call( $Header{$self}, $method => $request );
};
if( $@ ) {
carp $@;
return;
}
}
if( $Debug ) {
## FIXME: would be nicer to dump a SOM to XML, but how to do that?
require Data::Dumper;
print STDERR Data::Dumper::Dumper($som->envelope);
}
if( ref($som) && $som->fault ) {
carp "Fault: " . $som->faultstring
. ( $som->faultdetail ? " (" . $som->faultdetail . ")" : '' )
. "\n";
return;
}
return $som;
}
sub getFieldsList {
my $self = shift;
my $som = shift;
my $path = shift;
my $fields = shift;
return unless $som;
my %trans_id = ();
my @records = ();
for my $rec ( $som->valueof($path) ) {
my %response = ();
@response{keys %$fields} = @{$rec}{keys %$fields};
## avoid duplicates
next if $trans_id{$response{TransactionID}};
$trans_id{$response{TransactionID}} = 1;
push @records, \%response;
}
return \@records;
}
sub getFields {
my $self = shift;
my $som = shift;
my $path = shift;
my $response = shift;
my $fields = shift;
return unless $som;
## kudos to Erik Aronesty via email, Drew Simpson via rt.cpan.org (#28596)
## Erik wrote:
## <snip>
## If you want me to write the code for the "flagged" version, i
## can .. i think the '/@' flag is a pretty safe, and obvious flag.
##
## the advantage of the flagged version would be that the caller
## doesn't have to check the returned value ... in the case of a
## field where multiple values are expected.
## </snip>
##
## I agree with this on principle and would prefer it, but I voted
## against a special flag, now forcing the caller to check the
## return value, but only for the sake of keeping everything
## consistent with the rest of the API. If Danny Hembree wants to
## go through and implement Erik's suggestion, I'd be in favor of
## it.
for my $field ( keys %$fields ) {
my @vals = grep { defined } $som->valueof("$path/$fields->{$field}");
next unless @vals;
if( scalar(@vals) == 1 ) {
$response->{$field} = $vals[0];
}
else {
$response->{$field} = \@vals;
}
}
}
sub getBasic {
my $self = shift;
my $som = shift;
my $path = shift;
my $details = shift;
return unless $som;
for my $field qw( Ack Timestamp CorrelationID Version Build ) {
$details->{$field} = $som->valueof("$path/$field") || '';
}
return $details->{Ack} eq 'Success';
}
sub getErrors {
my $self = shift;
my $som = shift;
my $path = shift;
my $details = shift;
return unless $som;
my @errors = ();
for my $enode ( $som->valueof("$path/Errors") ) {
push @errors, { LongMessage => $enode->{LongMessage},
ErrorCode => $enode->{ErrorCode}, };
}
$details->{Errors} = \@errors;
return;
}
1;
__END__
=head1 NAME
Business::PayPal::API - PayPal API
=head1 SYNOPSIS
use Business::PayPal::API qw( ExpressCheckout GetTransactionDetails );
## certificate authentication
my $pp = new Business::PayPal::API
( Username => 'my_api1.domain.tld',
Password => 'this_is_my_password',
PKCS12File => '/path/to/cert.pkcs12',
PKCS12Password => '(pkcs12 password)',
sandbox => 1 );
## PEM cert authentication
my $pp = new Business::PayPal::API
( Username => 'my_api1.domain.tld',
Password => 'this_is_my_password',
CertFile => '/path/to/cert.pem',
KeyFile => '/path/to/cert.pem',
sandbox => 1 );
## 3-token (Signature) authentication
my $pp = new Business::PayPal::API
( Username => 'my_api1.domain.tld',
Password => 'Xdkis9k3jDFk39fj29sD9', ## supplied by PayPal
Signature => 'f7d03YCpEjIF3s9Dk23F2V1C1vbYYR3ALqc7jm0UrCcYm-3ksdiDwjfSeii', ## ditto
sandbox => 1 );
my %response = $pp->SetExpressCheckout( ... );
=head1 DESCRIPTION
B<Business::PayPal::API> supports both certificate authentication and
the new 3-token "Signature" authentication.
It also support PayPal's development I<sandbox> for testing. See the
B<sandbox> parameter to B<new()> below for details.
B<Business::PayPal::API> can import other B<API> derived classes:
use Business::PayPal::API qw( RefundTransaction );
This allows for much more concise and intuitive usage. For example,
these two statements are equivalent:
use Business::PayPal::API::RefundTransaction;
my $pp = new Business::PayPal::API::RefundTransaction( ... );
$pp->RefundTransaction( ... );
and more concisely:
use Business::PayPal::API qw( RefundTransaction );
my $pp = new Business::PayPal::API( ... );
$pp->RefundTransaction( ... );
The advantage of this becomes clear when you need to use multiple API
calls in your program; this allows you to use the same object to
invoke the various methods, instead of creating a new object for each
subclass. Here is an example of a B<API> object used to invoke various
PayPal APIs with the same object:
use Business::PayPal::API qw( GetTransactionDetails
TransactionSearch
RefundTransaction );
my $pp = new Business::PayPal::API( ... );
my $records = $pp->TransactionSearch( ... );
my %details = $pp->GetTransactionDetails( ... );
my %resp = $pp->RefundTransaction( ... );
However, you may certainly use just the subclass if that's all you
need. Every subclass should work as its own self-contained API.
For details on B<Business::PayPal::API::*> subclasses, see each
subclass's individual documentation.
=head2 new
Creates a new B<Business::PayPal::API> object.
A note about certificate authentication: PayPal (and this module)
support either PKCS#12 certificate authentication or PEM certificate
authentication. See options below.
=over 4
=item B<Username>
Required. This is the PayPal API username, usually in the form of
'my_api1.mydomain.tld'. You can find or create your API credentials by
logging into PayPal (if you want to do testing, as you should, you
should also create a developer sandbox account) and going to:
My Account -> Profile -> API Access -> Request API Credentials
Please see the I<PayPal API Reference> and I<PayPal Sandbox User
Guide> for details on creating a PayPal business account and sandbox
account for testing.
=item B<Password>
Required. If you use certificate authentication, this is the PayPal
API password created when you setup your certificate. If you use
3-token (Signature) authentication, this is the password PayPal
assigned you, along with the "API User Name" and "Signature Hash".
=item B<Subject>
Optional. This is used by PayPal to authenticate 3rd party billers
using your account. See the documents in L<SEE ALSO>.
=item B<Signature>
Required for 3-token (Signature) authentication. This is the
"Signature Hash" you received when you did "Request API Credentials"
in your PayPal Business Account.
=item B<PKCS12File>
Required for PKCS#12 certificate authentication, unless the
B<HTTPS_PKCS12_FILE> environment variable is already set.
This contains the path to your private key for PayPal
authentication. It is used to set the B<HTTPS_PKCS12_FILE> environment
variable. You may set this environment variable yourself and leave
this field blank.
=item B<PKCS12Password>
Required for PKCS#12 certificate authentication, unless the
B<HTTPS_PKCS12_PASSWORD> environment variable is already set.
This contains the PKCS#12 password for the key specified in
B<PKCS12File>. It is used to set the B<HTTPS_PKCS12_PASSWORD>
environment variable. You may set this environment variable yourself
and leave this field blank.
=item B<CertFile>
Required for PEM certificate authentication, unless the
HTTPS_CERT_FILE environment variable is already set.
This contains the path to your PEM format certificate given to you
from PayPal (and accessible in the same location that your Username
and Password and/or Signature Hash are found) and is used to set the
B<HTTPS_CERT_FILE> environment variable. You may set this environment
variable yourself and leave this field blank.
You may combine both certificate and private key into one file and set
B<CertFile> and B<KeyFile> to the same path.
=item B<KeyFile>
Required for PEM certificate authentication, unless the HTTPS_KEY_FILE
environment variable is already set.
This contains the path to your PEM format private key given to you
from PayPal (and accessible in the same location that your Username
and Password and/or Signature Hash are found) and is used to set the
B<HTTPS_KEY_FILE> environment variable. You may set this environment
variable yourself and leave this field blank.
You may combine both certificate and private key into one file and set
B<CertFile> and B<KeyFile> to the same path.
=item B<sandbox>
Required. If set to true (default), B<Business::PayPal::API> will
connect to PayPal's development sandbox, instead of PayPal's live
site. *You must explicitly set this to false (0) to access PayPal's
live site*.
If you use PayPal's development sandbox for testing, you must have
already signed up as a PayPal developer and created a Business sandbox
account and a Buyer sandbox account (and make sure both of them have
B<Verified> status in the sandbox).
When testing with the sandbox, you will use different usernames,
passwords, and certificates (if using certificate authentication) than
you will when accessing PayPal's live site. Please see the PayPal
documentation for details. See L<SEE ALSO> for references.
PayPal's sandbox reference:
L<https://www.paypal.com/IntegrationCenter/ic_sandbox.html>
=back
=head1 ERROR HANDLING
Every API call should return an B<Ack> response, whether I<Success>,
I<Failure>, or otherwise (depending on the API call). If it returns
any non-success value, you can find an I<Errors> entry in your return
hash, whose value is a listref of hashrefs:
[ { ErrorCode => 10002,
LongMessage => "Invalid security header" },
{ ErrorCode => 10030,
LongMessage => "Some other error" }, ]
You can retrieve these errors like this:
%response = $pp->doSomeAPICall();
if( $response{Ack} ne 'Success' ) {
for my $err ( @{$response{Errors}} ) {
warn "Error: " . $err->{LongMessage} . "\n";
}
}
=head1 TESTING
Testing the B<Business::PayPal::API::*> modules requires that you
create a file containing your PayPal Developer Sandbox authentication
credentials (e.g., API certificate authentication or 3-Token
authentication signature, etc.) and setting the B<WPP_TEST>
environment variable to point to this file.
The format for this file is as follows:
Username = your_api.username.com
Password = your_api_password
and then ONE of the following options:
a) supply 3-token authentication signature
Signature = xxxxxxxxxxxxxxxxxxxxxxxx
b) supply PEM certificate credentials
CertFile = /path/to/cert_key_pem.txt
KeyFile = /path/to/cert_key_pem.txt
c) supply PKCS#12 certificate credentials
PKCS12File = /path/to/cert.p12
PKCS12Password = pkcs12_password
You may also set the appropriate HTTPS_* environment variables for b)
and c) above (e.g., HTTPS_CERT_FILE, HTTPS_KEY_FILE,
HTTPS_PKCS12_File, HTTPS_PKCS12_PASSWORD) in lieu of putting this
information in a file.
Then use "WPP_TEST=my_auth.txt make test" (for Bourne shell derivates) or
"setenv WPP_TEST my_auth.txt && make test" (for C-shell derivates).
See 'auth.sample.*' files in this package for an example of the file
format. Variables are case-*sensitive*.
Any of the following variables are recognized:
Username Password Signature Subject
CertFile KeyFile PKCS12File PKCS12Password
BuyerEmail
Note: PayPal authentication may I<fail> if you set the certificate
environment variables and attempt to connect using 3-token
authentication (i.e., PayPal will use the first authentication
credentials presented to it, and if they fail, the connection is
aborted).
=head1 TROUBLESHOOTING
=head2 PayPal Authentication Errors
If you are experiencing PayPal authentication errors (e.g., "Security
header is not valid", "SSL negotiation failed", etc.), you should make
sure:
* your username and password match those found in your PayPal
Business account sandbox (this is not the same as your regular
account.
* you're not trying to use your live username and password for
sandbox testing and vice versa.
* if the sandbox works but "live" does not, make sure you've turned
off the 'sandbox' parameter correctly. Otherwise you'll be
passing your PayPal sandbox credentials to PayPal's live site
(which won't work).
* if you use certificate authentication, your certificate must be
the correct one (live or sandbox) depending on what you're doing.
* if you use 3-Token authentication (i.e., Signature), you don't
have any B<PKCS12*> parameters or B<CertFile> or B<KeyFile>
parameters in your constructor AND that none of the corresponding
B<HTTPS_*> environment variables are set. PayPal prefers
certificate authentication since it occurs at connection time; if
it fails, it will not try Signature authentication.
Try clearing your environment:
## delete all HTTPS, SSL env
delete $ENV{$_} for grep { /^(HTTPS|SSL)/ } keys %ENV;
## now put our own HTTPS env back in
$ENV{HTTPS_CERT_FILE} = '/var/path/to/cert.pem';
## create our paypal object
my $pp = new Business::PayPal::API...
* if you have already loaded Net::SSLeay (or IO::Socket::SSL), then
Net::HTTPS will prefer to use IO::Socket::SSL. I don't know how
to get SOAP::Lite to work with IO::Socket::SSL (e.g.,
Crypt::SSLeay uses HTTPS_* environment variables), so until then,
you can use this hack:
local $IO::Socket::SSL::VERSION = undef;
$pp->DoExpressCheckoutPayment(...);
This will tell Net::HTTPS to ignore the fact that IO::Socket::SSL
is already loaded for this scope and import Net::SSL (part of the
Crypt::SSLeay package) for its 'configure()' method.
* if you receive a message like "500 Can't connect to
api.sandbox.paypal.com:443 (Illegal seek)", you'll need to make
sure you have Crypt::SSLeay installed. It seems that other crypto
modules don't do the certificate authentication quite as well,
and LWP needs this to negotiate the SSL connection with PayPal.
See the DEBUGGING section below for further hints.
=head2 PayPal Munging URLs
PayPal seems to be munging my URLs when it returns.
SOAP::Lite follows the XML specification carefully, and encodes '&'
and '<' characters before applying them to the SOAP document. PayPal
does not properly URL-decode HTML entities '&amp;' and '&lt;' on the
way back, so if you have an ampersand in your ReturnURL (for example),
your customers will be redirected here:
http://domain.tld/prog?arg1=foo&amp;arg2=bar
instead of here:
http://domain.tld/prog?arg1=foo&arg2=bar
Solution:
Use CDATA tags to wrap your request:
ReturnURL => '<![CDATA[http://domain.tld/prog?arg1=foo&arg2=bar]]>'
You may also use semicolons instead of ampersands to separate your URL
arguments:
ReturnURL => 'http://domain.tld/prog?arg1=foo;arg2=bar'
(thanks to Ollie Ready)
=head1 DEBUGGING
You can see the raw SOAP XML sent and received by
B<Business::PayPal::API> by setting it's B<$Debug> variable:
$Business::PayPal::API::Debug = 1;
$pp->SetExpressCheckout( %args );
this will print the XML being sent, and dump a Perl data structure of
the SOM received on STDERR (so check your error_log if running inside
a web server).
If anyone knows how to turn a SOAP::SOM object into XML without
setting B<outputxml()>, let me know.
=head1 DEVELOPMENT
If you are a developer wanting to extend B<Business::PayPal::API> for
other PayPal API calls, you can review any of the included modules
(e.g., F<RefundTransaction.pm> or F<ExpressCheckout.pm>) for examples
on how to do this until I have more time to write a more complete
document.
But in a nutshell:
package Business::PayPal::API::SomeAPI;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our @EXPORT_OK = qw( SomeAPIMethod );
sub SomeAPIMethod {
...
}
Notice the B<@EXPORT_OK> variable. This is I<not> used by B<Exporter>
(we don't load Exporter at all): it is a special variable used by
B<Business::PayPal::API> to know which methods to import when
B<Business::PayPal::API> is run like this:
use Business::PayPal::API qw( SomeAPI );
That is, B<Business::PayPal::API> will import any subroutine into its
own namespace from the B<@EXPORT_OK> array. Now it can be used like this:
use Business::PayPal::API qw( SomeAPI );
my $pp = new Business::PayPal::API( ... );
$pp->SomeAPIMethod( ... );
Of course, we also do a 'use Business::PayPal::API' in the module so
that it can be used as a standalone module, if necessary:
use Business::PayPal::API::SomeAPI;
my $pp = new Business::PayPal::API::SomeAPI( ... ); ## same args as superclass
$pp->SomeAPIMethod( ... );
Adding the B<@EXPORT_OK> array in your module allows your module to be
used in the most convenient way for the given circumstances.
=head1 EXAMPLES
Andy Spiegl <paypalcheckout.Spiegl@kascada.com> has kindly donated
some example code (in German) for the ExpressCheckout API which may be
found in the F<eg> directory of this archive. Additional code examples
for other APIs may be found in the F<t> test directory.
=head1 EXPORT
None by default.
=head1 CAVEATS
Because I haven't figured out how to make SOAP::Lite read the WSDL
definitions directly and simply implement those (help, anyone?), I
have essentially recreated all of those WSDL structures internally in
this module.
(Note - 6 Oct 2006: SOAP::Lite's WSDL support is moving ahead, but
slowly. The methods used by this API are considered "best practice"
and are safe to use).
As with all web services, if PayPal stop supporting their API
endpoint, this module *may stop working*. You can help me keep this
module up-to-date if you notice such an event occuring.
Also, I didn't implement a big fat class hierarchy to make this module
"academically" correct. You'll notice that I fudged colliding
parameter names in B<DoExpressCheckoutPayment> and similar fudging may
be found in B<GetTransactionDetails>. The good news is that this was
written quickly, works, and is dead-simple to use. The bad news is
that this sort of collision might occur again as more and more data is
sent in the API (call it 'eBay API bloat'). I'm willing to take the
risk this will be rare (PayPal--please make it rare!).
=head1 ACKNOWLEDGEMENTS
Wherein I acknowledge all the good folks who have contributed to this
module in some way:
=over 4
=item * Daniel P. Hembree
for authoring the AuthorizationRequest, CaptureRequest,
DirectPayments, ReauthorizationRequest, and VoidRequest extensions.
Danny's contact information may be found in the AUTHOR section of the
above modules.
=item * <jshiles at base16consulting daught com>
for finding some API typos in the ExpressCheckout API
=item * Andy Spiegl <paypalcheckout.Spiegl@kascada.com>
for giving me the heads-up on PayPal's new 3-token auth URI and for a
sample command-line program (found in the 'eg' directory)
demonstrating the ExpressCheckout API.
=item * Ollie Ready <oready at drjays daught com>
for the heads-up on the newest 3-token auth URI as well as a pile of
documentation inconsistencies.
=item * Michael Hendricks <michael at ndrix daught org>
for a patch that adds ShippingTotal to the DirectPayments module.
=item * Erik Aronesty, Drew Simpson via rt.cpan.org (#28596)
for a patch to fix getFields() when multiple items are returned
=item * Sebastian Böhm via email, SDC via rt.cpan.org (#38915)
for a heads-up that the PayPal documentation for MassPay API was wrong
regarding the I<UniqueId> parameter.
=item * Jonathon Wright via email
for patches for B<ExpressCheckout> and B<RecurringPayments> that
implement I<BillingAgreement> and I<DoReferenceTransaction> API
calls.
=back
=head1 SEE ALSO
L<SOAP::Lite>, L<https://www.paypal.com/IntegrationCenter/ic_pro_home.html>,
L<https://www.paypal.com/IntegrationCenter/ic_expresscheckout.html>,
L<https://www.sandbox.paypal.com/en_US/pdf/PP_Sandbox_UserGuide.pdf>,
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scott Wiersdorf, E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006, 2007 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,144 @@
package Business::PayPal::API::AuthorizationRequest;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
#use SOAP::Lite +trace => 'debug';
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.11';
our $CVS_VERSION = '$Id: AuthorizationRequest.pm,v 1.1 2006/10/06 17:49:51 scott Exp $';
our @EXPORT_OK = qw(DoAuthorizationRequest);
sub DoAuthorizationRequest {
my $self = shift;
my %args = @_;
my %types = ( TransactionID => 'xs:string',
Amount => 'ebl:BasicAmountType',);
$args{currencyID} ||= 'USD';
my @ref_trans =
($self->version_req,
SOAP::Data->name( TransactionID => $args{TransactionID} )->type($types{TransactionID}),);
push @ref_trans,
SOAP::Data->name( Amount => $args{Amount} )
->type( $types{Amount} )
->attr( { currencyID => $args{currencyID} } );
my $request = SOAP::Data->name
( DoAuthorizationRequest => \SOAP::Data->value( @ref_trans ) )
->type("ns:AuthorizationRequestType");
my $som = $self->doCall( DoAuthorizationReq => $request )
or return;
my $path = '/Envelope/Body/DoAuthorizationResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response,
{ TransactionID => 'TransactionID',
Amount => 'Amount', }
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::AuthorizationRequest - PayPal AuthorizationRequest API
=head1 SYNOPSIS
use Business::PayPal::API::AuthorizationRequest;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::DoAuthorizationRequest ( ... );
my %response = $pp->DoAuthorizationRequest (
TransactionID => $transid,
CurrencyID => $currencyID,
Amount => $amout,
);
=head1 DESCRIPTION
B<Business::PayPal::API::AuthorizationRequest> implements PayPal's
B<AuthorizationRequest> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox. This request is only used with "Order" type
Authorizations. An "Order" must first be placed using the ExpressCheckout
module. DirectPayment authorizations can only be used for "Basic"
authorizations.
=head2 AuthorizationRequest
Implements PayPal's B<AuthorizationRequest> API call. Supported
parameters include:
TransactionID
Amount
currencyID (defaults to 'USD' if not supplied)
as described in the PayPal "Web Services API Reference" document. The
default B<currencyID> setting is 'USD' if not otherwise specified.
Returns a hash containing the results of the transaction.
Example:
my %resp = $pp->DoAuthorizationRequest (
TransactionID => $trans_id,
Amount => '15.00',
);
unless( $resp{Ack} ne 'Success' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Danny Hembree E<lt>danny@dynamical.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Danny Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,170 @@
package Business::PayPal::API::CaptureRequest;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
#use SOAP::Lite +trace => 'debug';
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.11';
our $CVS_VERSION = '$Id: CaptureRequest.pm,v 1.2 2007/10/16 19:09:26 scott Exp $';
our @EXPORT_OK = qw(DoCaptureRequest);
sub DoCaptureRequest {
my $self = shift;
my %args = @_;
my %types = (
AuthorizationID => 'xs:string',
#The inclusion of the "ebl:CompleteCodeType" here, or any other reasonable type,
#causes and error. Assigning a null string allows the module to work normally
#with the exception that testing for "Success" fails, one must test for not
#being a "Failure"... there may be a life lesson here.
CompleteType => '',
Amount => 'ebl:BasicAmountType',
Note => 'xs:string',
);
$args{currencyID} ||= 'USD';
$args{CompleteType} ||= 'Complete';
my @ref_trans =
(
$self->version_req,
SOAP::Data->name( AuthorizationID => $args{AuthorizationID} )->type($types{AuthorizationID}),
SOAP::Data->name( CompleteType => $args{CompleteType} )->type($types{CompleteType}),
);
if( $args{Amount} ) {
push @ref_trans,
SOAP::Data->name( Amount => $args{Amount} )
->type( $types{Amount} )
->attr( { currencyID => $args{currencyID} } )
}
my $request = SOAP::Data->name
( DoCaptureRequest => \SOAP::Data->value( @ref_trans ) )
->type("ns:DoCaptureRequestType");
my $som = $self->doCall( DoCaptureReq => $request )
or return;
my $path = '/Envelope/Body/DoCaptureResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$path .= '/DoCaptureResponseDetails/PaymentInfo';
$self->getFields($som, $path, \%response,
{
TransactionID => 'TransactionID',
ParentTransactionID => 'ParentTransactionID',
ReceiptID => 'ReceiptID',
TransactionType => 'TransactionType',
PaymentType => 'PaymentType',
PaymentDate => 'PaymentDate',
GrossAmount => 'GrossAmount',
FeeAmount => 'FeeAmount',
SettleAmount => 'SettleAmount',
TaxAmount => 'TaxAmount',
ExchangeRate => 'ExchangeRate',
PaymentStatus => 'PaymentStatus',
PendingReason => 'PendingReason',
ReasonCode => 'ReasonCode',
}
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::CaptureRequest - PayPal CaptureRequest API
=head1 SYNOPSIS
use Business::PayPal::API::CaptureRequest;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::DoCaptureRequest ( ... );
my %response = $pp->DoCaptureRequest( AuthorizationID => $transid,
CompleteType => 'Complete',
Amount => '13.00',
Note => "Give the fiddler his due." );
=head1 DESCRIPTION
B<Business::PayPal::API::DoCaptureRequest> implements PayPal's
B<CaptureRequest> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 CaptureRequest
Implements PayPal's B<CaptureRequest> API call. Supported
parameters include:
AuthorizationID
CompleteType (defaults to 'Complete' unless set to 'NotComplete')
Amount
currencyID (Currently must be the default, 'USD')
Note ("String, < 255 char, indicating information about the charges.")
as described in the PayPal "Web Services API Reference" document. The
default B<currencyID> setting is 'USD' if not otherwise specified. The
default B<CompleteType> setting is 'Complete' if not otherwise specified.
Returns a hash containing the results of the transaction.
Example:
my %resp = $pp->DoCaptureRequest (
AuthorizationID => $auth_id,
CompleteType => 'NotComplete',
Amount => '15.00',
CurrencyID => 'USD',
);
if( $resp{Ack} eq 'Failure' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Danny Hembree E<lt>danny-hembree@dynamical.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Danny Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,293 @@
package Business::PayPal::API::DirectPayments;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite;
#use SOAP::Lite +trace => 'debug';
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.03';
our $CVS_VERSION = '$Id: DirectPayments.pm,v 1.5 2009/07/28 18:00:59 scott Exp $';
our @EXPORT_OK = qw(DoDirectPaymentRequest);
sub DoDirectPaymentRequest {
my $self = shift;
my %args = @_;
my %types =( PaymentAction => '',
# Payment Detail
OrderTotal => 'xs:string',
ItemTotal => 'xsd:string',
ShippingTotal => 'xsd:string',
TaxTotal => 'xsd:string',
InvoiceID => 'xsd:string',
ButtonSource => 'xsd:string',
# Credit Card
CreditCardType => '',
CreditCardNumber => 'xsd:string',
ExpMonth => 'xs:int',
ExpYear => 'xs:int',
# CardOwner
Payer => 'ns:EmailAddressType',
# Payer Name
FirstName => 'xs:string',
LastName => 'xs:string',
# Payer Address
Street1 => 'xs:string',
Street2 => 'xs:string',
CityName => 'xs:string',
StateOrProvince => 'xs:string',
Country => 'xs:string',
PostalCode => 'xs:string',
# Shipping Address
ShipToName => 'xs:string',
ShipToStreet1 => 'xs:string',
ShipToStreet2 => 'xs:string',
ShipToCityName => 'xs:string',
ShipToStateOrProvince => 'xs:string',
ShipToCountry => 'xs:string',
ShipToPostalCode => 'xs:string',
# Misc
CVV2 => 'xs:string',
IPAddress => 'xs:string',
MerchantSessionId => 'xs:string',
);
$args{currencyID} ||= 'USD';
$args{PaymentAction} ||= 'Sale';
#Assemble Credit Card Information
my @payername = ( SOAP::Data->name(FirstName => $args{FirstName}),
SOAP::Data->name(LastName => $args{LastName}),
);
my @payeraddr = ( SOAP::Data->name(Street1 => $args{Street1} )->type($types{Street1}),
SOAP::Data->name(Street2 => $args{Street2} )->type($types{Street2}),
SOAP::Data->name(CityName => $args{CityName} )->type($types{CityName}),
SOAP::Data->name(StateOrProvince => $args{StateOrProvince} )->type($types{StateOrProvince}),
SOAP::Data->name(Country => $args{Country} )->type($types{Country}),
SOAP::Data->name(PostalCode => $args{PostalCode} )->type($types{PostalCode}),
);
my @shipaddr = ( SOAP::Data->name(Name => $args{ShipToName})->type($types{ShipToName}),
SOAP::Data->name(Street1 => $args{ShipToStreet1} )->type($types{ShipToStreet1}),
SOAP::Data->name(Street2 => $args{ShipToStreet2} )->type($types{ShipToStreet2}),
SOAP::Data->name(CityName => $args{ShipToCityName} )->type($types{ShipToCityName}),
SOAP::Data->name(StateOrProvince => $args{ShipToStateOrProvince} )->type($types{ShipToStateOrProvince}),
SOAP::Data->name(Country => $args{ShipToCountry} )->type($types{ShipToCountry}),
SOAP::Data->name(PostalCode => $args{ShipToPostalCode} )->type($types{ShipToPostalCode}),
);
my @ccard = ( SOAP::Data->name(CreditCardType => $args{CreditCardType})->type($types{CreditCardType}),
SOAP::Data->name(CreditCardNumber => $args{CreditCardNumber})->type($types{CreditCardNumber}),
SOAP::Data->name(ExpMonth => $args{ExpMonth})->type($types{ExpMonth}),
SOAP::Data->name(ExpYear => $args{ExpYear})->type($types{ExpYear}),
);
my @ccowner = ( SOAP::Data->name
(CardOwner => \SOAP::Data->value
( SOAP::Data->name(Payer => $args{Payer})->type($types{Payer}),
SOAP::Data->name(PayerName => \SOAP::Data->value ( @payername )),
SOAP::Data->name(Address => \SOAP::Data->value( @payeraddr )),
)
)
);
push( @ccard, @ccowner);
push( @ccard, SOAP::Data->name(CVV2 => $args{CVV2})->type($types{CVV2}));
#Assemble Payment Details
my @paydetail = ( SOAP::Data->name(OrderTotal => $args{OrderTotal})
->attr({currencyID=>$args{currencyID}})->type($types{currencyID}),
SOAP::Data->name(ItemTotal => $args{ItemTotal})
->attr({currencyID => $args{currencyID}})->type($types{currencyID}),
SOAP::Data->name(TaxTotal => $args{TaxTotal})
->attr({currencyID => $args{currencyID}})->type($types{currencyID}),
SOAP::Data->name(ShippingTotal => $args{ShippingTotal})
->attr({currencyID => $args{currencyID}})->type($types{currencyID}),
SOAP::Data->name(ShipToAddress => \SOAP::Data->value( @shipaddr)),
SOAP::Data->name(InvoiceID => $args{InvoiceID})->type($types{InvoiceID}),
SOAP::Data->name(ButtonSource => $args{ButtonSource})->type($types{ButtonSource})
);
my @payreqdetail = ( SOAP::Data->name(PaymentAction => $args{PaymentAction})->type(''),
SOAP::Data->name(PaymentDetails =>\SOAP::Data->value( @paydetail )),
SOAP::Data->name(CreditCard => \SOAP::Data->value( @ccard)),
SOAP::Data->name(IPAddress => $args{IPAddress})->type($types{IPAddress}),
SOAP::Data->name(MerchantSessionId => $args{MerchantSessionId})->type($types{MerchantSessionId}),
);
#Assemble request
my @reqval = ( SOAP::Data->value($self->version_req),
SOAP::Data->name( DoDirectPaymentRequestDetails => \SOAP::Data->value( @payreqdetail ))->attr({xmlns=>"urn:ebay:apis:eBLBaseComponents"}),
);
my $request = (SOAP::Data->name(DoDirectPaymentRequest => \SOAP::Data->value(@reqval)), );
my $som = $self->doCall( DoDirectPaymentReq => $request ) or return;
my $path = '/Envelope/Body/DoDirectPaymentResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields( $som, $path, \%response, { TransactionID => 'TransactionID',
Amount => 'Amount',
AVSCode => 'AVSCode',
CVV2Code => 'CVV2Code',
Timestamp => 'Timestamp',
}
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::DirectPayments - PayPal DirectPayments API
=head1 SYNOPSIS
use Business::PayPal::API qw(DirectPayments);
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API(
Username => 'name_api1.example.org',
Password => 'somepass',
CertFile => '/path/to/tester1.cert_key_pem.txt',
KeyFile => '/path/to/tester1.cert_key_pem.txt',
sandbox => 1,
);
my %response = $pp->DoDirectPaymentRequest (
PaymentAction => 'Sale',
OrderTotal => 13.59,
TaxTotal => 0.0,
ShippingTotal => 0.0,
ItemTotal => 0.0,
HandlingTotal => 0.0,
InvoiceID => 'your-tracking-number',
CreditCardType => 'Visa',
CreditCardNumber => '4561435600988217',
ExpMonth => '01',
ExpYear => '2007',
CVV2 => '123',
FirstName => 'James',
LastName => 'PuffDaddy',
Street1 => '1st Street LaCausa',
Street2 => '',
CityName => 'La',
StateOrProvince => 'Ca',
PostalCode => '90210',
Country => 'US',
Payer => 'Joe@Example.org',
ShipToName => 'Jane Doe',
ShipToStreet1 => '1234 S. Pleasant St.',
ShipToStreet2 => 'Suite #992',
ShipToCityName => 'Vacation Town',
ShipToStateOrProvince => 'FL',
ShipToCountry => 'US',
ShipToPostalCode => '12345',
CurrencyID => 'USD',
IPAddress => '10.0.0.1',
MerchantSessionID => '10113301',
);
=head1 DESCRIPTION
B<Business::PayPal::API::DirectPayments> implements PayPal's
B<DirectPayments> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 DoDirectPaymentRequest
Implements PayPal's B<DoDirectPaymentRequest> API call. Supported
parameters include:
PaymentAction ( Sale|Authorize, Sale is default )
OrderTotal
TaxTotal
ShippingTotal
ItemTotal
HandlingTotal
InvoiceID
CreditCardType
CreditCardNumber
ExpMonth ( two digits, leading zero )
ExpYear ( four digits, 20XX )
CVV2
FirstName
LastName
Street1
Street2
CityName
StateOrProvince
PostalCode
Country
Payer
ShipToName
ShipToStreet1
ShipToStreet2
ShipToCityName
ShipToStateOrProvince
ShipToCountry
ShipToPostalCode
CurrencyID (USD is default)
IPAddress
MerchantSessionID
as described in the PayPal "Web Services API Reference" document.
Returns a hash containing the results of the transaction. The B<Ack>
element and TransactionID are the most useful return values.
Example:
my %resp = $pp->DoDirectPaymentRequest(
PaymentAction => 'Sale',
OrderTotal => '10.99',
...
);
unless( $resp{Ack} ne 'Success' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Daniel Hembree E<lt>danny@dynamical.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Daniel P. Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,553 @@
package Business::PayPal::API::ExpressCheckout;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.14';
our $CVS_VERSION = '$Id: ExpressCheckout.pm,v 1.13 2009/07/28 18:00:59 scott Exp $';
our @EXPORT_OK = qw( SetExpressCheckout GetExpressCheckoutDetails DoExpressCheckoutPayment );
## if you specify an InvoiceID, PayPal seems to remember it and not
## allow you to bill twice with it.
sub SetExpressCheckout {
my $self = shift;
my %args = @_;
my %types = ( Token => 'ebl:ExpressCheckoutTokenType',
OrderTotal => 'cc:BasicAmountType',
currencyID => '',
MaxAmount => 'cc:BasicAmountType',
OrderDescription => 'xs:string',
Custom => 'xs:string',
InvoiceID => 'xs:string',
ReturnURL => 'xs:string',
CancelURL => 'xs:string',
Address => 'ebl:AddressType',
ReqConfirmShipping => 'xs:string',
NoShipping => 'xs:string',
AddressOverride => 'xs:string',
LocaleCode => 'xs:string',
PageStyle => 'xs:string',
'cpp-header-image' => 'xs:string',
'cpp-header-border-color' => 'xs:string',
'cpp-header-back-color' => 'xs:string',
'cpp-payflow-color' => 'xs:string',
PaymentAction => '',
BuyerEmail => 'ebl:EmailAddressType' );
## billing agreement details type
my %badtypes = ( BillingType => '', #'ns:BillingCodeType',
BillingAgreementDescription => 'xs:string',
PaymentType => '', #'ns:MerchantPullPaymentCodeType',
BillingAgreementCustom => 'xs:string', );
## set some defaults
$args{PaymentAction} ||= 'Sale';
$args{currencyID} ||= 'USD';
my $currencyID = delete $args{currencyID};
## SetExpressCheckoutRequestDetails
my @secrd =
( SOAP::Data->name( OrderTotal => delete $args{OrderTotal} )->type( $types{OrderTotal} )
->attr( {currencyID => $currencyID, xmlns => $self->C_xmlns_ebay}),
SOAP::Data->name( ReturnURL => delete $args{ReturnURL} )->type( $types{ReturnURL} ),
SOAP::Data->name( CancelURL => delete $args{CancelURL} )->type( $types{CancelURL} ),
);
## add all the other fields
for my $field ( keys %types ) {
next unless defined $args{$field};
if( $field eq 'MaxAmount' ) {
push @secrd, SOAP::Data->name( $field => $args{$field} )->type( $types{$field} )
->attr( {currencyID => $currencyID, xmlns => $self->C_xmlns_ebay} );
}
else {
push @secrd, SOAP::Data->name( $field => $args{$field} )->type( $types{$field} );
}
}
my @btypes = ();
for my $field ( keys %badtypes ) {
next unless $args{$field};
push @btypes, SOAP::Data->name( $field => $args{$field} )->type( $badtypes{$field} );
}
push @secrd, SOAP::Data->name( BillingAgreementDetails => \SOAP::Data->value(@btypes) )
if $args{'BillingType'};
my $request = SOAP::Data
->name( SetExpressCheckoutRequest => \SOAP::Data->value
( $self->version_req,
SOAP::Data->name( SetExpressCheckoutRequestDetails => \SOAP::Data->value(@secrd) )
->attr( {xmlns => $self->C_xmlns_ebay} ),
)
)->type( 'ns:SetExpressCheckoutRequestType' );
my $som = $self->doCall( SetExpressCheckoutReq => $request )
or return;
my $path = '/Envelope/Body/SetExpressCheckoutResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response, { Token => 'Token' });
return %response;
}
sub GetExpressCheckoutDetails {
my $self = shift;
my $token = shift;
my $request = SOAP::Data
->name( GetExpressCheckoutDetailsRequest => \SOAP::Data->value
( $self->version_req,
SOAP::Data->name( Token => $token )
->type('xs:string')->attr( {xmlns => $self->C_xmlns_ebay} ),
)
)->type( 'ns:GetExpressCheckoutRequestType' );
my $som = $self->doCall( GetExpressCheckoutDetailsReq => $request )
or return;
my $path = '/Envelope/Body/GetExpressCheckoutDetailsResponse';
my %details = ();
unless( $self->getBasic($som, $path, \%details) ) {
$self->getErrors($som, $path, \%details);
return %details;
}
$self->getFields($som,
"$path/GetExpressCheckoutDetailsResponseDetails",
\%details,
{ Token => 'Token',
Custom => 'Custom',
InvoiceID => 'InvoiceID',
ContactPhone => 'ContactPhone',
Payer => 'PayerInfo/Payer',
PayerID => 'PayerInfo/PayerID',
PayerStatus => 'PayerInfo/PayerStatus',
FirstName => 'PayerInfo/PayerName/FirstName',
LastName => 'PayerInfo/PayerName/LastName',
PayerBusiness => 'PayerInfo/PayerBusiness',
AddressStatus => 'PayerInfo/Address/AddressStatus',
Name => 'PayerInfo/Address/Name',
Street1 => 'PayerInfo/Address/Street1',
Street2 => 'PayerInfo/Address/Street2',
CityName => 'PayerInfo/Address/CityName',
StateOrProvince => 'PayerInfo/Address/StateOrProvince',
PostalCode => 'PayerInfo/Address/PostalCode',
Country => 'PayerInfo/Address/Country',
} );
return %details;
}
sub DoExpressCheckoutPayment {
my $self = shift;
my %args = @_;
my %types = ( Token => 'xs:string',
PaymentAction => '', ## NOTA BENE!
PayerID => 'ebl:UserIDType',
currencyID => '',
);
## PaymentDetails
my %pd_types = ( OrderTotal => 'ebl:BasicAmountType',
OrderDescription => 'xs:string',
ItemTotal => 'ebl:BasicAmountType',
ShippingTotal => 'ebl:BasicAmountType',
HandlingTotal => 'ebl:BasicAmountType',
TaxTotal => 'ebl:BasicAmountType',
Custom => 'xs:string',
InvoiceID => 'xs:string',
ButtonSource => 'xs:string',
NotifyURL => 'xs:string',
);
## ShipToAddress
my %st_types = ( ST_Name => 'xs:string',
ST_Street1 => 'xs:string',
ST_Street2 => 'xs:string',
ST_CityName => 'xs:string',
ST_StateOrProvince => 'xs:string',
ST_Country => 'xs:string',
ST_PostalCode => 'xs:string',
);
##PaymentDetailsItem
my %pdi_types = ( PDI_Name => 'xs:string',
PDI_Amount => 'ebl:BasicAmountType',
PDI_Number => 'xs:string',
PDI_Quantity => 'xs:string',
PDI_Tax => 'ebl:BasicAmountType',
);
$args{PaymentAction} ||= 'Sale';
$args{currencyID} ||= 'USD';
my @payment_details = ( );
## push OrderTotal here and delete it (i.e., and all others that have special attrs)
push @payment_details, SOAP::Data->name( OrderTotal => $args{OrderTotal} )
->type( $pd_types{OrderTotal} )
->attr( { currencyID => $args{currencyID},
xmlns => $self->C_xmlns_ebay } );
## don't process it again
delete $pd_types{OrderTotal};
for my $field ( keys %pd_types ) {
if( $args{$field} ) {
push @payment_details,
SOAP::Data->name( $field => $args{$field} )
->type( $pd_types{$field} );
}
}
##
## ShipToAddress
##
my @ship_types = ();
for my $field ( keys %st_types ) {
if( $args{$field} ) {
(my $name = $field) =~ s/^ST_//;
push @ship_types,
SOAP::Data->name( $name => $args{$field} )
->type( $st_types{$field} );
}
}
if( scalar @ship_types ) {
push @payment_details,
SOAP::Data->name( ShipToAddress => \SOAP::Data->value
( @ship_types )->type('ebl:AddressType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
);
}
##
## PaymentDetailsItem
##
my @payment_details_item = ();
for my $field ( keys %pdi_types ) {
if( $args{$field} ) {
(my $name = $field) =~ s/^PDI_//;
push @payment_details_item,
SOAP::Data->name( $name => $args{$field} )
->type( $pdi_types{$field} );
}
}
if( scalar @payment_details_item ) {
push @payment_details,
SOAP::Data->name( PaymentDetailsItem => \SOAP::Data->value
( @payment_details_item )->type('ebl:PaymentDetailsItemType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
);
}
##
## ExpressCheckoutPaymentDetails
##
my @express_details = (
SOAP::Data->name( Token => $args{Token} )
->type($types{Token})->attr( {xmlns => $self->C_xmlns_ebay} ),
SOAP::Data->name( PaymentAction => $args{PaymentAction} )
->type($types{PaymentAction})->attr( {xmlns => $self->C_xmlns_ebay} ),
SOAP::Data->name( PayerID => $args{PayerID} )
->type($types{PayerID})->attr( {xmlns => $self->C_xmlns_ebay} ),
SOAP::Data->name( PaymentDetails => \SOAP::Data->value
( @payment_details )->type('ebl:PaymentDetailsType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
), );
##
## the main request object
##
my $request = SOAP::Data
->name( DoExpressCheckoutPaymentRequest => \SOAP::Data->value
( $self->version_req,
SOAP::Data->name( DoExpressCheckoutPaymentRequestDetails => \SOAP::Data->value
( @express_details )->type( 'ns:DoExpressCheckoutPaymentRequestDetailsType' )
)->attr( {xmlns => $self->C_xmlns_ebay} ),
)
);
my $som = $self->doCall( DoExpressCheckoutPaymentReq => $request )
or return;
my $path = '/Envelope/Body/DoExpressCheckoutPaymentResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields( $som,
"$path/DoExpressCheckoutPaymentResponseDetails",
\%response,
{ Token => 'Token',
BillingAgreementID => 'BillingAgreementID',
TransactionID => 'PaymentInfo/TransactionID',
TransactionType => 'PaymentInfo/TransactionType',
PaymentType => 'PaymentInfo/PaymentType',
PaymentDate => 'PaymentInfo/PaymentDate',
GrossAmount => 'PaymentInfo/GrossAmount',
FeeAmount => 'PaymentInfo/FeeAmount',
SettleAmount => 'PaymentInfo/SettleAmount',
TaxAmount => 'PaymentInfo/TaxAmount',
ExchangeRate => 'PaymentInfo/ExchangeRate',
PaymentStatus => 'PaymentInfo/PaymentStatus',
PendingReason => 'PaymentInfo/PendingReason',
} );
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::ExpressCheckout - PayPal Express Checkout API
=head1 SYNOPSIS
use Business::PayPal::API::ExpressCheckout;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::ExpressCheckout ( ... );
my %resp = $pp->SetExpressCheckout
( OrderTotal => '55.43', ## defaults to USD
ReturnURL => 'http://site.tld/return.html',
CancelURL => 'http://site.tld/canceltation.html', );
... time passes, buyer validates the token with PayPal ...
my %details = $pp->GetExpressCheckoutDetails($resp{Token});
## now ask PayPal to xfer the money
my %payinfo = $pp->DoExpressCheckoutPayment( Token => $details{Token},
PaymentAction => 'Sale',
PayerID => $details{PayerID},
OrderTotal => '55.43' );
=head1 DESCRIPTION
B<Business::PayPal::API::ExpressCheckout> implements PayPal's
B<Express Checkout API> using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 SetExpressCheckout
Implements PayPal's "Website Payment Pro" B<SetExpressCheckout> API call. Supported
parameters include:
Token
OrderTotal
currencyID
MaxAmount
OrderDescription
Custom
InvoiceID
ReturnURL
CancelURL
Address
ReqConfirmShipping
NoShipping
AddressOverride
LocaleCode
PageStyle
'cpp-header-image'
'cpp-header-border-color'
'cpp-header-back-color'
'cpp-payflow-color'
PaymentAction
BuyerEmail
BillingType
BillingAgreementDescription
PaymentType
BillingAgreementCustom
as described in the PayPal "Web Services API Reference" document. The
default currency setting is 'USD' if not otherwise specified.
Returns a hash containing a 'Token' key, whose value represents the
PayPal transaction token.
Required fields:
OrderTotal, ReturnURL, CancelURL.
my %resp = $pp->SetExpressCheckout();
my $token = $resp{Token};
Example (courtesy Ollie Ready):
my $address = {
Name => 'Some Customer',
Street1 => '888 Test St.',
Street2 => 'Suite 9',
CityName => 'San Diego',
StateOrProvince => 'CA',
PostalCode => '92111',
Country => 'US',
Phone => '123-123-1234',
};
my %response = $pp->SetExpressCheckout(
OrderTotal => '11.01',
ReturnURL => '<![CDATA[http://example.com/p?cmd=checkout]]>',
CancelURL => 'http://example.com',
PaymentAction => 'Authorization',
AddressOverride => 1,
Address => $address,
);
=head2 GetExpressCheckoutDetails
Implements PayPal's WPP B<SetExpressCheckout> API call. Supported
parameters include:
Token
as described in the PayPal "Web Services API Reference" document. This
is the same token you received from B<SetExpressCheckout>.
Returns a hash with the following keys:
Token
Custom
InvoiceID
ContactPhone
Payer
PayerID
PayerStatus
FirstName
LastName
PayerBusiness
AddressStatus
Name
Street1
Street2
CityName
StateOrProvince
PostalCode
Country
Required fields:
Token
=head2 DoExpressCheckoutPayment
Implements PayPal's WPP B<SetExpressCheckout> API call. Supported
parameters include:
Token
PaymentAction (defaults to 'Sale' if not supplied)
PayerID
currencyID (defaults to 'USD' if not supplied)
OrderTotal
OrderDescription
ItemTotal
ShippingTotal
HandlingTotal
TaxTotal
Custom
InvoiceID
ButtonSource
NotifyURL
ST_Name
ST_Street1
ST_Street2
ST_CityName
ST_StateOrProvince
ST_Country
ST_PostalCode
PDI_Name
PDI_Amount
PDI_Number
PDI_Quantity
PDI_Tax
as described in the PayPal "Web Services API Reference" document.
Returns a hash with the following keys:
Token
TransactionID
TransactionType
PaymentType
PaymentDate
GrossAmount
FeeAmount
SettleAmount
TaxAmount
ExchangeRate
PaymentStatus
PendingReason
BillingAgreementID (if BillingType 'MerchantInitiatedBilling'
was specified during SetExpressCheckout)
Required fields:
Token, PayerID, OrderTotal
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head1 EXAMPLES
Andy Spiegl <paypalcheckout.Spiegl@kascada.com> has kindly donated
some example code (in German) which may be found in the F<eg>
directory of this archive. Additional code examples may be found in
the F<t> test directory.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<SOAP::Lite>, L<Business::PayPal::API>,
L<https://www.paypal.com/IntegrationCenter/ic_expresscheckout.html>,
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scott Wiersdorf, E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.6 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,295 @@
package Business::PayPal::API::GetTransactionDetails;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.12';
our $CVS_VERSION = '$Id: GetTransactionDetails.pm,v 1.5 2009/07/28 18:00:59 scott Exp $';
our @EXPORT_OK = qw(GetTransactionDetails); ## fake exporter
sub GetTransactionDetails {
my $self = shift;
my %args = @_;
my @trans =
(
$self->version_req,
SOAP::Data->name( TransactionID => $args{TransactionID} )->type( 'xs:string' ),
);
my $request = SOAP::Data->name
( GetTransactionDetailsRequest => \SOAP::Data->value( @trans ) )
->type("ns:GetTransactionDetailsRequestType");
my $som = $self->doCall( GetTransactionDetailsReq => $request )
or return;
my $path = '/Envelope/Body/GetTransactionDetailsResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$path .= '/PaymentTransactionDetails';
$self->getFields($som, $path, \%response,
{ Business => '/ReceiverInfo/Business',
Receiver => '/ReceiverInfo/Receiver',
ReceiverID => '/ReceiverInfo/ReceiverID',
Payer => '/PayerInfo/Payer',
PayerID => '/PayerInfo/PayerID',
PayerStatus => '/PayerInfo/PayerStatus',
Salutation => '/PayerInfo/PayerName/Salutation',
FirstName => '/PayerInfo/PayerName/FirstName',
MiddleName => '/PayerInfo/PayerName/MiddleName',
LastName => '/PayerInfo/PayerName/LastName',
PayerCountry => '/PayerInfo/PayerCountry',
PayerBusiness => '/PayerInfo/PayerBusiness',
AddressOwner => '/PayerInfo/Address/AddressOwner',
AddressStatus => '/PayerInfo/Address/AddressStatus',
ADD_Name => '/PayerInfo/Address/Name',
Street1 => '/PayerInfo/Address/Street1',
Street2 => '/PayerInfo/Address/Street2',
CityName => '/PayerInfo/Address/CityName',
StateOrProvince => '/PayerInfo/Address/StateOrProvince',
Country => '/PayerInfo/Address/Country',
CountryName => '/PayerInfo/Address/CountryName',
Phone => '/PayerInfo/Address/Phone',
PostalCode => '/PayerInfo/Address/PostalCode',
TransactionID => '/PaymentInfo/TransactionID',
ParentTransactionID => '/PaymentInfo/ParentTransactionID',
ReceiptID => '/PaymentInfo/ReceiptID',
TransactionType => '/PaymentInfo/TransactionType',
PaymentType => '/PaymentInfo/PaymentType',
PaymentDate => '/PaymentInfo/PaymentDate',
GrossAmount => '/PaymentInfo/GrossAmount',
FeeAmount => '/PaymentInfo/FeeAmount',
SettleAmount => '/PaymentInfo/SettleAmount',
TaxAmount => '/PaymentInfo/TaxAmount',
ExchangeRate => '/PaymentInfo/ExchangeRate',
PaymentStatus => '/PaymentInfo/PaymentStatus',
PendingReason => '/PaymentInfo/PendingReason',
ReasonCode => '/PaymentInfo/ReasonCode',
InvoiceID => '/PaymentItemInfo/InvoiceID',
Custom => '/PaymentItemInfo/Custom',
Memo => '/PaymentItemInfo/Memo',
SalesTax => '/PaymentItemInfo/SalesTax',
PII_SalesTax => '/PaymentItemInfo/PaymentItem/SalesTax',
PII_Name => '/PaymentItemInfo/PaymentItem/Name',
PII_Number => '/PaymentItemInfo/PaymentItem/Number',
PII_Quantity => '/PaymentItemInfo/PaymentItem/Quantity',
PII_Amount => '/PaymentItemInfo/PaymentItem/Amount',
PII_Options => '/PaymentItemInfo/PaymentItem/Options',
PII_SubscriptionID => '/PaymentItemInfo/Subscription/SubscriptionID',
PII_SubscriptionDate => '/PaymentItemInfo/Subscription/SubscriptionDate',
PII_EffectiveDate => '/PaymentItemInfo/Subscription/EffectiveDate',
PII_RetryTime => '/PaymentItemInfo/Subscription/RetryTime',
PII_Username => '/PaymentItemInfo/Subscription/Username',
PII_Password => '/PaymentItemInfo/Subscription/Password',
PII_Recurrences => '/PaymentItemInfo/Subscription/Recurrences',
PII_reattempt => '/PaymentItemInfo/Subscription/reattempt',
PII_recurring => '/PaymentItemInfo/Subscription/recurring',
PII_Amount => '/PaymentItemInfo/Subscription/Amount',
PII_period => '/PaymentItemInfo/Subscription/period',
PII_BuyerID => '/PaymentItemInfo/Auction/BuyerID',
PII_ClosingDate => '/PaymentItemInfo/Auction/ClosingDate',
PII_multiItem => '/PaymentItemInfo/Auction/multiItem',
}
);
## multiple payment items
my $paymentitems = $self->getFieldsList( $som, $path . '/PaymentItemInfo/PaymentItem',
{ SalesTax => 'SalesTax',
Name => 'Name',
Number => 'Number',
Quantity => 'Quantity',
Amount => 'Amount',
Options => 'Options',
} );
if( scalar(@$paymentitems) > 1 ) {
$response{PaymentItems} = $paymentitems;
}
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::GetTransactionDetails - PayPal GetTransactionDetails API
=head1 SYNOPSIS
use Business::PayPal::API::GetTransactionDetails;
my $pp = new Business::PayPal::API::GetTransactionDetails ( ... );
or
## see Business::PayPal::API documentation for parameters
use Business::PayPal::API qw(GetTransactionDetails);
my $pp = new Business::PayPal::API( ... );
my %response = $pp->GetTransactionDetails( TransactionID => $transid, );
=head1 DESCRIPTION
B<Business::PayPal::API::GetTransactionDetails> implements PayPal's
B<GetTransactionDetails> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 GetTransactionDetails
Implements PayPal's B<GetTransactionDetails> API call. Supported
parameters include:
TransactionID
as described in the PayPal "Web Services API Reference" document.
Returns a hash containing the transaction details, including these fields:
Business
Receiver
ReceiverID
Payer
PayerID
PayerStatus
Salutation
FirstName
MiddleName
LastName
PayerCountry
PayerBusiness
AddressOwner
AddressStatus
ADD_Name
Street1
Street2
CityName
StateOrProvince
Country
CountryName
Phone
PostalCode
TransactionID
ParentTransactionID
ReceiptID
TransactionType
PaymentType
PaymentDate
GrossAmount
FeeAmount
SettleAmount
TaxAmount
ExchangeRate
PaymentStatus
PendingReason
ReasonCode
InvoiceID
Custom
Memo
SalesTax
PII_SaleTax
PII_Name
PII_Number
PII_Quantity
PII_Amount
PII_Options
PII_SubscriptionID
PII_SubscriptionDate
PII_EffectiveDate
PII_RetryTime
PII_Username
PII_Password
PII_Recurrences
PII_reattempt
PII_recurring
PII_Amount
PII_period
PII_BuyerID
PII_ClosingDate
PII_multiItem
As described in the API document. Note: some fields have prefixes to
remove ambiguity for like-named fields (e.g., "PII_").
If there are multiple PaymentItems, then an additional field
'PaymentItems' will be available with an arrayref of PaymentItem
records:
PaymentItems => [ { SalesTax => ...,
Name => '...',
Number => '...',
Quantity => '...',
Amount => '...',
},
{ SalesTax => ..., etc.
} ]
Example:
my %resp = $pp->GetTransactionDetails( TransactionID => $trans_id );
print "Payer: $resp{Payer}\n";
for my $item ( @{ $resp{PaymentItems} } ) {
print "Name: " . $item->{Name} . "\n";
print "Amt: " . $item->{Amount} . "\n";
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scot Wiersdorf E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,188 @@
package Business::PayPal::API::MassPay;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.03';
our $CVS_VERSION = '$Id: MassPay.pm,v 1.3 2009/07/28 18:00:59 scott Exp $';
our @EXPORT_OK = qw( MassPay );
sub MassPay {
my $self = shift;
my %args = @_;
## set some defaults
$args{currencyID} ||= 'USD';
$args{ReceiverType} ||= 'EmailAddress';
$args{MassPayItems} ||= [];
$args{Version} ||= "1.0";
my %types = ( EmailSubject => 'xs:string',
Version => 'xsd:string',
# ReceiverType => 'ebl:ReceiverInfoCodeType', ## EmailAddress | UserID
);
my %attr = ( Version => { xmlns => $self->C_xmlns_ebay },
Amount => { currencyID => $args{currencyID} }, );
## mass pay item
my %mpi_type = ( ReceiverEmail => 'ebl:EmailAddressType',
ReceiverID => 'xs:string',
Amount => 'ebl:BasicAmountType',
UniqueId => 'xs:string',
Note => 'xs:string', );
my @recipients = @{ $args{MassPayItems} };
my @masspay = ();
for my $type ( sort keys %types ) {
next unless $args{$type};
if( $attr{$type} ) {
push @masspay, SOAP::Data->name( $type => $args{$type} )->type($types{$type})->attr( { %{ $attr{$type} } } );
}
else {
push @masspay, SOAP::Data->name( $type => $args{$type} )->type($types{$type});
}
}
if( $args{ReceiverType} eq 'UserID' ) {
delete $mpi_type{ReceiverEmail};
}
else {
delete $mpi_type{ReceiverID};
}
for my $rcpt ( @recipients ) {
my @rcpt = ();
for my $type ( keys %mpi_type ) {
next unless $mpi_type{$type};
if( $attr{$type} ) {
push @rcpt, SOAP::Data->name( $type => $rcpt->{$type} )->type($mpi_type{$type})->attr( { %{ $attr{$type} } } );
}
else {
push @rcpt, SOAP::Data->name( $type => $rcpt->{$type} )->type($mpi_type{$type});
}
}
push @masspay, SOAP::Data->name( MassPayItem => \SOAP::Data->value( @rcpt ) )->type("ns:MassPayRequestItemType");
}
my $request = SOAP::Data->name
( MassPayRequest => \SOAP::Data->value( @masspay ) )
->type("ns:MassPayRequestType");
my $som = $self->doCall( MassPayReq => $request )
or return;
my $path = '/Envelope/Body/MassPayResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::MassPay - PayPal MassPay API
=head1 SYNOPSIS
use Business::PayPal::API::MassPay;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::MassPay ( ... );
my %response = $pp->MassPay( EmailSubject => "Here's your moola",
MassPayItems => [ { ReceiverEmail => 'joe@somewhere.tld',
Amount => '95.44',
Note => 'Thanks for your stuff!' },
{ ReceiverEmail => 'bob@elsewhere.tld',
Amount => '15.31',
Note => 'We owe you one' }, ] );
);
=head1 DESCRIPTION
B<Business::PayPal::API::MassPay> implements PayPal's B<Mass Pay> API
using SOAP::Lite to make direct API calls to PayPal's SOAP API
server. It also implements support for testing via PayPal's
I<sandbox>. Please see L<Business::PayPal::API> for details on using
the PayPal sandbox.
=head2 MassPay
Implements PayPal's B<Mass Pay> API call. Supported parameters
include:
EmailSubject
MassPayItems
The B<MassPayItem> parameter is a list reference of hashrefs, each
containing the following fields:
ReceiverEmail
Amount
UniqueId
Note
as described in the PayPal "Web Services API Reference" document.
Returns a hash containing the generic response structure (as per the
PayPal Web Services API).
Example:
my %resp = $pp->MassPay( EmailSubject => "This is the subject",
MassPayItems => [ { ReceiverEmail => 'joe@test.tld',
Amount => '24.00',
UniqueId => "123456",
Note => "Enjoy the money. Don't spend it all in one place." } ] );
unless( $resp{Ack} eq 'Success' ) {
die "Failed: " . $resp{Errors}[0]{LongMessage} . "\n";
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head1 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scot Wiersdorf E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2007 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,143 @@
package Business::PayPal::API::ReauthorizationRequest;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.12';
our $CVS_VERSION = '$Id: ReauthorizationRequest.pm,v 1.2 2007/09/27 20:32:32 scott Exp $';
our @EXPORT_OK = qw(DoReauthorizationRequest);
sub DoReauthorizationRequest {
my $self = shift;
my %args = @_;
my %types = ( AuthorizationID => 'xs:string',
Amount => 'ebl:BasicAmountType',);
$args{currencyID} ||= 'USD';
my @ref_trans =
($self->version_req,
SOAP::Data->name( AuthorizationID => $args{AuthorizationID} )->type($types{AuthorizationID}),);
push @ref_trans,
SOAP::Data->name( Amount => $args{Amount} )
->type( $types{Amount} )
->attr( { currencyID => $args{currencyID} } );
my $request = SOAP::Data->name
( DoReauthorizationRequest => \SOAP::Data->value( @ref_trans ) )
->type("ns:ReauthorizationRequestType");
my $som = $self->doCall( DoReauthorizationReq => $request )
or return;
my $path = '/Envelope/Body/DoReauthorizationResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response,
{ AuthorizationID => 'AuthorizationID',
Amount => 'Amount', }
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::ReauthorizationRequest - PayPal ReauthorizationRequest API
=head1 SYNOPSIS
use Business::PayPal::API::ReauthorizationRequest;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::ReauthorizationRequest ( ... );
my %response = $pp->DoReauthorizationRequest (
AuthorizationID => $transid,
Amount => $amount,
CurrencyID => $currencyID
);
=head1 DESCRIPTION
B<Business::PayPal::API::ReauthorizationRequest> implements PayPal's
B<DoReauthorizationRequest> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 DoReauthorizationRequest
Implements PayPal's B<DoReauthorizationRequest> API call. Supported
parameters include:
AuthorizationID
Amount
currencyID (defaults to 'USD' if not supplied)
as described in the PayPal "Web Services API Reference" document. The
default B<currencyID> setting is 'USD' if not otherwise specified. The
DoReauthorization is not allowed before the three day grace period set
for the original AuthorizeRequest.
Returns a hash containing the results of the transaction.
Example:
my %resp = $pp->DoReauthorizationRequest (
AuthorizationID => $trans_id,
Amount => '15.00',
CurrencyID => 'USD'
);
unless( $resp{Ack} ne 'Success' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Danny Hembree E<lt>danny@dynamical.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Danny Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,579 @@
package Business::PayPal::API::RecurringPayments;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.02';
our $CVS_VERSION = '$Id: RecurringPayments.pm,v 1.2 2009/07/28 18:00:59 scott Exp $';
our @EXPORT_OK = qw( SetCustomerBillingAgreement
GetBillingAgreementCustomerDetails
CreateRecurringPaymentsProfile
DoReferenceTransaction);
our $API_VERSION = '50.0';
sub SetCustomerBillingAgreement {
my $self = shift;
my %args = @_;
## billing agreement details type
my %badtypes = ( BillingType => '', # 'ns:BillingCodeType',
BillingAgreementDescription => 'xs:string',
PaymentType => '', # 'ns:MerchantPullPaymentCodeType',
BillingAgreementCustom => 'xs:string', );
my %types = ( # BillingAgreementDetails => 'ns:BillingAgreementDetailsType',
ReturnURL => 'xs:string',
CancelURL => 'xs:string',
LocaleCode => 'xs:string',
PageStyle => 'xs:string',
'cpp-header-image' => 'xs:string',
'cpp-header-border-color' => 'xs:string',
'cpp-header-back-color' => 'xs:string',
'cpp-payflow-color' => 'xs:string',
PaymentAction => '',
BuyerEmail => 'ns:EmailAddressType', );
## set defaults
$args{BillingType} ||= 'RecurringPayments';
$args{PaymentType} ||= 'InstantOnly';
$args{currencyID} ||= 'USD';
my @btypes = ();
for my $field ( keys %badtypes ) {
next unless $args{$field};
push @btypes, SOAP::Data->name( $field => $args{$field} )->type( $badtypes{$field} );
}
my @scba = ();
for my $field ( keys %types ) {
next unless $args{$field};
push @scba, SOAP::Data->name( $field => $args{$field} )->type( $types{$field} );
}
push @scba, SOAP::Data->name( BillingAgreementDetails => \SOAP::Data->value(@btypes) );
my $request = SOAP::Data
->name( SetCustomerBillingAgreementRequest => \SOAP::Data->value
( $API_VERSION,
SOAP::Data->name( SetCustomerBillingAgreementRequestDetails => \SOAP::Data->value(@scba)
)->attr( {xmlns => $self->C_xmlns_ebay} ),
)
)->type( 'ns:SetCustomerBillingAgreementRequestDetailsType' );
my $som = $self->doCall( SetCustomerBillingAgreementReq => $request )
or return;
my $path = '/Envelope/Body/SetCustomerBillingAgreementResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response, { Token => 'Token' });
return %response;
}
sub GetBillingAgreementCustomerDetails {
my $self = shift;
my $token = shift;
my $request = SOAP::Data
->name( GetBillingAgreementCustomerDetailsRequest => \SOAP::Data->value
( $self->version_req,
SOAP::Data->name( Token => $token )
->type('xs:string')->attr( {xmlns => $self->C_xmlns_ebay} ),
)
)->type( 'ns:GetBillingAgreementCustomerDetailsResponseType' );
my $som = $self->doCall( GetBillingAgreementCustomerDetailsReq => $request )
or return;
my $path = '/Envelope/Body/GetBillingAgreementCustomerDetailsResponse';
my %details = ();
unless( $self->getBasic($som, $path, \%details) ) {
$self->getErrors($som, $path, \%details);
return %details;
}
$self->getFields($som,
"$path/GetBillingAgreementCustomerDetailsResponseDetails",
\%details,
{ Token => 'Token',
Payer => 'PayerInfo/Payer',
PayerID => 'PayerInfo/PayerID',
PayerStatus => 'PayerInfo/PayerStatus', ## 'unverified'
PayerBusiness => 'PayerInfo/PayerBusiness',
Name => 'PayerInfo/Address/Name',
AddressOwner => 'PayerInfo/Address/AddressOwner', ## 'PayPal'
AddressStatus => 'PayerInfo/Address/AddressStatus', ## 'none'
Street1 => 'PayerInfo/Address/Street1',
Street2 => 'PayerInfo/Address/Street2',
StateOrProvince => 'PayerInfo/Address/StateOrProvince',
PostalCode => 'PayerInfo/Address/PostalCode',
CountryName => 'PayerInfo/Address/CountryName',
Salutation => 'PayerInfo/PayerName/Salutation',
FirstName => 'PayerInfo/PayerName/FirstName',
MiddleName => 'PayerInfo/PayerName/MiddleName',
LastName => 'PayerInfo/PayerName/LastName',
Suffix => 'PayerInfo/PayerName/Suffix',
} );
return %details;
}
sub CreateRecurringPaymentsProfile {
my $self = shift;
my %args = @_;
## RecurringPaymentProfileDetails
my %profiledetailstype = ( SubscriberName => 'xs:string',
# SubscriberShipperAddress => 'ns:AddressType',
BillingStartDate => 'xs:dateTime', ## MM-DD-YY
ProfileReference => 'xs:string', );
## ScheduleDetailsType
my %schedtype = ( Description => 'xs:string',
# ActivationDetails => 'ns:ActivationDetailsType',
# TrialPeriod => 'ns:BillingPeriodDetailsType',
# PaymentPeriod => 'ns:BillingPeriodDetailsType',
MaxFailedPayments => 'xs:int',
AutoBillOutstandingAmount => 'ns:AutoBillType', ); ## NoAutoBill or AddToNextBilling
## activation details
my %activationdetailstype = ( InitialAmount => 'cc:BasicAmountType',
FailedInitialAmountAction => 'ns:FailedPaymentAction' ); ## ContinueOnFailure or CancelOnFailure
## BillingPeriodDetailsType
my %trialbilltype = ( TrialBillingPeriod => 'ns:BillingPeriodType',
TrialBillingFrequency => 'xs:int',
TrialTotalBillingCycles => 'xs:int',
TrialAmount => 'cc:AmountType',
TrialShippingAmount => 'cc:AmountType',
TrialTaxAmount => 'cc:AmountType', );
my %paymentbilltype = ( PaymentBillingPeriod => 'ns:BillingPeriodType',
PaymentBillingFrequency => 'xs:int',
PaymentTotalBillingCycles => 'xs:int',
PaymentAmount => 'cc:AmountType',
PaymentShippingAmount => 'cc:AmountType',
PaymentTaxAmount => 'cc:AmountType', );
## AddressType
my %payaddrtype = ( CCPayerName => 'xs:string',
CCPayerStreet1 => 'xs:string',
CCPayerStreet2 => 'xs:string',
CCPayerCityName => 'xs:string',
CCPayerStateOrProvince => 'xs:string',
CCPayerCountry => 'xs:string', ## ebl:CountryCodeType
CCPayerPostalCode => 'xs:string',
CCPayerPhone => 'xs:string', );
my %shipaddrtype = ( SubscriberShipperName => 'xs:string',
SubscriberShipperStreet1 => 'xs:string',
SubscriberShipperStreet2 => 'xs:string',
SubscriberShipperCityName => 'xs:string',
SubscriberShipperStateOrProvince => 'xs:string',
SubscriberShipperCountry => 'xs:string', ## ebl:CountryCodeType
SubscriberShipperPostalCode => 'xs:string',
SubscriberShipperPhone => 'xs:string', );
## credit card payer
my %payerinfotype = ( CCPayer => 'ns:EmailAddressType',
CCPayerID => 'xs:string',
CCPayerStatus => 'xs:string',
CCPayerName => 'xs:string',
CCPayerCountry => 'xs:string',
CCPayerPhone => 'xs:string',
CCPayerBusiness => 'xs:string',
# Address => 'ns:AddressType',
);
## credit card details
my %creditcarddetailstype = ( # CardOwner => 'ns:PayerInfoType',
CreditCardType => 'ebl:CreditCardType', ## Visa, MasterCard, Discover, Amex, Switch, Solo
CreditCardNumber => 'xs:string',
ExpMonth => 'xs:int',
ExpYear => 'xs:int',
CVV2 => 'xs:string',
StartMonth => 'xs:string',
StartYear => 'xs:string',
IssueNumber => 'xs:string', );
## this gets pushed onto scheduledetails
my @activationdetailstype = ();
for my $field ( keys %activationdetailstype ) {
next unless exists $args{$field};
my $real_field = $field;
push @activationdetailstype, SOAP::Data->name( $real_field => $args{$field} )->type( $activationdetailstype{$field} );
}
## this gets pushed onto scheduledetails
my @trialbilltype = ();
for my $field ( keys %trialbilltype ) {
next unless exists $args{$field};
(my $real_field = $field) =~ s/^Trial//;
push @trialbilltype, SOAP::Data->name( $real_field => $args{$field} )->type( $trialbilltype{$field} );
}
## this gets pushed onto scheduledetails
my @paymentbilltype = ();
for my $field ( keys %paymentbilltype ) {
next unless exists $args{$field};
(my $real_field = $field) =~ s/^Payment//;
push @paymentbilltype, SOAP::Data->name( $real_field => $args{$field} )->type( $paymentbilltype{$field} );
}
## this gets pushed onto the top
my @sched = ();
for my $field ( keys %schedtype ) {
next unless exists $args{$field};
push @sched, SOAP::Data->name( $field => $args{$field} )->type( $schedtype{$field} );
}
push @sched, SOAP::Data->name( TrialPeriod => \SOAP::Data->value(@trialbilltype) ); #->type( 'ns:BillingPeriodDetailsType' );
push @sched, SOAP::Data->name( PaymentPeriod => \SOAP::Data->value(@paymentbilltype) ); #->type( 'ns:BillingPeriodDetailsType' );
## this gets pushed into profile details
my @shipaddr = ();
for my $field ( keys %shipaddrtype ) {
next unless exists $args{$field};
(my $real_field = $field) =~ s/^SubscriberShipper//;
push @shipaddr, SOAP::Data->name( $real_field => $args{$field} )->type( $shipaddrtype{$field} );
}
## this gets pushed into payerinfo (from creditcarddetails)
my @payeraddr = ();
for my $field ( keys %payaddrtype ) {
next unless $args{$field};
(my $real_field = $field) =~ s/^CCPayer//;
push @payeraddr, SOAP::Data->name( $real_field => $args{$field} )->type( payaddrtype{$field} );
}
## credit card type
my @creditcarddetails = ();
for my $field ( keys %creditcarddetailstype ) {
next unless $args{$field};
(my $real_field = $field) =~ s/^CC//;
push @payeraddr, SOAP::Data->name( $real_field => $args{$field} )->type( payaddrtype{$field} );
}
## this gets pushed onto the top
my @profdetail = ();
for my $field ( keys %profiledetailstype ) {
next unless exists $args{$field};
push @profdetail, SOAP::Data->name( $field => $args{$field} )->type( $profiledetailstype{$field} );
}
push @profdetail, SOAP::Data->name( SubscriberShipperAddress => \SOAP::Data->value(@shipaddr) );
## crappard?
my @crpprd = ();
push @crpprd, SOAP::Data->name( Token => $args{Token} );
push @crpprd, SOAP::Data->name( CreditCardDetails => \SOAP::Data->value(@creditcarddetails) ); #->type( 'ns:CreditCardDetailsType' );
push @crpprd, SOAP::Data->name( RecurringPaymentProfileDetails => \SOAP::Data->value(@profdetail) ); #->type( 'ns:RecurringPaymentProfileDetailsType' );
push @crpprd, SOAP::Data->name( ScheduleDetails => \SOAP::Data->value(@sched) ); #->type( 'ns:ScheduleDetailsType' );
my $request = SOAP::Data->name
( CreateRecurringPaymentsProfileRequest => \SOAP::Data->value
( $API_VERSION,
# ( $self->version_req,
SOAP::Data->name( CreateRecurringPaymentsProfileRequestDetails => \SOAP::Data->value(@crpprd)
)->attr( {xmlns => $self->C_xmlns_ebay} )
)
); #->type( 'ns:CreateRecurringPaymentsProfileRequestType' );
my $som = $self->doCall( CreateRecurringPaymentsProfileReq => $request )
or return;
my $path = '/Envelope/Body/CreateRecurringPaymentsProfileResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response, { Token => 'Token' });
return %response;
}
sub DoReferenceTransaction {
my $self = shift;
my %args = @_;
my %types = ( ReferenceID => 'xs:string',
PaymentAction => '', ## NOTA BENE!
currencyID => '',
);
## PaymentDetails
my %pd_types = ( OrderTotal => 'ebl:BasicAmountType',
OrderDescription => 'xs:string',
ItemTotal => 'ebl:BasicAmountType',
ShippingTotal => 'ebl:BasicAmountType',
HandlingTotal => 'ebl:BasicAmountType',
TaxTotal => 'ebl:BasicAmountType',
Custom => 'xs:string',
InvoiceID => 'xs:string',
ButtonSource => 'xs:string',
NotifyURL => 'xs:string',
);
## ShipToAddress
my %st_types = ( ST_Name => 'xs:string',
ST_Street1 => 'xs:string',
ST_Street2 => 'xs:string',
ST_CityName => 'xs:string',
ST_StateOrProvince => 'xs:string',
ST_Country => 'xs:string',
ST_PostalCode => 'xs:string',
ST_Phone => 'xs:string',
);
##PaymentDetailsItem
my %pdi_types = ( PDI_Name => 'xs:string',
PDI_Description => 'xs:string',
PDI_Amount => 'ebl:BasicAmountType',
PDI_Number => 'xs:string',
PDI_Quantity => 'xs:string',
PDI_Tax => 'ebl:BasicAmountType',
);
$args{PaymentAction} ||= 'Sale';
$args{currencyID} ||= 'USD';
my @payment_details = ( );
## push OrderTotal here and delete it (i.e., and all others that have special attrs)
push @payment_details, SOAP::Data->name( OrderTotal => $args{OrderTotal} )
->type( $pd_types{OrderTotal} )
->attr( { currencyID => $args{currencyID},
xmlns => $self->C_xmlns_ebay } );
## don't process it again
delete $pd_types{OrderTotal};
for my $field ( keys %pd_types ) {
if( $args{$field} ) {
push @payment_details,
SOAP::Data->name( $field => $args{$field} )
->type( $pd_types{$field} );
}
}
##
## ShipToAddress
##
my @ship_types = ();
for my $field ( keys %st_types ) {
if( $args{$field} ) {
(my $name = $field) =~ s/^ST_//;
push @ship_types,
SOAP::Data->name( $name => $args{$field} )
->type( $st_types{$field} );
}
}
if( scalar @ship_types ) {
push @payment_details,
SOAP::Data->name( ShipToAddress => \SOAP::Data->value
( @ship_types )->type('ebl:AddressType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
);
}
##
## PaymentDetailsItem
##
my @payment_details_item = ();
for my $field ( keys %pdi_types ) {
if( $args{$field} ) {
(my $name = $field) =~ s/^PDI_//;
push @payment_details_item,
SOAP::Data->name( $name => $args{$field} )
->type( $pdi_types{$field} );
}
}
if( scalar @payment_details_item ) {
push @payment_details,
SOAP::Data->name( PaymentDetailsItem => \SOAP::Data->value
( @payment_details_item )->type('ebl:PaymentDetailsItemType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
);
}
##
## ReferenceTransactionPaymentDetails
##
my @reference_details = (
SOAP::Data->name( ReferenceID => $args{ReferenceID} )
->type($types{ReferenceID})->attr( {xmlns => $self->C_xmlns_ebay} ),
SOAP::Data->name( PaymentAction => $args{PaymentAction} )
->type($types{PaymentAction})->attr( {xmlns => $self->C_xmlns_ebay} ),
SOAP::Data->name( PaymentDetails => \SOAP::Data->value
( @payment_details )->type('ebl:PaymentDetailsType')
->attr( {xmlns => $self->C_xmlns_ebay} ),
), );
##
## the main request object
##
my $request = SOAP::Data
->name( DoReferenceTransactionRequest => \SOAP::Data->value
( $self->version_req,
SOAP::Data->name( DoReferenceTransactionRequestDetails => \SOAP::Data->value
( @reference_details )->type( 'ns:DoReferenceTransactionRequestDetailsType' )
)->attr( {xmlns => $self->C_xmlns_ebay} ),
)
);
my $som = $self->doCall( DoReferenceTransactionReq => $request )
or return;
my $path = '/Envelope/Body/DoReferenceTransactionResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields( $som,
"$path/DoReferenceTransactionResponseDetails",
\%response,
{ BillingAgreementID => 'BillingAgreementID',
TransactionID => 'PaymentInfo/TransactionID',
TransactionType => 'PaymentInfo/TransactionType',
PaymentType => 'PaymentInfo/PaymentType',
PaymentDate => 'PaymentInfo/PaymentDate',
GrossAmount => 'PaymentInfo/GrossAmount',
FeeAmount => 'PaymentInfo/FeeAmount',
SettleAmount => 'PaymentInfo/SettleAmount',
TaxAmount => 'PaymentInfo/TaxAmount',
ExchangeRate => 'PaymentInfo/ExchangeRate',
PaymentStatus => 'PaymentInfo/PaymentStatus',
PendingReason => 'PaymentInfo/PendingReason',
ReasonCode => 'PaymentInfor/ReasonCode',
} );
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::RecurringPayments - PayPal RecurringPayments API
=head1 SYNOPSIS
use Business::PayPal::API::RecurringPayments;
my $pp = new Business::PayPal::API::RecurringPayments( ... );
my %resp = $pp->FIXME
## Ask PayPal to charge a new transaction from the ReferenceID
## This method is used both for Recurring Transactions as well
## as for Express Checkout's MerchantInitiatedBilling, where
## ReferenceID is the BillingAgreementID returned from
## ExpressCheckout->DoExpressCheckoutPayment
my %payinfo = $pp->DoReferenceTransaction( ReferenceID => $details{ReferenceID},
PaymentAction => 'Sale',
OrderTotal => '55.43' );
=head1 DESCRIPTION
THIS MODULE IS NOT COMPLETE YET. PLEASE DO NOT REPORT ANY BUGS RELATED
TO IT.
=head2 DoReferenceTransaction
Implements PayPal's WPP B<DoReferenceTransaction> API call. Supported
parameters include:
ReferenceID (aka BillingAgreementID)
PaymentAction (defaults to 'Sale' if not supplied)
currencyID (defaults to 'USD' if not supplied)
OrderTotal
OrderDescription
ItemTotal
ShippingTotal
HandlingTotal
TaxTotal
Custom
InvoiceID
ButtonSource
NotifyURL
ST_Name
ST_Street1
ST_Street2
ST_CityName
ST_StateOrProvince
ST_Country
ST_PostalCode
ST_Phone
PDI_Name
PDI_Description
PDI_Amount
PDI_Number
PDI_Quantity
PDI_Tax
as described in the PayPal "Web Services API Reference" document.
Returns a hash with the following keys:
BillingAgreementID
TransactionID
TransactionType
PaymentType
PaymentDate
GrossAmount
FeeAmount
SettleAmount
TaxAmount
ExchangeRate
PaymentStatus
PendingReason
ReasonCode
Required fields:
ReferenceID, OrderTotal
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scot Wiersdorf E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2007 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,158 @@
package Business::PayPal::API::RefundTransaction;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.11';
our $CVS_VERSION = '$Id: RefundTransaction.pm,v 1.4 2006/03/24 17:11:50 scott Exp $';
our @EXPORT_OK = qw(RefundTransaction);
sub RefundTransaction {
my $self = shift;
my %args = @_;
my %types = ( TransactionID => 'xs:string',
RefundType => '', ## Other | Full | Partial
Amount => 'ebl:BasicAmountType',
Memo => 'xs:string', );
$args{currencyID} ||= 'USD';
$args{RefundType} ||= 'Full';
my @ref_trans =
(
$self->version_req,
SOAP::Data->name( TransactionID => $args{TransactionID} )->type($types{TransactionID}),
SOAP::Data->name( RefundType => $args{RefundType} )->type($types{RefundType}),
);
if( $args{RefundType} ne 'Full' && $args{Amount} ) {
push @ref_trans,
SOAP::Data->name( Amount => $args{Amount} )
->type( $types{Amount} )
->attr( { currencyID => $args{currencyID} } )
}
push @ref_trans,
SOAP::Data->name( Memo => $args{Memo} )->type( $types{Memo} )
if $args{Memo};
my $request = SOAP::Data->name
( RefundTransactionRequest => \SOAP::Data->value( @ref_trans ) )
->type("ns:RefundTransactionRequestType");
my $som = $self->doCall( RefundTransactionReq => $request )
or return;
my $path = '/Envelope/Body/RefundTransactionResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response,
{ RefundTransactionID => 'RefundTransactionID',
FeeRefundAmount => 'FeeRefundAmount',
NetRefundAmount => 'NetRefundAmount',
GrossRefundAmount => 'GrossRefundAmount', }
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::RefundTransaction - PayPal RefundTransaction API
=head1 SYNOPSIS
use Business::PayPal::API::RefundTransaction;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::RefundTransaction ( ... );
my %response = $pp->RefundTransaction( TransactionID => $transid,
RefundType => 'Full',
Memo => "Please come again!" );
=head1 DESCRIPTION
B<Business::PayPal::API::RefundTransaction> implements PayPal's
B<RefundTransaction> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 RefundTransaction
Implements PayPal's B<RefundTransaction> API call. Supported
parameters include:
TransactionID
RefundType (defaults to 'Full' if not supplied)
Amount
Memo
currencyID (defaults to 'USD' if not supplied)
as described in the PayPal "Web Services API Reference" document. The
default B<currencyID> setting is 'USD' if not otherwise specified. The
default B<RefundType> setting is 'Full' if not otherwise specified.
If B<RefundType> is set to 'Full', B<Amount> is ignored (even if
set). If B<RefundType> is set to 'Partial', B<Amount> is required.
Returns a hash containing the results of the transaction. The B<Ack>
element is likely the only useful return value at the time of this
revision (the Nov. 2005 errata to the Web Services API indicates that
the documented fields 'TransactionID', 'GrossAmount', etc. are I<not>
returned with this API call).
Example:
my %resp = $pp->RefundTransaction( TransactionID => $trans_id,
RefundType => 'Partial',
Amount => '15.00', );
unless( $resp{Ack} ne 'Success' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scot Wiersdorf E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,179 @@
package Business::PayPal::API::TransactionSearch;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.11';
our $CVS_VERSION = '$Id: TransactionSearch.pm,v 1.3 2006/03/24 17:11:37 scott Exp $';
our @EXPORT_OK = qw( TransactionSearch );
sub TransactionSearch {
my $self = shift;
my %args = @_;
my %types = ( StartDate => 'xs:dateTime',
EndDate => 'xs:dateTime',
Payer => 'ebl:EmailAddressType',
Receiver => 'ebl:EmailAddressType',
ReceiptID => 'xs:string',
TransactionID => 'xs:string',
InvoiceID => 'xs:string',
PayerName => 'xs:string',
AuctionItemNumer => 'xs:string',
TransactionClass => '',
Amount => 'ebl:BasicAmountType',
CurrencyCode => 'xs:token',
Status => '',
);
my @trans =
(
$self->version_req,
SOAP::Data->name( StartDate => $args{StartDate} )->type( delete $types{StartDate} )
);
for my $type ( keys %types ) {
next unless $args{$type};
push @trans, SOAP::Data->name( $type => $args{$type} )->type($types{$type});
}
my $request = SOAP::Data->name
( TransactionSearchRequest => \SOAP::Data->value( @trans ) )
->type("ns:TransactionSearchRequestType");
my $som = $self->doCall( TransactionSearchReq => $request )
or return;
my $path = '/Envelope/Body/TransactionSearchResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
return $self->getFieldsList( $som, $path . '/PaymentTransactions',
{ Timestamp => 'Timestamp',
Timezone => 'Timezone',
Type => 'Type',
Payer => 'Payer',
PayerDisplayName => 'PayerDisplayName',
TransactionID => 'TransactionID',
Status => 'Status',
GrossAmount => 'GrossAmount',
FeeAmount => 'FeeAmount',
NetAmount => 'NetAmount',
} );
}
1;
__END__
=head1 NAME
Business::PayPal::API::TransactionSearch - PayPal TransactionSearch API
=head1 SYNOPSIS
use Business::PayPal::API::TransactionSearch;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::TransactionSearch ( ... );
my %response = $pp->TransactionSearch( StartDate => '1998-01-01T00:00:00Z',
TransactionID => $transid, );
=head1 DESCRIPTION
B<Business::PayPal::API::TransactionSearch> implements PayPal's
B<TransactionSearch> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 TransactionSearch
Implements PayPal's B<TransactionSearch> API call. Supported
parameters include:
StartDate (required)
EndDate
Payer
Receiver
TransactionID
PayerName
AuctionItemNumber
InvoiceID
TransactionClass
Amount
CurrencyCode
Status
as described in the PayPal "Web Services API Reference" document. The
syntax for StartDate is:
YYYY-MM-DDTHH:MM:SSZ
'T' and 'Z' are literal characters 'T' and 'Z' respectively, e.g.:
2005-12-22T08:51:28Z
Returns a list reference containing up to 100 matching records (as per
the PayPal Web Services API). Each record is a hash reference with the
following fields:
Timestamp
Timezone
Type
Payer
PayerDisplayName
TransactionID
Status
GrossAmount
FeeAmount
NetAmount
Example:
my $records = $pp->TransactionSearch( StartDate => '2006-03-21T22:29:55Z',
InvoiceID => '599294993', );
for my $rec ( @$records ) {
print "Record:\n";
print "TransactionID: " . $rec->{TransactionID} . "\n";
print "Payer Email: " . $rec->{Payer} . "\n";
print "Amount: " . $rec->{GrossAmount} . "\n\n";
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Scot Wiersdorf E<lt>scott@perlcode.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Scott Wiersdorf
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

View file

@ -0,0 +1,136 @@
package Business::PayPal::API::VoidRequest;
use 5.008001;
use strict;
use warnings;
use SOAP::Lite 0.67;
use Business::PayPal::API ();
our @ISA = qw(Business::PayPal::API);
our $VERSION = '0.12';
our $CVS_VERSION = '$Id: VoidRequest.pm,v 1.2 2007/09/27 20:32:32 scott Exp $';
our @EXPORT_OK = qw(DoVoidRequest);
sub DoVoidRequest {
my $self = shift;
my %args = @_;
my %types = ( AuthorizationID => 'xs:string',
Note => 'xs:string', );
my @ref_trans =
(
$self->version_req,
SOAP::Data->name( AuthorizationID => $args{AuthorizationID} )->type($types{AuthorizationID}),
);
if ($args{Note}) {
push @ref_trans,
SOAP::Data->name( Note => $args{Note} )->type( $types{Note} )
if $args{Note};
}
my $request = SOAP::Data->name
( DoVoidRequest => \SOAP::Data->value( @ref_trans ) )
->type("ns:VoidRequestType");
my $som = $self->doCall( DoVoidReq => $request )
or return;
my $path = '/Envelope/Body/DoVoidResponse';
my %response = ();
unless( $self->getBasic($som, $path, \%response) ) {
$self->getErrors($som, $path, \%response);
return %response;
}
$self->getFields($som, $path, \%response,
{ AuthorizationID => 'AuthorizationID' }
);
return %response;
}
1;
__END__
=head1 NAME
Business::PayPal::API::VoidRequest - PayPal VoidRequest API
=head1 SYNOPSIS
use Business::PayPal::API::VoidRequest;
## see Business::PayPal::API documentation for parameters
my $pp = new Business::PayPal::API::VoidRequest ( ... );
my %response = $pp->DoVoidRequest( AuthorizationID => $transid
Note => "Please come again!" );
=head1 DESCRIPTION
B<Business::PayPal::API::VoidRequest> implements PayPal's
B<VoidRequest> API using SOAP::Lite to make direct API calls to
PayPal's SOAP API server. It also implements support for testing via
PayPal's I<sandbox>. Please see L<Business::PayPal::API> for details
on using the PayPal sandbox.
=head2 DoVoidRequest
Implements PayPal's B<DoVoidRequest> API call. Supported
parameters include:
AuthorizationID
Note
The B<AuthorizationID> is the original ID. Not a subsequent ID from a
B<ReAuthorizationRequest>. The note is a 255 character message for
whatever purpose you deem fit.
Returns a hash containing the results of the transaction. The B<Ack>
element is likely the only useful return value at the time of this
revision (the Nov. 2005 errata to the Web Services API indicates that
the documented fields 'AuthorizationID', 'GrossAmount', etc. are I<not>
returned with this API call).
Example:
my %resp = $pp->DoVoidRequest( AuthorizationID => $trans_id,
Note => 'Sorry about that.' );
unless( $resp{Ack} ne 'Success' ) {
for my $error ( @{$response{Errors}} ) {
warn "Error: " . $error->{LongMessage} . "\n";
}
}
=head2 ERROR HANDLING
See the B<ERROR HANDLING> section of B<Business::PayPal::API> for
information on handling errors.
=head2 EXPORT
None by default.
=head1 SEE ALSO
L<https://developer.paypal.com/en_US/pdf/PP_APIReference.pdf>
=head1 AUTHOR
Danny Hembree E<lt>danny@dynamical.orgE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2006 by Danny Hembree
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.5 or,
at your option, any later version of Perl 5 you may have available.
=cut

53
t/API.pl Normal file
View file

@ -0,0 +1,53 @@
=pod
The tester must supply their own PayPal sandbox seller authentication
(either using certificates or 3-token auth), as well as the buyer
sandbox account (email address).
Should we set env variables, prompt for them, or have them in a conf
file? Prompt for them, but we should allow for an input file as an env
variable:
WPP_TEST=auth.txt make test
=cut
sub do_args {
unless( $ENV{WPP_TEST} && -f $ENV{WPP_TEST} ) {
die "See the TESTING section in `perldoc Business::PayPal::API documentation`\n";
exit;
}
my %args = ();
open FILE, "<", $ENV{WPP_TEST}
or die "Could not open $ENV{WPP_TEST}: $!\n";
my @variables = qw( Username Password Signature Subject timeout
CertFile KeyFile PKCS12File PKCS12Password
BuyerEmail
);
my %patterns = ();
@patterns{map { qr/^$_\b/i } @variables} = @variables;
while( <FILE> ) {
chomp;
MATCH: for my $pat (keys %patterns) {
next unless $_ =~ $pat;
(my $value = $_) =~ s/$pat\s*=\s*(.+)/$1/;
$args{ $patterns{$pat} } = $value;
delete $patterns{$pat};
last MATCH;
}
}
close FILE;
## leave this!
$args{sandbox} = 1;
return %args;
}
1;

5
t/Business-PayPal-API.t Normal file
View file

@ -0,0 +1,5 @@
use Test::More tests => 1;
BEGIN { use_ok('Business::PayPal::API') };
#########################

199
t/DirectPayments.t Normal file
View file

@ -0,0 +1,199 @@
#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => '9';
}
use Business::PayPal::API qw( DirectPayments CaptureRequest ReauthorizationRequest VoidRequest RefundTransaction );
my @methlist = qw( DirectPayments CaptureRequest ReauthorizationRequest VoidRequest RefundTransaction);
use_ok('Business::PayPal::API', @methlist);
require 't/API.pl';
my %args = do_args();
my ($transale,$tranvoid,$tranbasic,$tranrefund);
my ($ppsale,$ppvoid,$ppbasic,$pprefund,$pprefund1,$ppcap,$ppcap1);
my (%respsale,%resprefund,%resprefund1,%respbasic,%respcap,%respcap1,%respvoid);
#Test Partial Refund on Sale
#$Business::PayPal::API::Debug=1;
$ppsale = new Business::PayPal::API(%args);
%respsale = $ppsale->DoDirectPaymentRequest (
PaymentAction => 'Sale',
OrderTotal => 13.87,
TaxTotal => 0.0,
ItemTotal => 0.0,
CreditCardType => 'Visa',
CreditCardNumber => '4561435600988217',
ExpMonth => '01',
ExpYear => +(localtime)[5]+1901,
CVV2 => '123',
FirstName => 'JP',
LastName => 'Morgan',
Street1 => '1st Street LaCausa',
Street2 => '',
CityName => 'La',
StateOrProvince => 'Ca',
PostalCode => '90210',
Country => 'US',
Payer => 'mall@example.org',
CurrencyID => 'USD',
IPAddress => '10.0.0.1',
MerchantSessionID => '10113301',
);
#$Business::PayPal::API::Debug=0;
if(is( $respsale{'Ack'}, 'Success', 'Direct Payment Sale')) {
$transale = $respsale{'TransactionID'};
#$Business::PayPal::API::Debug=1;
$pprefund = new Business::PayPal::API(%args);
%resprefund = $pprefund->RefundTransaction (
TransactionID => $transale,
RefundType => 'Partial',
Amount => '3.00',
Memo => 'Partial three dollar refund',
);
#$Business::PayPal::API::Debug=0;
is( $resprefund{'Ack'}, 'Success', 'Partial Refund for sale');
}
#Test Full Refund on Sale
#$Business::PayPal::API::Debug=1;
$ppsale = new Business::PayPal::API(%args);
%respsale = $ppsale->DoDirectPaymentRequest (
PaymentAction => 'Sale',
OrderTotal => 13.87,
TaxTotal => 0.0,
ItemTotal => 0.0,
CreditCardType => 'Visa',
CreditCardNumber => '4561435600988217',
ExpMonth => '01',
ExpYear => +(localtime)[5]+1901,
CVV2 => '123',
FirstName => 'JP',
LastName => 'Morgan',
Street1 => '1st Street LaCausa',
Street2 => '',
CityName => 'La',
StateOrProvince => 'Ca',
PostalCode => '90210',
Country => 'US',
Payer => 'mall@example.org',
CurrencyID => 'USD',
IPAddress => '10.0.0.1',
MerchantSessionID => '10113301',
);
#$Business::PayPal::API::Debug=0;
if(is( $respsale{'Ack'}, 'Success', 'Direct Payment Sale')) {
$transale = $respsale{'TransactionID'};
#$Business::PayPal::API::Debug=1;
$pprefund1 = new Business::PayPal::API(%args);
%resprefund1 = $pprefund1->RefundTransaction (
TransactionID => $transale,
RefundType => 'Full',
Memo => 'Full refund',
);
#$Business::PayPal::API::Debug=0;
is( $resprefund1{'Ack'}, 'Success', 'Full Refund for sale');
}
#Basic Authorization and Capture
%args=do_args();
#$Business::PayPal::API::Debug=0;
$ppbasic = new Business::PayPal::API(%args);
%respbasic = $ppbasic->DoDirectPaymentRequest (
PaymentAction => 'Authorization',
OrderTotal => 13.87,
TaxTotal => 0.0,
ItemTotal => 0.0,
CreditCardType => 'Visa',
CreditCardNumber => '4561435600988217',
ExpMonth => '01',
ExpYear => +(localtime)[5]+1901,
CVV2 => '123',
FirstName => 'JP',
LastName => 'Morgan',
Street1 => '1st Street LaCausa',
Street2 => '',
CityName => 'La',
StateOrProvince => 'Ca',
PostalCode => '90210',
Country => 'US',
Payer => 'mall@example.org',
CurrencyID => 'USD',
IPAddress => '10.0.0.1',
MerchantSessionID => '10113301',
);
#$Business::PayPal::API::Debug=0;
if( is( $respbasic{'Ack'}, 'Success', 'Direct Payment Basic Authorization') ) {
$tranbasic = $respbasic{'TransactionID'};
#Test Partial Capture
#$Business::PayPal::API::Debug=1;
$ppcap = new Business::PayPal::API(%args);
%respcap = $ppcap->DoCaptureRequest (
AuthorizationID => $tranbasic,
CompleteType => 'NotComplete',
Amount => '3.00',
Note => 'Partial Capture',
);
#$Business::PayPal::API::Debug=0;
is( $respcap{'Ack'}, 'Success', 'Partial Capture');
#Test Full Capture
#$Business::PayPal::API::Debug=1;
$ppcap1 = new Business::PayPal::API(%args);
%respcap1 = $ppcap1->DoCaptureRequest (
AuthorizationID => $tranbasic,
CompleteType => 'Complete',
Amount => '6.00',
);
#$Business::PayPal::API::Debug=0;
is( $respcap1{'Ack'}, 'Success', 'Full Capture');
}
else { skip( "direct payment auth failed", 2 ) }
#Test Void
$ppbasic = new Business::PayPal::API(%args);
%respbasic = $ppbasic->DoDirectPaymentRequest (
PaymentAction => 'Authorization',
OrderTotal => 18.37,
TaxTotal => 0.0,
ItemTotal => 0.0,
CreditCardType => 'Visa',
CreditCardNumber => '4561435600988217',
ExpMonth => '01',
ExpYear => +(localtime)[5]+1901,
CVV2 => '123',
FirstName => 'JP',
LastName => 'Morgan',
Street1 => '1st Street LaCausa',
Street2 => '',
CityName => 'La',
StateOrProvince => 'Ca',
PostalCode => '90210',
Country => 'US',
Payer => 'mall@example.org',
CurrencyID => 'USD',
IPAddress => '10.0.0.1',
MerchantSessionID => '10113301',
);
#$Business::PayPal::API::Debug=1;
$ppvoid = new Business::PayPal::API(%args);
%respvoid = $ppvoid->DoVoidRequest ( AuthorizationID => $respbasic{TransactionID},
Note => 'Authorization Void', );
#$Business::PayPal::API::Debug=0;
is( $respvoid{'Ack'}, 'Success', 'Authorization Voided');

92
t/ExpressCheckout.t Normal file
View file

@ -0,0 +1,92 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 6;
}
use_ok( 'Business::PayPal::API::ExpressCheckout' );
#########################
require 't/API.pl';
my %args = do_args();
## we're passing more to new() than we normally would because we're
## using %args elsewhere below. See documentation for the correct
## arguments.
my $pp = new Business::PayPal::API::ExpressCheckout( %args );
##
## set checkout info
##
#$Business::PayPal::API::Debug = 1;
my %response = $pp->SetExpressCheckout
( OrderTotal => '55.43',
ReturnURL => 'http://www.google.com/',
CancelURL => 'http://www.google.com/',
Custom => "This field is custom. Isn't that great?",
PaymentAction => 'Sale',
BuyerEmail => $args{BuyerEmail}, ## from %args
);
#$Business::PayPal::API::Debug = 0;
my $token = $response{Token};
ok( $token, "Got token" );
die "No token from PayPal! Check your authentication information and try again."
unless $token;
my $pp_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=$token";
print STDERR <<"_TOKEN_";
Now paste the following URL into your browser (you'll need to have
another browser window already logged into the PayPal developer site):
$pp_url
Login to PayPal as the Buyer you specified in '$ENV{WPP_TEST}' and
proceed to checkout (this authorizes the transaction represented by
the token). When finished, PayPal will redirect you to a non-existent
URL:
http://localhost/return.html?token=$token&PayerID=XXXXXXXXXXXXX
Notice the *PayerID* URL argument (XXXXXXXXXXXXX) on the redirect from
PayPal.
_TOKEN_
print STDERR "\nType or paste that PayerID here and hit Enter: \n";
my $payerid = <STDIN>; chomp $payerid;
die "Need a PayerID.\n" unless $payerid;
##
## get checkout details
##
my %details = $pp->GetExpressCheckoutDetails($token);
is( $details{Token}, $token, "details ok" );
#use Data::Dumper;
#print STDERR Dumper \%details;
$details{PayerID} = $payerid;
my %payment = ( Token => $details{Token},
PaymentAction => 'Sale',
PayerID => $details{PayerID},
OrderTotal => '55.43',
);
##
## do checkout
##
my %payinfo = $pp->DoExpressCheckoutPayment(%payment);
is( $payinfo{Ack}, 'Success', "successful payment" );
is( $payinfo{Token}, $token, "payment ok" );
is( $payinfo{GrossAmount}, 55.43, "amount correct" );

112
t/ExpressOrder.t Normal file
View file

@ -0,0 +1,112 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 8;
}
use_ok( 'Business::PayPal::API::ExpressCheckout' );
#########################
require 't/API.pl';
my %args = do_args();
## we're passing more to new() than we normally would because we're
## using %args elsewhere below. See documentation for the correct
## arguments.
my $pp = new Business::PayPal::API::ExpressCheckout( %args );
##
## set checkout info
##
#$Business::PayPal::API::Debug = 1;
my %response = $pp->SetExpressCheckout
( OrderTotal => '55.43',
ReturnURL => 'http://www.google.com/',
CancelURL => 'http://www.google.com/',
Custom => "This field is custom. Isn't that great?",
PaymentAction => 'Order',
BuyerEmail => $args{BuyerEmail}, ## from %args
);
#$Business::PayPal::API::Debug = 0;
my $token = $response{Token};
ok( $token, "Got token" );
die "No token from PayPal! Check your authentication information and try again."
unless $token;
my $pp_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=$token";
print STDERR <<"_TOKEN_";
Now paste the following URL into your browser (you will need to have
another browser window already logged into the PayPal developer site):
$pp_url
Login to PayPal as the Buyer you specified in '$ENV{WPP_TEST}' and
proceed to checkout (this authorizes the transaction represented by
the token). When finished, PayPal will redirect you to a non-existent
URL:
http://localhost/return.html?token=$token&PayerID=XXXXXXXXXXXXX
Notice the *PayerID* URL argument (XXXXXXXXXXXXX) on the redirect from
PayPal.
Once completed, The Payer account and Payee account can be checked for an order,
authorization, and void.
_TOKEN_
print STDERR "\nType or paste that PayerID here and hit Enter: \n";
my $payerid = <STDIN>; chomp $payerid;
die "Need a PayerID.\n" unless $payerid;
##
## get checkout details
##
my %details = $pp->GetExpressCheckoutDetails($token);
is( $details{Token}, $token, "details ok" );
#use Data::Dumper;
#print STDERR Dumper \%details;
$details{PayerID} = $payerid;
my %payment = ( Token => $details{Token},
PaymentAction => 'Order',
PayerID => $details{PayerID},
OrderTotal => '55.43',
);
##
## do checkout
##
#$Business::PayPal::API::Debug = 1;
my %payinfo = $pp->DoExpressCheckoutPayment(%payment);
#$Business::PayPal::API::Debug = 0;
#If Order is successful then authorize it, then void it.
if(is( $payinfo{Ack}, 'Success', "successful payment" )) {
my $transid= $payinfo{TransactionID};
my $amount= '25.43';
use_ok('Business::PayPal::API::AuthorizationRequest');
%args = do_args();
#$Business::PayPal::API::Debug = 1;
$ppauth = new Business::PayPal::API::AuthorizationRequest(%args);
my %resp = $ppauth->DoAuthorizationRequest( TransactionID => $transid,
Amount => $amount);
is( $resp{Ack}, 'Success', 'Successful order authorization' );
use_ok( 'Business::PayPal::API::VoidRequest' );
%args = do_args();
my $ppvoid= new Business::PayPal::API::VoidRequest( %args );
%resp1= $ppvoid->DoVoidRequest( AuthorizationID => $transid,
Note => 'Voided' );
is( $resp1{Ack}, 'Success', 'Successful order void' );
}

44
t/GetTransactionDetails.t Normal file
View file

@ -0,0 +1,44 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 2;
}
use_ok( 'Business::PayPal::API::GetTransactionDetails' );
#########################
require 't/API.pl';
my %args = do_args();
my $pp = new Business::PayPal::API::GetTransactionDetails( %args );
print STDERR <<"_TRANSID_";
Please login to the PayPal Developer's site, and start a sandbox in
the Business account you want to test.
Review the Business accounts transaction history:
My Account -> History (tab)
Click the 'Details' link for a transaction whose status is
'Completed'. Copy the Transaction ID for that transaction. The
transaction id may appear like this:
Express Checkout Payment Received (ID # 2DE2563K55B16978M)
_TRANSID_
print STDERR "\nType or paste that Transaction ID here and hit Enter: \n";
my $transid = <STDIN>; chomp $transid;
die "Need a transaction id.\n" unless $transid;
#$Business::PayPal::API::Debug = 1;
my %resp = $pp->GetTransactionDetails( TransactionID => $transid );
is( $resp{Ack}, 'Success', "transaction received" );

37
t/MassPay.t Normal file
View file

@ -0,0 +1,37 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 3;
}
use_ok( 'Business::PayPal::API::MassPay' );
#########################
require 't/API.pl';
my %args = do_args();
my $pp = new Business::PayPal::API::MassPay( %args );
#$Business::PayPal::API::Debug = 1;
my %resp = $pp->MassPay( EmailSubject => "This is the subject; nice eh?",
MassPayItems => [ { ReceiverEmail => 'joe@test.tld',
Amount => '24.00',
UniqueID => "123456",
Note => "Enjoy the money. Don't spend it all in one place." } ] );
is( $resp{Ack}, 'Success', "successful payment" );
%resp = $pp->MassPay( EmailSubject => "This is the subject; nice eh?",
MassPayItems => [ { ReceiverEmail => 'bob@test.tld',
Amount => '25.00',
UniqueID => "123457",
Note => "Enjoy the money. Don't spend it all in one place." },
{ ReceiverEmail => 'foo@test.tld',
Amount => '42.00',
UniqueID => "123458",
Note => "Enjoy the money. Don't spend it all in one place." } ] );
is( $resp{Ack}, 'Success', "successful payments" );

123
t/RecurringPayments.t Normal file
View file

@ -0,0 +1,123 @@
# -*- mode: cperl -*-
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 4;
}
use_ok( 'Business::PayPal::API::RecurringPayments' );
#########################
require 't/API.pl';
my %args = do_args();
my $pp = new Business::PayPal::API::RecurringPayments(%args);
$Business::PayPal::API::Debug = 1;
my %response = $pp->SetCustomerBillingAgreement
(
BillingType => 'RecurringPayments',
ReturnURL => 'http://www.google.com/',
CancelURL => 'http://www.google.com/',
BuyerEmail => $args{BuyerEmail},
);
$Business::PayPal::API::Debug = 0;
my $token = $response{Token};
ok( $token, "Got token" );
is( $response{Ack}, 'Success', "SetCustomerBillingAgreement successful" );
exit;
die "No token from PayPal! Check your authentication information and try again."
unless $token;
my $pp_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_customer-billing-agreement&token=$token";
=pod
print STDERR <<"_TOKEN_";
Now paste the following URL into your browser (you'll need to have
another browser window already logged into the PayPal developer site):
$pp_url
Login to PayPal as the Buyer you specified in '$ENV{WPP_TEST}' and
proceed to checkout (this authorizes the transaction represented by
the token). When finished, PayPal will redirect you to a non-existent
URL:
http://localhost/return.html?token=$token&PayerID=XXXXXXXXXXXXX
Notice the *PayerID* URL argument (XXXXXXXXXXXXX) on the redirect from
PayPal.
_TOKEN_
print STDERR "\nType or paste that PayerID here and hit Enter: \n";
my $payerid = <STDIN>; chomp $payerid;
die "Need a PayerID.\n" unless $payerid;
=cut
## CreateRecurringPaymentsProfile
$Business::PayPal::API::Debug = 1;
my %profile = $pp->CreateRecurringPaymentsProfile
( Token => $token,
## RecurringPaymentProfileDetails
SubscriberName => 'Joe Schmoe',
SubscriberShipperName => "Schmoe House",
SubscriberShipperStreet1 => '1234 Street St.',
SubscriberShipperCityName => 'Orem',
SubscriberShipperStateOrProvince => 'UT',
SubscriberShipperPostalCode => '84222',
SubscriberShipperCountry => 'USA',
SubscriberShipperPhone => '123-123-1234',
BillingStartDate => '12-31-08',
ProfileReference => 'BH12341234',
## ScheduleDetails
Description => "12 Month Hosting Package: We Love You!",
InitialAmount => '12.34',
TrialBillingPeriod => "Month",
TrialBillingFrequency => 1,
TrialTotalBillingCycles => 1,
TrialAmount => 0.00,
TrialShippingAmount => 0.00,
TrialTaxAmount => 0.00,
PaymentBillingPeriod => "Year",
PaymentBillingFrequency => 1,
PaymentTotalBillingCycles => 1,
PaymentAmount => 95.40,
PaymentShippingAmount => 0.00,
PaymentTaxAmount => 0.00,
MaxFailedPayments => 1,
AutoBillOutstandingAmount => 'AddToNextBilling',
);
$Business::PayPal::API::Debug = 0;
## GetBillingAgreementCustomerDetails
#$Business::PayPal::API::Debug = 1;
my %details = $pp->GetBillingAgreementCustomerDetails($token);
$Business::PayPal::API::Debug = 0;
is( $details{Ack}, "Success", "details ok" );

55
t/RefundTransaction.t Normal file
View file

@ -0,0 +1,55 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 2;
}
use_ok( 'Business::PayPal::API::RefundTransaction' );
#########################
require 't/API.pl';
my %args = do_args();
my $pp = new Business::PayPal::API::RefundTransaction( %args );
print STDERR <<"_TRANSID_";
Please login to the PayPal Developer's site, and start a sandbox in
the Business account you want to test.
Review the Business accounts transaction history:
My Account -> History (tab)
Follow the 'Details' link for a transaction (whose status is
'Completed') that occurred in the past 60 days.
Copy the Transaction ID for that transaction. It may appear like this:
Express Checkout Payment Received (ID # 2DE2563K55B16978M)
_TRANSID_
print STDERR "\nType or paste that Transaction ID here and hit Enter: \n";
my $transid = <STDIN>; chomp $transid;
die "Need a transaction id.\n" unless $transid;
my %resp = $pp->RefundTransaction( TransactionID => $transid,
RefundType => 'Full',
Memo => 'Fancy refund time.' );
is( $resp{Ack}, 'Success', "Successful refund." );
if( $resp{Ack} ) {
print STDERR <<"_REFUND_";
You may now login to your Business sandbox account and verify the
transaction was refunded.
_REFUND_
}

47
t/TransactionSearch.t Normal file
View file

@ -0,0 +1,47 @@
use Test::More;
if( ! $ENV{WPP_TEST} || ! -f $ENV{WPP_TEST} ) {
plan skip_all => 'No WPP_TEST env var set. Please see README to run tests';
}
else {
plan tests => 2;
}
use_ok( 'Business::PayPal::API::TransactionSearch' );
#########################
require 't/API.pl';
my %args = do_args();
my $pp = new Business::PayPal::API::TransactionSearch( %args );
print STDERR <<"_TRANSID_";
Please login to the PayPal Developer's site, and start a sandbox in
the Business account you want to test.
Review the Business accounts transaction history:
My Account -> History (tab)
Click the 'Details' link for a transaction whose status is
'Completed'. Copy the Transaction ID for that transaction. The
transaction id may appear like this:
Express Checkout Payment Received (ID # 2DE2563K55B16978M)
_TRANSID_
print STDERR "\nType or paste that Transaction ID here and hit Enter: \n";
my $transid = <STDIN>; chomp $transid;
die "Need a transaction id.\n" unless $transid;
my $startdate = '1998-01-01T00:00:00Z'; ## before PayPal
#$Business::PayPal::API::Debug = 1;
my $resp = $pp->TransactionSearch( StartDate => $startdate,
TransactionID => $transid, );
ok( scalar @$resp, "matching transaction(s) found" );