Creating a Paypal Listener

@Michel Bujardet from this thread: https://forum.xojo.com/37424-what-s-your-programming-goal-for-the-new-year
you offered:

[quote=306618:@Michel Bujardet]I use Instant Payment Notification with a Xojo Web app as listener, which receives notifications from Paypal and deliver instructions for download.

https://developer.paypal.com/webapps/developer/docs/classic/ipn/integration-guide/IPNImplementation/

The trick is to have Paypal hit HandleURL or HandleSpecialURL, and to post back using Xojo.Core.HTTPSocket in secure mode, because it supports http 1.1.

Another option would be to use CURL (most samples they show use it), but I don’t know if MBS CURL is able to do http 1.1 required by Paypal.[/quote]

Paypal summary of Steps 1-3:

  1. Your listener listens for the HTTP POST IPN messages that PayPal sends with each event.
  2. After receiving the IPN message from PayPal, your listener returns an empty HTTP 200 response to PayPal. Otherwise, PayPal resends the IPN message.
  3. Your listener sends the complete message back to PayPal using HTTPS POST.

I think I am in pretty good shape with step #1 - I have implemented a listener in HandleURL event of my listener app.
At the end of my receiving the IPN message in step #1 above, I end the routine with:

Request.Status = 200 Return True
… which I believe satisfies step #2 above?

Here’s where I am asking for guidance…
Since I have exited the HandleURL routine as described above (Request.Status = 200; Return True), then I need to trigger the routine to address step #3 above. Is that correct that this needs to happen outside the HandleURL event - since I need to return 200 BEFORE I execute on step #3? And if that is correct, how do you trigger the step #3 process - do you just set up a timer or something like that to invoke the step #3 process?

Consider using a Thread for Step 3. Doing so minimizes the time spent in HandleURL and almost immediately accomplishes Step 3.

I like that idea… but - the only way I know how to do that is:

Request.Status = 200 validateThread.Run Return True

And therefore, I run the thread before getting to the “Return True” statement - and I don’t think that sequence would work.
So I think I need to do this:

Request.Status = 200 validateTimer.mode = 1 Return True

Where validateTimer will then execute the validate thread/method.
Am I over thinking this?

[quote=313868:@Mark Pastor]At the end of my receiving the IPN message in step #1 above, I end the routine with:

Request.Status = 200
Return True[/quote]

For Step 3 I use a Xojo.Core.HTTPSocket (it supports http 1.1) to post back the received fields to Paypal, to confirm.

https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNIntro/

We do a bit of database work as part of Step 3 so returning the message to PayPal occurs after Step 2 or they have been indifferent to the sequence. A delay should ensure the order of the sequence but for the past couple of years, it’s worked for us without a delay.

I do everything in HandleURL, and indeed there is no need for special delay.

I thought that doing anything in HandleURL was a separate thread. Is that not true any more?

I’m not a communications expert -
I have set up my socket as follows - please let me know if you see something not right.

responseSocket = New HTTPSecureSocket responseSocket.Secure = True responseSocket.connectiontype = responseSocket.TLSv12

After receiving the IPN from paypal, I set up the socket as above and send back to paypal the verify command with their payload.
My socket show it connects, but I don’t receive any other events such as page received or error.

Is it obvious what I am not doing correctly?

Ah - I am not using xojo framework socket - I will go try that.

That is the whole point : each handleURL has its own thread, so it can be tapped into without disturbing the rest of the program.

Mark : You cannot use the request object directly, or the old HTTPSocket, because Xojo Web does not support the required HTTP 1.1.

That is why I use a Xojo.Net.HTTPSocket to post back.

