I haven't attempted any beautiful abstractions but here is some nuts and bolts code for anyone who's interested:
set success_p 0
set tx [ns_queryget tx]
if { [string equal $tx ""] } {
#No paypal data! Take appropriate action, probably
#return or error.
}
set token "(your token)"
#Retrieve transaction data from paypal.
set qsset [ns_set new qsset]
ns_set put $qsset tx "$tx"
ns_set put $qsset at $token
ns_set put $qsset cmd "_notify-synch"
set results [ns_httpspost https://www.paypal.com/cgi-bin/webscr "" $qsset]
ns_set free $qsset
set vars [list txn_id mc_currency payment_gross first_name last_name payer_email custom]
#Extract transaction data.
foreach line [split $results \n] {
if { [string equal $line SUCCESS] } {
set success_p 1
}
set info [split $line \=]
if { [llength $info] == 2 && [lsearch $vars [lindex $info 0]] >= 0 } {
set [lindex $info 0] [lindex $info 1]
}
if { [string equal [lindex $info 0] payer_email] } {
set payer_email [ns_urldecode $payer_email]
}
}
#Validate transaction data and pack local data structure, a list suitable for use with array get.
set txn_vars [list]
foreach var $vars {
if { ![info exists $var] } {
set success_p 0
} else {
lappend txn_vars $var [set $var]
if { [string equal $var custom] } {
#take whatever special actions needed for
#your custom var (e.g. database key)
}
}
}
return [list $success_p $txn_vars]