http://qs1969.pair.com?node_id=888819


in reply to using Business::PayPal

By automated payment, do you mean recurring? If so, then no, for me, at least.

I did however, across the Christmas break a couple of years back, bust it to deliver for a New Year launch an online order form using this module.

The first distinction it is important to understand is between the PayPal services. Essentially they offer two options: WPS (Website Payments Standard) which hands the transaction off to PayPal for the collection of credit card data, before returning your user to your own site; and WPP (Website Payments Pro), which operates more like a traditional merchant account and is suitable for a PCI compliant, locked down server which accepts credit card data only across an encrypted connection. I wasted all sorts of time before I figured out that one.

Here is the other distinction: WPS will cost you transaction fees. WPP will cost you transaction fees, plus $30 / month. Add another $30 / month if you want to handle recurring transactions through WPP. You can do recurring transactions with WPS, but your clients will be required to have or create a paypal account to do business with you that way. I regularly heard complaints about that from my clients, even before they pulled the rug out from under wikileaks.

At any rate, this is the code I wrote, back when I did the Winter jam:

sub _instantiate_pp_nvp { my $self = shift; if(defined($self->{'pp_nvp'}) && $self->{'pp_nvp'}->isa('Business::P +ayPal::NVP')){ return; } my $pp_branch; if($self->{'cfg'}->param("paypal.pp_use_sandbox")){ $pp_branch = 'test'; } else { $pp_branch = 'live'; } $self->{'pp_nvp'} = Business::PayPal::NVP->new( Version => '57.0', test => { user => $self->{'cfg'}->param("paypal.pp_sandbox_username" +), pwd => $self->{'cfg'}->param("paypal.pp_sandbox_password" +), sig => $self->{'cfg'}->param("paypal.pp_sandbox_signature +") }, live => { user => $self->{'cfg'}->param("paypal.pp_username"), pwd => $self->{'cfg'}->param("paypal.pp_password"), sig => $self->{'cfg'}->param("paypal.pp_signature") }, branch => $pp_branch ); return; } sub _SetExpressCheckout { my $self = shift; my $fields = shift; my $gross_sales = $self->_compute_invoice($fields); $self->_instantiate_pp_nvp(); $fields->{'AMT'} = $gross_sales; $fields->{'DESC'} = $self->{'cfg'}->param("inventory.product_name"); $fields->{'CURRENCYCODE'} = 'USD'; $fields->{'PAYMENTACTION'} = 'Sale'; $fields->{'RETURNURL'} = $self->{'cfg'}->param("paypal.pp_return_url +"); $fields->{'CANCELURL'} = $self->{'cfg'}->param("paypal.pp_cancel_url +"); $fields->{'VERSION'} = $self->{'cfg'}->param("paypal.pp_api_version" +); $fields->{'METHOD'} = 'SetExpressCheckout'; $fields->{'REQCONFIRMSHIPPING'} = '0'; $fields->{'ADDROVERRIDE'} = '0'; $fields->{'NOSHIPPING'} = '0'; $fields->{'ALLOWNOTE'} = '0'; $fields->{'LOCALECODE'} = 'US'; $fields->{'HDRBORDERCOLOR'} = $self->{'cfg'}->param("paypal.pp_hdrbo +rdercolor"); $fields->{'HDRBACKCOLOR'} = $self->{'cfg'}->param("paypal.pp_hdrback +color"); $fields->{'PAYFLOWCOLOR'} = $self->{'cfg'}->param("paypal.pp_payflow +color"); # $fields->{'SOLUTIONTYPE'} = 'Mark'; # $fields->{'LANDINGPAGE'} = 'Login'; # $fields->{'CHANNELTYPE'} = 'Merchant'; my %response = $self->{'pp_nvp'}->SetExpressCheckout( %{$fields} ); # $self->log('DEBUG','->get_paypal_token_nvp() says API server said: + ',\%response); my %details = $self->{'pp_nvp'}->GetExpressCheckoutDetails( 'Token' +=> $response{'TOKEN'} ); $self->{'paypal_details'} = \%details; # $self->log('DEBUG','Express Checkout Details include: ',$self->{'p +aypal_details'}); return %details; } sub _GetExpressCheckoutDetails { my $self = shift; my $token = shift; $self->_instantiate_pp_nvp(); my %response; $response{'TOKEN'} = $token; my %details = $self->{'pp_nvp'}->GetExpressCheckoutDetails( 'Token' +=> $response{'TOKEN'} ); $self->{'paypal_details'} = \%details; print STDERR '->_GetExpressCheckoutDetails() says the details are: ' + . Dumper(\%details); return \%details; } sub _DoExpressCheckoutPayment { my $self = shift; # my $fields = shift; # return unless(defined($self->{'paypal_details'})); # $self->log('DEBUG','$ymd->DoExpressCheckoutPayment got these incom +ing arguments: ',$fields); # my $gross_sales = $self->_compute_invoice($fields); $self->_instantiate_pp_nvp(); my %args = ( token => $self->{'s'}->param('token'), TOKEN => $self->{'s'}->param('token'), PayerID => $self->{'s'}->param('payerid'), PAYERID => $self->{'s'}->param('payerid'), # AMT => '1.00', AMT => $self->{'s'}->{'AMT'}, # OrderTotal => '1.00', OrderTotal => $self->{'s'}->{'AMT'}, CURRENCYCODE => 'USD', PAYMENTACTION => 'Sale', METHOD => 'DoExpressCheckoutPayment' ); print STDERR 'DoExpressCheckoutPayment got these arguments: ' . Dump +er(\%args); my %response = $self->{'pp_nvp'}->DoExpressCheckoutPayment( %args ); print STDERR 'DoExpressCheckoutPayment responds: ' . Dumper(\%respon +se); if($response{'ACK'} eq 'Success' && defined($response{'TRANSACTIONID +'})){ my $sql = $self->{'cfg'}->param("sql.insert_pp_transaction"); my $sth = $self->{'dbh'}->prepare($sql); $sth->execute( $response{'TRANSACTIONID'}, $response{'TOKEN'}, $response{'ACK'}, $response{'PAYMENTSTATUS'}, $response{'AMT'}, $response{'FEEAMT'}, $response{'TAXAMT'}, $response{'CURRENCYCODE'}, $response{'PAYMENTTYPE'}, $response{'TRANSACTIONTYPE'}, $response{'REASONCODE'}, $response{'PENDINGREASON'}, $response{'ORDERTIME'}, $response{'TIMESTAMP'}, $response{'VERSION'}, $response{'BUILD'}, $response{'CORRELATIONID'} ); } return \%response; }
There was other code in that project to interact with the user, to validate their data, to prepare parameters to hand off to templates and such. I wound up coding only the DoExpressCheckoutPayment option, because my client was not ready to incur the monthly overhead for WPP. I'm pretty sure those were all the PayPal interactions I used. I found using their name-value pairs API far easier to wrap my head around than the alternative, whose name escapes me now. I spent much time reading and re-reading their API documentation.

In the best tradition of feature creep, this stand alone form was suddenly required to sport content around it. At the risk of bringing down the wrath of the perl monks, I installed a (php-based) drupal cms to handle that. The next time my client asked to extend the online order form, and now with a cms in place to provide a framework, I ripped out my custom code and installed ubercart and its paypal payment module in its place. In two or four hours (don't really remember now), I had that set up to replicate the functionality of my custom code, providing whatever the additional requested feature may have been, plus all the other features built into ubercart.

Just a week or two ago I have installed ubercart and uc_recurring on my own consulting firm's (drupal-based) site to handle subscriptions for our hosting service and one time payments for other products. It took me a couple of days of reading and a conversation with one of the principal contributors to figure out that drupal's ubercart will handle recurring payments fairly easily with WPP, but that the version available now still requires a patch (available on a comment in the issues queue) to make it work with WPS.

Trust that is helpful.

-- Hugh

if( $lal && $lol ) { $life++; }
if( $insurance->rationing() ) { $people->die(); }