Business-PayPal-API-0.70/000755 000765 000120 00000000000 12050621474 015075 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/auth.sample.3token000644 000765 000120 00000000205 12050620075 020434 0ustar00olafadmin000000 000000 Username = test1_api.mydomain.tld Password = XXXXXXXXXXXXXXXX Signature = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Business-PayPal-API-0.70/auth.sample.cert000644 000765 000120 00000000205 12050620075 020166 0ustar00olafadmin000000 000000 Username = test1_api.mydomain.tld Password = myapipassword CertFile = /www/var/cert_key_pem.txt KeyFile = /www/var/cert_key_pem.txt Business-PayPal-API-0.70/Changes000644 000765 000120 00000016141 12050621100 016355 0ustar00olafadmin000000 000000 Revision history for Perl extension Business::PayPal::API. Release 0.70 - First OALDERS release ------------------------------------- - Fixes RT #70424 (Syntax deprecated in perl v5.14) Release 0.68 - First hembreed release ------------------------------------- I've moved the module's scm to http://github.com/hembreed/Business-PayPal-API so others may share my pain. CPAN will be updated from there. - API.pm 1. Changed eq 'Success' to =~ /Success/ to accomodate variations introduced by PayPal. 2. Changed test for duplicates to test for defined variable first. This was causing errors in 5.10+ perl versions. -t/*t Changed test for Success in all test modules as above -t/RecurringPayments Corrected errors in type definitions and testing. Needs more work. -TransactionSearch Corrected test instructions and code to allow transaction searches using the transactionID. This works for some types of transactions but not others as per PayPal. It is the only way to get MassPayment information. 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 ============================================================================= Business-PayPal-API-0.70/eg/000755 000765 000120 00000000000 12050621473 015467 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/lib/000755 000765 000120 00000000000 12050621473 015642 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/Makefile.PL000644 000765 000120 00000000725 12050620075 017047 0ustar00olafadmin000000 000000 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 ') : ()), ); Business-PayPal-API-0.70/MANIFEST000644 000765 000120 00000001602 12050621474 016225 0ustar00olafadmin000000 000000 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 META.json Module JSON meta-data (added by MakeMaker) Business-PayPal-API-0.70/META.json000644 000765 000120 00000001551 12050621474 016520 0ustar00olafadmin000000 000000 { "abstract" : "PayPal API", "author" : [ "Scott Wiersdorf " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.120630", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Business-PayPal-API", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "runtime" : { "requires" : { "SOAP::Lite" : "0.67" } } }, "release_status" : "stable", "version" : "0.70" } Business-PayPal-API-0.70/META.yml000644 000765 000120 00000000747 12050621473 016355 0ustar00olafadmin000000 000000 --- abstract: 'PayPal API' author: - 'Scott Wiersdorf ' build_requires: ExtUtils::MakeMaker: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 6.62, CPAN::Meta::Converter version 2.120630' license: unknown meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Business-PayPal-API no_index: directory: - t - inc requires: SOAP::Lite: 0.67 version: 0.70 Business-PayPal-API-0.70/README000644 000765 000120 00000003607 12050620075 015757 0ustar00olafadmin000000 000000 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. Business-PayPal-API-0.70/t/000755 000765 000120 00000000000 12050621473 015337 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/t/API.pl000644 000765 000120 00000002343 12050620075 016304 0ustar00olafadmin000000 000000 =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( ) { 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; Business-PayPal-API-0.70/t/Business-PayPal-API.t000644 000765 000120 00000000142 12050620075 021104 0ustar00olafadmin000000 000000 use Test::More tests => 1; BEGIN { use_ok('Business::PayPal::API') }; ######################### Business-PayPal-API-0.70/t/DirectPayments.t000644 000765 000120 00000014501 12050620075 020455 0ustar00olafadmin000000 000000 #!/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 => '7'; } 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 Full Refund on Sale #$Business::PayPal::API::Debug=1; $ppsale = new Business::PayPal::API(%args); %respsale = $ppsale->DoDirectPaymentRequest ( PaymentAction => 'Sale', OrderTotal => 11.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(like( $respsale{'Ack'}, qr/Success/, 'Direct Payment Sale')) { $transale = $respsale{'TransactionID'}; #$Business::PayPal::API::Debug=1; $pprefund = new Business::PayPal::API(%args); %resprefund = $pprefund->RefundTransaction ( TransactionID => $transale, RefundType => 'Full', Memo => 'Full direct sale refund', ); #$Business::PayPal::API::Debug=0; like( $resprefund{'Ack'}, qr/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( like( $respbasic{'Ack'}, qr/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; like( $respcap{'Ack'}, qr/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; like( $respcap1{'Ack'}, qr/Success/, 'Full Capture'); } else { skip( "direct payment auth failed", 2 ) } #Test Void $ppbasic = new Business::PayPal::API(%args); %respbasic = $ppbasic->DoDirectPaymentRequest ( PaymentAction => 'Authorization', OrderTotal => 17.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; like( $respvoid{'Ack'}, qr/Success/, 'Authorization Voided'); Business-PayPal-API-0.70/t/ExpressCheckout.t000644 000765 000120 00000004646 12050620075 020652 0ustar00olafadmin000000 000000 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 = ; 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); like( $payinfo{Ack}, qr/Success/ , "successful payment" ); is( $payinfo{Token}, $token, "payment ok" ); is( $payinfo{GrossAmount}, 55.43, "amount correct" ); Business-PayPal-API-0.70/t/ExpressOrder.t000644 000765 000120 00000006637 12050620075 020162 0ustar00olafadmin000000 000000 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 = ; 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(like( $payinfo{Ack}, qr/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); like( $resp{Ack}, qr/Succes/ , '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' ); like( $resp1{Ack}, qr/Success/, 'Successful order void' ); } Business-PayPal-API-0.70/t/GetTransactionDetails.t000644 000765 000120 00000002207 12050620075 021755 0ustar00olafadmin000000 000000 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 = ; chomp $transid; die "Need a transaction id.\n" unless $transid; #$Business::PayPal::API::Debug = 1; my %resp = $pp->GetTransactionDetails( TransactionID => $transid ); like( $resp{Ack}, qr/Success/ , "transaction received" ); Business-PayPal-API-0.70/t/MassPay.t000644 000765 000120 00000003053 12050620075 017077 0ustar00olafadmin000000 000000 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." } ] ); like( $resp{Ack}, qr/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." } ] ); like( $resp{Ack}, qr/Success/ , "successful payments" ); Business-PayPal-API-0.70/t/RecurringPayments.t000644 000765 000120 00000006502 12050620075 021205 0ustar00olafadmin000000 000000 # -*- 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 => 3; } 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', BillingAgreementDescription => '10.00 per month for 1 year', 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" ); like( $response{Ack}, qr/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 = ; 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 => '2009-12-01Z', 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; like( $details{Ack}, qr/Success/ , "details ok" ); Business-PayPal-API-0.70/t/RefundTransaction.t000644 000765 000120 00000002615 12050620075 021156 0ustar00olafadmin000000 000000 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 = ; chomp $transid; die "Need a transaction id.\n" unless $transid; my %resp = $pp->RefundTransaction( TransactionID => $transid, RefundType => 'Full', Memo => 'Fancy refund time.' ); like( $resp{Ack}, qr/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_ } Business-PayPal-API-0.70/t/TransactionSearch.t000644 000765 000120 00000002746 12050620075 021145 0ustar00olafadmin000000 000000 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. This function will allow you to view MassPayment transactions by transaction ID's. It may fail for other types of transaction ID's. The GetTransactionDetails can be used in those cases but not for Mass Payment transactions. I know, I'm just the messenger. Review the Business accounts transaction history: My Account -> History (tab) Click the 'Details' link for a Mass Payment transaction whose status is 'Processed'. Go to the bottom of the page and click "View Detail". It will give you download choices. Download it however you prefer the copy the Transaction ID from the message. _TRANSID_ print STDERR "\nType or paste that Transaction ID here and hit Enter: \n"; my $transid = ; chomp $transid; die "Need a transaction id.\n" unless $transid; my $startdate = '1998-01-01T01:45:10.00Z'; #$Business::PayPal::API::Debug = 1; my $resp = $pp->TransactionSearch( StartDate => $startdate, TransactionID => $transid, ); ok( scalar @$resp , "Matching Transactions Found"); Business-PayPal-API-0.70/lib/Business/000755 000765 000120 00000000000 12050621473 017435 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/lib/Business/PayPal/000755 000765 000120 00000000000 12050621473 020623 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/lib/Business/PayPal/API/000755 000765 000120 00000000000 12050621473 021234 5ustar00olafadmin000000 000000 Business-PayPal-API-0.70/lib/Business/PayPal/API.pm000644 000765 000120 00000062333 12050621100 021564 0ustar00olafadmin000000 000000 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.70'; 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 if( defined $response{TransactionID}) { if( $trans_id{$response{TransactionID}}) { next; } else { $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: ## ## 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. ## ## ## 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} =~ /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 supports both certificate authentication and the new 3-token "Signature" authentication. It also support PayPal's development I for testing. See the B parameter to B below for details. B can import other B 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 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 subclasses, see each subclass's individual documentation. =head2 new Creates a new B 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 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 and I for details on creating a PayPal business account and sandbox account for testing. =item B 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 Optional. This is used by PayPal to authenticate 3rd party billers using your account. See the documents in L. =item B 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 Required for PKCS#12 certificate authentication, unless the B environment variable is already set. This contains the path to your private key for PayPal authentication. It is used to set the B environment variable. You may set this environment variable yourself and leave this field blank. =item B Required for PKCS#12 certificate authentication, unless the B environment variable is already set. This contains the PKCS#12 password for the key specified in B. It is used to set the B environment variable. You may set this environment variable yourself and leave this field blank. =item B 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 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 and B to the same path. =item B 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 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 and B to the same path. =item B Required. If set to true (default), B 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 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 for references. PayPal's sandbox reference: L =back =head1 ERROR HANDLING Every API call should return an B response, whether I, I, or otherwise (depending on the API call). If it returns any non-success value, you can find an I 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 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 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 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 parameters or B or B parameters in your constructor AND that none of the corresponding B 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 => '' 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 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, let me know. =head1 DEVELOPMENT If you are a developer wanting to extend B for other PayPal API calls, you can review any of the included modules (e.g., F or F) 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 used by B (we don't load Exporter at all): it is a special variable used by B to know which methods to import when B is run like this: use Business::PayPal::API qw( SomeAPI ); That is, B 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 has kindly donated some example code (in German) for the ExpressCheckout API which may be found in the F directory of this archive. Additional code examples for other APIs may be found in the F 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 and similar fudging may be found in B. 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 * for finding some API typos in the ExpressCheckout API =item * Andy Spiegl 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 for the heads-up on the newest 3-token auth URI as well as a pile of documentation inconsistencies. =item * Michael Hendricks 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 parameter. =item * Jonathon Wright via email for patches for B and B that implement I and I API calls. =back =head1 SEE ALSO L, L, L, L, L =head1 AUTHOR Scott Wiersdorf, Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/AuthorizationRequest.pm000644 000765 000120 00000007666 12050620075 026017 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L 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 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 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 section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Danny Hembree Edanny@dynamical.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/CaptureRequest.pm000644 000765 000120 00000012505 12050620075 024546 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 CaptureRequest Implements PayPal's B 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 setting is 'USD' if not otherwise specified. The default B 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 section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Danny Hembree Edanny-hembree@dynamical.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/DirectPayments.pm000644 000765 000120 00000027677 12050620075 024545 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 DoDirectPaymentRequest Implements PayPal's B 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 element and TransactionID are the most useful return values. Example: my %resp = $pp->DoDirectPaymentRequest( PaymentAction => 'Sale', OrderTotal => '10.99', ... ); unless( $resp{Ack} !~ /Success/ ) { for my $error ( @{$response{Errors}} ) { warn "Error: " . $error->{LongMessage} . "\n"; } } =head2 ERROR HANDLING See the B section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Daniel Hembree Edanny@dynamical.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/ExpressCheckout.pm000644 000765 000120 00000041347 12050620075 024717 0ustar00olafadmin000000 000000 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 implements PayPal's B using SOAP::Lite to make direct API calls to PayPal's SOAP API server. It also implements support for testing via PayPal's I. Please see L for details on using the PayPal sandbox. =head2 SetExpressCheckout Implements PayPal's "Website Payment Pro" B 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 => '', CancelURL => 'http://example.com', PaymentAction => 'Authorization', AddressOverride => 1, Address => $address, ); =head2 GetExpressCheckoutDetails Implements PayPal's WPP B 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. 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 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 section of B for information on handling errors. =head1 EXAMPLES Andy Spiegl has kindly donated some example code (in German) which may be found in the F directory of this archive. Additional code examples may be found in the F test directory. =head2 EXPORT None by default. =head1 SEE ALSO L, L, L, L =head1 AUTHOR Scott Wiersdorf, Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/GetTransactionDetails.pm000644 000765 000120 00000023573 12050620075 026034 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 GetTransactionDetails Implements PayPal's B 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 section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Scot Wiersdorf Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/MassPay.pm000644 000765 000120 00000012503 12050620075 023145 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 MassPay Implements PayPal's B API call. Supported parameters include: EmailSubject MassPayItems The B 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} !~ /Success/ ) { die "Failed: " . $resp{Errors}[0]{LongMessage} . "\n"; } =head2 ERROR HANDLING See the B section of B for information on handling errors. =head1 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Scot Wiersdorf Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/ReauthorizationRequest.pm000644 000765 000120 00000007653 12050620075 026342 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 DoReauthorizationRequest Implements PayPal's B 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 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} !~ /Success/ ) { for my $error ( @{$response{Errors}} ) { warn "Error: " . $error->{LongMessage} . "\n"; } } =head2 ERROR HANDLING See the B section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Danny Hembree Edanny@dynamical.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/RecurringPayments.pm000644 000765 000120 00000052737 12050620075 025266 0ustar00olafadmin000000 000000 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 ( $self->version_req, #$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 => 'xs:string', ##'ns:BillingPeriodType', TrialBillingFrequency => 'xs:int', TrialTotalBillingCycles => 'xs:int', TrialAmount => 'cc:AmountType', TrialShippingAmount => 'cc:AmountType', TrialTaxAmount => 'cc:AmountType', ); my %paymentbilltype = ( PaymentBillingPeriod => 'xs:string', ##'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 => 'ebl:UserIDType', CCPayerStatus => 'xs:string', CCPayerName => 'xs:string', CCPayerCountry => 'xs:string', CCPayerPhone => 'xs:string', CCPayerBusiness => 'xs:string', CCAddress => 'xs:string', ); ## 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:int', ); ## 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 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 =head1 AUTHOR Scot Wiersdorf Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/RefundTransaction.pm000644 000765 000120 00000011146 12050620075 025223 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 RefundTransaction Implements PayPal's B 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 setting is 'USD' if not otherwise specified. The default B setting is 'Full' if not otherwise specified. If B is set to 'Full', B is ignored (even if set). If B is set to 'Partial', B is required. Returns a hash containing the results of the transaction. The B 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 returned with this API call). Example: my %resp = $pp->RefundTransaction( TransactionID => $trans_id, RefundType => 'Partial', Amount => '15.00', ); unless( $resp{Ack} !~ /Success/ ) { for my $error ( @{$response{Errors}} ) { warn "Error: " . $error->{LongMessage} . "\n"; } } =head2 ERROR HANDLING See the B section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Scot Wiersdorf Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/TransactionSearch.pm000644 000765 000120 00000012042 12050620075 025201 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 TransactionSearch Implements PayPal's B 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 section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Scot Wiersdorf Escott@perlcode.orgE =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 Business-PayPal-API-0.70/lib/Business/PayPal/API/VoidRequest.pm000644 000765 000120 00000006763 12050620075 024055 0ustar00olafadmin000000 000000 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 implements PayPal's B 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. Please see L for details on using the PayPal sandbox. =head2 DoVoidRequest Implements PayPal's B API call. Supported parameters include: AuthorizationID Note The B is the original ID. Not a subsequent ID from a B. The note is a 255 character message for whatever purpose you deem fit. Returns a hash containing the results of the transaction. The B 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 returned with this API call). Example: my %resp = $pp->DoVoidRequest( AuthorizationID => $trans_id, Note => 'Sorry about that.' ); unless( $resp{Ack} !~ /Success/ ) { for my $error ( @{$response{Errors}} ) { warn "Error: " . $error->{LongMessage} . "\n"; } } =head2 ERROR HANDLING See the B section of B for information on handling errors. =head2 EXPORT None by default. =head1 SEE ALSO L =head1 AUTHOR Danny Hembree Edanny@dynamical.orgE =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 Business-PayPal-API-0.70/eg/paypal-checkout-example.pl000644 000765 000120 00000051222 12050620075 022545 0ustar00olafadmin000000 000000 #!/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 [--help|--usage] [--version] [--manpage] [--debug] [--username] [--password] [--signature] [--step] [--OrderTotal] [--OrderDescription] [--InvoiceID] [--BuyerEmail] [--Token] =head1 DESCRIPTION B 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 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 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 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 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, L, L, L, L =head1 AUTHOR Dr. Andy Spiegl Epaypalcheckout.Spiegl@kascada.comE =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