[code] // Set dictionary
dim entity as string
if debugbuild then
entity = “payment_type=instant&payment_date=Sun%20Feb%2028%202016%2022%3A24%3A42%20GMT%2B0100%20%28CET%29&payment_status=Completed&address_status=confirmed&payer_status=verified&first_name=Ginette&last_name=Smith&payer_email=michel@fontmenu.com&payer_id=TESTBUYERID01&address_name=John%20Smith&address_country=United%20States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San%20Jose&address_street=123%20any%20street&business=seller%40paypalsandbox.com&receiver_email=paypal-fonts@fontmenu.com&receiver_id=seller%40paypalsandbox.com&residence_country=US&item_name=something&item_number=AK-1234&quantity=1&shipping=3.04&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&mc_gross_1=9.34&txn_type=web_accept&txn_id=580373057&notify_version=2.1&custom=AB776E2678FD3B695A3C2E8129571F2AEB463D13A764EC09EEF28172181AC2D189DDA6D0&invoice=abc1234&test_ipn=1&verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31AvuP1FzfXF7VqgkW9eYfB4k4P09J”
else
entity = Request.Entity
end if

	logme = logme + request.Entity+EndOfLine
	dim s as string = DecodeURLComponent(entity)
	s = s.ReplaceAll("+", " ")
	logme = logme + "s : "+s+EndOfLine
	dim args() as string = split(s, "&")
	logme = logme + "args ubound : "+str(args.Ubound)+EndOfLine
	
	if args.Ubound > 0 then
			' Simple data in a Dictionary
			Dim info As New Xojo.Core.Dictionary
			mySocket = New xojo.Net.HTTPSocket
			
			logme = logme + "loading dictionary"+EndOfLine
			for i as integer = 0 to args.Ubound-1
					dim paire() as string = split(args(i),"=")
					dico.value(paire(0)) = paire(1)
			next
			logme = logme + "dictionary :"+str(dico.Count)+EndOfLine
			
			' Convert to JSON text
			Dim json As Text
			json = Xojo.Data.GenerateJSON(info)
			
			' Convert to MemoryBlock  
			Dim data As Xojo.Core.MemoryBlock
			data = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(json)
			
			mySocket.SetRequestContent(data, "application/x-www-form-urlencoded")
			mySocket.Send("POST", "https://ipnpb.paypal.com/cgi-bin/webscr")[/code]

To see if everything is OK, use the IPN Simulator :
https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNSimulator/

It lets you trigger a request from Paypal and tells you the result.

That’s good to know. So you are consistently completing Step 3 prior to completing Step 2?

It is still a separate Thread.

No, in practice I return 200 through Request before the code I posted.

Pardon my ignorance here. The only way I know how to do this is:

request.status = 200 return true

Am I correct in assuming the 200 status is only sent upon the return true statement?
Because if that is the case, then you have exited the HandleURL event without the opportunity to execute your code above.

And if I am not correct, then how do you: [quote=314094:]return 200 through Request before the code I posted[/quote] ?

By the way - I do appreciate your willingness to be helpful - so, thank you.

[quote=314203:@Mark Pastor]Pardon my ignorance here. The only way I know how to do this is:

request.status = 200 return true
[/quote]
The return true will send it that very instant. If you add code that processes things between request.status and return true that code will process and your status will still be whatever you set it when the method completes.

So, if the code for step 3 processes in the HandleURL event before the return true statement, then step 3 will happen before step 2 completes. That is the clarity I am looking for.

It’s the start of a race condition. Your web app will begin the process of the third step before it returns the HTTP status code of 200. Chances are it will complete HandleURL (and return the status) before being able to send out the request for verification, but who knows, the race is on!

Remember! New Framework sockets are always aysnc.

Edit for clarification.

Before the code I posted, I have

Request.Status = 200 // An HTTP Response code of 200 means everything is OK

and at the end of HandleURL

Return True

I didn’t see any “+” in the paypal payload; and I am not fully fluent in HTML, is there a reason I should have this replacement in my code?

Request entity may contain space, encoded as “+”. So I replace them by space. The same result would be attained with DecodeURLComponent. I don’t remember why I used replaceAll instead.