Avi and Julian,
Suppose your Seaside site user interaction goes something like this: 1. Choose your product 2. Pay 3. Process the sale
Now suppose you want step 2 (Pay) to take place on Verisign's site.
It would be nice if I could model this using the component call: mechanism already in place. eg, I would call WAPaymentStep which would forward the user to the Verisign site and answer a PaymentResponse upon "returning" from Verisign's site.
There are two parts to this problem: 1. Sending them to the payment site and 2. receiving them back to Seaside after payment.
1. This part is easy: WAPaymentStep presents a form to the user that upon submission posts the necessary variables to the payment site.
2. This is the hard part. Here's my plan so far... WAPaymentStep needs to register a callback that would a) process the POST from Verisign site (their response) and create a PaymentResponse object b) return the Payment Response using #answer:.
What I would like is: Example code on how to register an arbitrary callback block. Does such a callback have access to POST variables? If not, can you suggest another way to get posted variables into a smalltalk object which gets "returned" using #answer: ?
I can imagine another Seaside service called "VerisignService" who's job it is to accept responses from Verisign and leave an appropriate PaymentResponse in a "mailbox" to be picked up by WAPaymentStep. VerisignService then calls some continuation to "wake up" the WAPaymentStep, which "checks its mail" and returns the PaymentResponse using #answer: .
Any ideas would be appreciated.
Thank you.
Derek Brans Nerd on a Wire Web design that's anything but square http://www.nerdonawire.com mailto: brans@nerdonawire.com phone: 604.874.6463 toll-free: 1-877-NERD-ON-A-WIRE
On Thu, 12 Jun 2003, Derek Brans wrote:
- This part is easy: WAPaymentStep presents a form to the user that
upon submission posts the necessary variables to the payment site.
- This is the hard part. Here's my plan so far...
WAPaymentStep needs to register a callback that would a) process the POST from Verisign site (their response) and create a PaymentResponse object b) return the Payment Response using #answer:.
Sorry, I'm not following you here. Can you break this down into HTTP requests + responses, and who is making them to where?
Sure, thanks for taking the time to read that, Avi. There are two parts to this problem: 1. Sending them to the payment site and 2. receiving them back to Seaside after payment.
1. This part is easy: WAPaymentStep presents a form to the user that upon submission posts the necessary variables to the payment site.
Is this part clear? If not: i) WAPaymentStep>>renderContentOn: renders a static confirmation page that says "Are you sure this information is correct?". This page has hidden fields (amount, name, address). ii) When client hits submit "Yes this information is correct" all those fields get posted by the client's browser to Verisigns cgi script. 2. This is the hard part. Here's my plan so far... WAPaymentStep needs to register a callback that would a) process the POST from Verisign site (their response) and create a PaymentResponse object b) return the Payment Response using #answer:.
i) on Verisign's site, client enters CC info and clicks "Pay this amount" ii) client is presented with Verisign hosted receipt page (with hidden fields...) . iii) Client clicks "Done" and at that point the client's browser posts the hidden fields on the receipt page ( including the results of payment) to a _static_ url of my choice.
As an aside, for whatever reason I want, I can have Verisigns server "silently" POST a copy of the information to my server _before_ the client gets back to my server. Also, when I send the client over to Verisign's site, there are up to 10 variables I can include to be returned to me with the client's post or the silent post. This is where I will store my Seaside session key and continuation key.
Now we're back in Seaside, at a static url, with a bunch of posted fields (posted by the client's browser, including a session key). This static url will probably be a Seaside service which will route the payment response back to the correct session and component in the following way:
I need to process the fields and create a PaymentResponse. Then I put the PaymentResponse in some sort of "mailbox" Then wake up the WAPaymentStep by invoking some continuation. The WAPaymentStep grabs the PaymentResponse from its "mailbox" and returns it using #answer:.
Does that make any sense?
Thanks again. Derek
Derek, what did you end up doing on this issue (this thread you posted on about a month ago)?
I use "Novus" for credit card processing at our bountifulbaby.com site. The "Novus" account in turn was obtained through Costco (via an Executive-level Costco membership, which costs me about $100 year to maintain). They provide an internet interface via several different API's. There are 3 basic API's available to me, as follows (I am currently just doing #1, which obviously is a bit manually intensive):
**** API #1. I collect the credit card information from the customer, via a secure SSL connection to my Squeak box, and then later I manually launch a browser (Netscape or whatever), and go to my IonGate credit card processing web site, and log in with my merchant account name and password. I am then (by the IonGate site) presented with a merchant credit card authorization terminal screen where I manually type in the collected customer credit card details and order details. Whatever I enter is then processed via the IonGate gateway each night, and the funds from those transactions are automatically debited/credited to my bank account each night. ****
API #1, above, works, but is obviously manually intensive.
(then skipping down into what you wrote below, you'll find a description of API #2 later)
Derek Brans wrote:
Sure, thanks for taking the time to read that, Avi.
There are two parts to this problem: 1. Sending them to the payment site and 2. receiving them back to Seaside after payment. 1. This part is easy: WAPaymentStep presents a form to the user that upon submission posts the necessary variables to the payment site.
Is this part clear? If not: i) WAPaymentStep>>renderContentOn: renders a static confirmation page that says "Are you sure this information is correct?". This page has hidden fields (amount, name, address). ii) When client hits submit "Yes this information is correct" all those fields get posted by the client's browser to Verisigns cgi script.
**** API #2. I can, if I wish, programmatically create a form similar to what you just described for your Verisign account. And, it looks like it would need to work very similarly (possibly identically) to what you've described above and below. ****
But, there is also another way-- API #3, which might even be easier to do (because there wouldn't be a need to orchestrate the dance you've described below between your customer's browser, your credit card processor's system (Verisign in your case, IonGate in my case), and Seaside. #3 is a server-to-server scenario.
(skip down to the bottom of your message to find API #3)
2. This is the hard part. Here's my plan so far... WAPaymentStep needs to register a callback that would a) process the POST from Verisign site (their response) and create a PaymentResponse object b) return the Payment Response using #answer:.
i) on Verisign's site, client enters CC info and clicks "Pay this amount" ii) client is presented with Verisign hosted receipt page (with hidden fields...) . iii) Client clicks "Done" and at that point the client's browser posts the hidden fields on the receipt page ( including the results of payment) to a _static_ url of my choice.
As an aside, for whatever reason I want, I can have Verisigns server "silently" POST a copy of the information to my server _before_ the client gets back to my server. Also, when I send the client over to Verisign's site, there are up to 10 variables I can include to be returned to me with the client's post or the silent post. This is where I will store my Seaside session key and continuation key.
Now we're back in Seaside, at a static url, with a bunch of posted fields (posted by the client's browser, including a session key). This static url will probably be a Seaside service which will route the payment response back to the correct session and component in the following way:
I need to process the fields and create a PaymentResponse. Then I put the PaymentResponse in some sort of "mailbox" Then wake up the WAPaymentStep by invoking some continuation. The WAPaymentStep grabs the PaymentResponse from its "mailbox" and returns it using #answer:.
Does that make any sense?
Thanks again. Derek
**** API #3. This is similar to API #2, but remember, for API #2, you said:
ii) When client hits submit "Yes this information is correct" all those fields get posted by the client's browser to Verisigns cgi script.
Well, for #3, instead of the client's browser posting the information to Verisigns cgi script (IonGate's asp script in my case), you let your server post it. That means your server will receive Verisign's (IonGate's, for me) response. And then you let Seaside send the appropriate info back to your client's browser, based on what Verisign's server told you. ****
There seems to be a couple of advantages to doing #3 instead of #2.
One difference is: For #2, the values passed to the payment processor (Verisign for you, IonGate for me) are visible to the client's browser using the "view source" within the client's browser, whereas for #3, those values are not visible to the client. For the IonGate gateway, I've looked at those values that would be sent in, and it doesn't look like there is anything that could be maliciously tampered with anyway, but you never know. It seems like it would be better to completely hide it, and by letting your server post the form to the payment processor rather than your client's browser, you could easily completely hide it.
Another difference is: The dance you've described for #2 might be simpler for #3. For one thing, there is no need to forward the user to the Verisign site in the manner you've described. The user would always stay connected solely to your own site. For me, I use Stunnel for SSL secure connection to my Squeak box, and it works fine. Plus, I recently purchased a "real" SSL certificate from GeoTrust ($349 for two years). Before that I was using a self-signed certificate which most folks seemed satisfied with, but now with a "real" SSL certificate from GeoTrust, the rest of the folks should be satisified as well.
Anyway, with Stunnel and an SSL certificate, it really is easy to handle all the SSL communication myself, in a very acceptable manner for the user, and just let the user stay at the site, and never forward them to any payment processor site or any other 3rd party site.
So, for #3, your server would post the credit card information to Verisign (after collecting it from the user), and then just wait for a pass/fail message from your payment processor, and then display that message back to your user.
But, even for #3, the difficulty for me is the fact that I still have to give my payment processor a specific "static" URL to post their response back to me.
In your case, you said the following:
Also, when I send the client over to Verisign's site, there are up to 10 variables I can include to be returned to me with the client's post
For me, this is basically the same-- there are up to 15 variables that the IonGate gateway will echo back to me on it's return post. And, I imagine, just as you've said, this is where I would think I would need to store the session key and continuation key.
When the payment processor posted back to a static URL, the rest of what you've described seems right:
Now we're back in Seaside, at a static url, with a bunch of posted fields,... This static url will probably be a Seaside service which will route the payment response back to the correct session and component in the following way:
I need to process the fields and create a PaymentResponse. Then I put the PaymentResponse in some sort of "mailbox" Then wake up the WAPaymentStep by invoking some continuation. The WAPaymentStep grabs the PaymentResponse from its "mailbox" and returns it using #answer:.
Wow. This really does seem difficult-- far more work than what you would intuitively think. If only we could just call the payment processor "page" like it was a subroutine, and get return values from it, just like a subroutine. In other words, if only we could call the external payment processor "page" like it was just another Seaside component. There would then be no need of the last part of this dance synchronization nonsense.
Anyway, what did you ever end up doing?
Derek,
My "server-to-server" scheme looks like it would work. Here's sample code for my IonGate gateway (but with 'mymerchantloginname' replaced with the actual name for me):
| args resultStream | args _ Dictionary new. args at: 'login' put: #('mymerchantloginname' ); at: 'amount' put: #('1.50' ); at: 'CardType' put: #('VISA' ); at: 'cardnum' put: #('4111111111111111' ); at: 'Expires' put: #('1104' ); at: 'Cardname' put: #( 'NEVIN PRATT'); at: 'Address' put: #('3734 SOUTH 800 WEST' ); at: 'City' put: #('BOUNTIFUL' ); at: 'State' put: #('UT' ); at: 'ZIP' put: #( '84010' ); at: 'Country' put: #('USA' ); at: 'Phone' put: #('801-992-3137' ); at: 'Email' put: #('nevin@smalltalkpro.com' ); at: 'Invoiceno' put: #('1529' ); at: 'Description' put: #('ITEMS 225 2x226' ); at: 'receipturl' put: #('DISPLAY' ); at: 'returnallerrors' put: #('YES' ); yourself. resultStream _ HTTPClient httpPostDocument: 'http://secure.iongate.com/iongate.asp' args: args. resultStream inspect.
With the above code, the 'resultStream' returns a MIMEDocument. The MIMEDocument has a 'content' ivar value that contains a string. I look for a line in that string beginning with RESPONSECODE=. The value after the equals ('=') should be AA. If it is anything else, I next look for a line in that string beginning with AUTHRESPONSE=. The text that follows the AUTHRESPONSE is the reason the card charge was declined.
For the above actual code sample, but with my own 'merchantloginname', I get the following for those values:
RESPONSECODE=ND AUTHRESPONSE=INVALID CARD
That is what I expected, since the card number in the sample code above is obviously wrong.
Another thing I discovered (through trial and error) is that for my IonGate gateway, the field names mentioned in the code are *case sensitive*!!! For example, 'ZIP' works, but 'Zip' does not work, 'Cardname' works, but 'CARDNAME' does not work, etc.
Anyway, for you, something very similar to the above should work. But, now the problem:
The above sample code has http://secure.iongate.com/iongate.asp instead of https://secure.iongate.com/iongate.asp
I'm sure you can see the issue there :-) I use (and have configured) 'Stunnel' as an SSL server, but the above code requires an SSL client to connect to IonGate's SSL server. Thus, Stunnel needs to be configured to be usable as both an SSL client *and* an SSL server (or else I need some other SSL client mechanism, or else I need to go with API #2 from my previous email and use the SSL client capabilities of the customer's browser).
I've not yet resolved that issue.
Nevin
Nevin Pratt wrote:
<..snip the sample code and explanation...>
The above sample code has http://secure.iongate.com/iongate.asp instead of https://secure.iongate.com/iongate.asp
I'm sure you can see the issue there :-) I use (and have configured) 'Stunnel' as an SSL server, but the above code requires an SSL client to connect to IonGate's SSL server. Thus, Stunnel needs to be configured to be usable as both an SSL client *and* an SSL server (or else I need some other SSL client mechanism, or else I need to go with API #2 from my previous email and use the SSL client capabilities of the customer's browser).
I've not yet resolved that issue.
Nevin
Stunnel doesn't seem to be configurable to simultaneously be an SSL client *and* an SSL server, so I started up a second instance of Stunnel to be the SSL client. Thus, the above becomes
https://localhost:%7Bmy client stunnel port}/iongate.asp
with {my client stunnel port} replaced with the actual port number that I've set the second instance of Stunnel up on. This client instance of Stunnel then forwards to 'secure.iongate.com:443' (thus establishing a secure connection), and things seem to work fine.
I've now got a working 'server-to-server' SSL connection between my own server and my IonGate Credit Card Authorization Gateway. It wasn't that hard.
I haven't actually put it into production yet, though, because I want to test some more.
Nevin
seaside@lists.squeakfoundation.org