Initial git commit of release 0.62
This commit is contained in:
commit
925e625e76
30 changed files with 5489 additions and 0 deletions
178
Changes
Normal file
178
Changes
Normal 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
30
MANIFEST
Normal 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
11
META.yml
Normal 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
12
Makefile.PL
Normal 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
62
README
Normal 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
3
auth.sample.3token
Normal file
|
@ -0,0 +1,3 @@
|
|||
Username = test1_api.mydomain.tld
|
||||
Password = XXXXXXXXXXXXXXXX
|
||||
Signature = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
4
auth.sample.cert
Normal file
4
auth.sample.cert
Normal 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
|
784
eg/paypal-checkout-example.pl
Normal file
784
eg/paypal-checkout-example.pl
Normal 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
800
lib/Business/PayPal/API.pm
Normal 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 '&' and '<' 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&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
|
144
lib/Business/PayPal/API/AuthorizationRequest.pm
Normal file
144
lib/Business/PayPal/API/AuthorizationRequest.pm
Normal 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
|
170
lib/Business/PayPal/API/CaptureRequest.pm
Normal file
170
lib/Business/PayPal/API/CaptureRequest.pm
Normal 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
|
293
lib/Business/PayPal/API/DirectPayments.pm
Normal file
293
lib/Business/PayPal/API/DirectPayments.pm
Normal 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
|
553
lib/Business/PayPal/API/ExpressCheckout.pm
Normal file
553
lib/Business/PayPal/API/ExpressCheckout.pm
Normal 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
|
295
lib/Business/PayPal/API/GetTransactionDetails.pm
Normal file
295
lib/Business/PayPal/API/GetTransactionDetails.pm
Normal 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
|
188
lib/Business/PayPal/API/MassPay.pm
Normal file
188
lib/Business/PayPal/API/MassPay.pm
Normal 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
|
143
lib/Business/PayPal/API/ReauthorizationRequest.pm
Normal file
143
lib/Business/PayPal/API/ReauthorizationRequest.pm
Normal 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
|
579
lib/Business/PayPal/API/RecurringPayments.pm
Normal file
579
lib/Business/PayPal/API/RecurringPayments.pm
Normal 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
|
158
lib/Business/PayPal/API/RefundTransaction.pm
Normal file
158
lib/Business/PayPal/API/RefundTransaction.pm
Normal 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
|
179
lib/Business/PayPal/API/TransactionSearch.pm
Normal file
179
lib/Business/PayPal/API/TransactionSearch.pm
Normal 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
|
136
lib/Business/PayPal/API/VoidRequest.pm
Normal file
136
lib/Business/PayPal/API/VoidRequest.pm
Normal 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
53
t/API.pl
Normal 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
5
t/Business-PayPal-API.t
Normal file
|
@ -0,0 +1,5 @@
|
|||
use Test::More tests => 1;
|
||||
BEGIN { use_ok('Business::PayPal::API') };
|
||||
|
||||
#########################
|
||||
|
199
t/DirectPayments.t
Normal file
199
t/DirectPayments.t
Normal 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
92
t/ExpressCheckout.t
Normal 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
112
t/ExpressOrder.t
Normal 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
44
t/GetTransactionDetails.t
Normal 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
37
t/MassPay.t
Normal 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
123
t/RecurringPayments.t
Normal 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
55
t/RefundTransaction.t
Normal 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
47
t/TransactionSearch.t
Normal 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" );
|
Loading…
Reference in a new issue