Forum OpenACS Development: First stab at a PayPal package

Posted by Mark Aufflick on
My only real requirement is a convenient way to handle PayPal IPN (Instant Payment Notifications). IPN in a nutshell is PayPal POSTing all the details of a transaction to a pre-agreed URL on your server. To ensure its validity, you then post that back to a PayPal URL which either verifies or rejects it.

My code currently supports only buy now links, which are suitable for my purposes as I just want to charge a single calculated amount at a time.

Before I start committing this package to the CVS tree, I wanted to get a bit of feedback on my implementation approach. You can browse the (undocumented) code on my CVS server:

openacs/packages/ paypal-support is the PayPal package, pump-invoicing is a simple package using paypal-support.

The overall idea is this:

pump-invoicing uses an api-call from paypal-support to generate the PayPal "buy now" link (actually a form snippet):

db_multirow -extend { buy_now } invoice_list invoice_list {
	select invoice_id, description, total, notes
	from invoices
	where organization_id = :organization
	and   deleted = 'f';
} {
	set buy_now [paypal::buy_now \
					 -item_name $description \
					 -amount $total \
					 -no_shipping \
					 -invoice_id $invoice_id \
					 -return_url $return_url \
					 -callback_impl [invoicing::package_key]]
When the customer completes the paypal transaction, PayPal will post the IPN to /paypal-support/ipn-callback - see openacs/packages/paypal-support/www/ipn-callback.tcl (Prepare yourself for a confusion of the word callback - sorry about that, but PayPal does a callback on us, and we do a Tcl callback internally...)

After verifying the IPN, the paypal-support package (optionally) uses the callback mechanism to pass the IPN info back to the client package. In our example, pump-invoicing, here is the callback implementation:

ad_proc -callback paypal::ipn::callback -impl pump-invoicing {
} {
	Implementation of the ipn callback provided by paypal-support
} {
	ns_log Notice "pump-invoicing: received an ipn callback for invoice [ns_set get $ipn_ns_set invoice]"

	return 1
I think that's nice and simple. There are a few nuances - some dealt with, some still to be - with how we need to interact with PayPal, but that's about it.

I'd appreciate it if anyone interested in PayPal could take a look at the code and give me some feedback.

The reason I am using an ns_set to pass in the IPN info is so if PayPal add extra info fields, the callback interface won't have to change.

I am planning to add some helper procs to paypal-support to interrogate the ns_set for standard pieces of information to provide abstraction for the common pieces of information - oh for method calls on ns_sets :)

Posted by Eric Wolfram on
What is the function of this code? I'm not clear on what the user experiences. Do you have a user example set up? Is it simply: User arrives at ISP site and decides to sign up for an account. So they click "buy now" or "subscribe now" and the code generates a paypal invoice (wrapped in your template) and a form that allows them to pay with visa securly?
Posted by Mark Aufflick on
That's the first and easy part.

The second (and not hard, but not quite so easy part) is handling Paypal's IPN (Instant Payment Notification) interface which allows you to verify that a payment actually happened.

My IPN in a nutshell (1st paragraph of original post) isn't that informative, but the beauty of having it abstracted in this sort of way is that you won't need to know anything about it.

Your package would do the following:

1) Implement a tcl callback(s) that you want to run when Paypal has correctly notified you of a payment
2) Use the tcl API to generate the Buy Now link(s) (one of the arguments that you pass to the api is later used to determine which tcl callback to call)

When Paypal sends it's notification, the package will verify it, and then call your requested callback with an ns_set providing all the data from Paypal (total amount, shipping, tax, invoice number etc.)

Two examples of what you might do with it are:

when you get actual notification of the payment,
a) send a request to your warehouse to ship the product
b) have the tcl callback set their user account to verified if you are running a subscription website

Note that IPNs come straight away for a buy now purchase - I believe that if you are using something like subscriptions etc., you will get an IPN every time a payment is made to your account.

Posted by Mark Aufflick on
FYI updated urls. No changes in a long time, but I did migrate to subversion, so the above urls no longer work.


svn url:

trac browse:

pump-invoicing: (example package using paypal-support)

svn url:

trac browse: