overrides, chaos, and multiplepaymentprocessors

View: New views
8 Messages — Rating Filter:   Alert me  

overrides, chaos, and multiplepaymentprocessors

by Brandon Craig Rhodes :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


As Michael suggested last week, I have studied how all of the payment
processors besides Google Checkout integrate with GetPaid; Michael, it
was a great suggestion!  And, thanks to guidance which Christopher
Johnson provided when we talked on Friday, I have also read through the
various branches in GetPaid labeled "multiplepaymentprocessors".

   (USABILITY PEOPLE: You can skip down to the "Big Questions That I
                      Need Feedback On" section farther down if you want
                      to skip the technical discussion.)

As a result, I now have a more complete picture of how the various
GetPaid payment processors are designed.  To keep things straight, I
sketched out this rough diagram in Inkscape this afternoon:

    http://rhodesmill.org/brandon/static/2009/getpaid-overrides.png

Red text in the diagram indicates the presence of a ZCML override of
some sort.  Note that I have left out extra steps that might be present
in the wizard, like shipping options; when present, such steps would sit
between the "Address Info" and the "Review & Pay" pages in the diagram.

Payment Processors, and How They Work
-------------------------------------

Taking the diagram from top to bottom:

 * Google Checkout:

   The payment processor provides its own copies of the shopping cart
   view and shopping cart portlet, so that it can draw a pretty "Google
   Checkout" button on each of them.  The buttons point at a view named
   "/google-checkout" on the Plone site, which has no associated
   template but instead redirects directly to Google itself.

   OPEN QUESTION: It is not clear to me why the checkout buttons do not
   point at Google directly instead of going through the extra view, but
   I see Google Analytics parameters being constructed on the way that
   might have something to do with it.

 * ClickandBuy / PagSeguro:

   These both provide their own @@getpaid-checkout-wizard view that
   removes all of the normal functionality, and shows a big button that,
   when pushed, sends the user to an off-site checkout process.

   OPEN QUESTION: This approach competes directly with the Google
   Checkout approach.  Which is better: providing a special checkout
   button for "Checkout"?  Or, letting them visit our normal checkout
   URL then asking them to click off-site?  The former requires one less
   click from the user; the latter gives them a bit more warning that
   they are leaving.  Should we rewrite all three payment processors to
   use a common approach, and then provide an admin config option for
   whether a store owner wants the extra "Click here to checkout" step
   added or not?

 * PayPal:

   This also provides its own @@getpaid-checkout-wizard, but the view
   simply does an immediate redirect to PayPal.

   OPEN QUESTION: Like with Google Checkout, the question is, why?
   Because it was easier than overriding the "Checkout" buttons?  Or
   because the extra step lets a few last variables and parameters be
   put into place before the customer arrives at PayPal?

 * Luottokunta / Verkkomaksut:

   These processors only change the last step of the default checkout
   wizard.  They simply provide an alternate form for the last view,
   named @@checkout-review-pay, that asks for some fields that their
   particular payment processors need to operate.

 * Ogone / PxPay:

   These payment processors do a good deal more violence than the
   previous two we looked at.  Like them, they let most of the checkout
   wizard views complete their job, and then just override the final
   @@checkout-review-pay page.  But instead of presenting a slightly
   adjusted form, they actually redirect the user away from the Plone
   site altogether, so the customer's credit card information can be
   entered on the remote site.

   OPEN QUESTION: Why do I call these two more "violent"?  I think it's
   because of how painfully they abuse the idea of an IPaymentProcessor.
   Instead of simply dropping the idea altogether and performing their
   redirects directly - which is what a view is for, right? - these two
   payment processors have their @@checkout-review-pay views call the
   authorize() method of a PaymentProcessor object that then - I warned
   you that this was painful, right? - performs the redirect itself!
   They put, in other words, view logic inside of an interface that's
   supposed to process a credit card and return an actual result.

 * nullpayment / AuthorizeDotNet / PayFlowPro / PaymentTech

   These are the well-behaved payment processors that actually work as
   GetPaid intended.  Note the happy lack of red text from their area of
   the diagram!  They accept payment information and try to get the
   payment processed, and leave it up to the PloneGetPaid views to
   report back to the user whether their purchase worked.

So, there you have it!  No fewer than six separate techniques are used
in GetPaid payment processors for what are really amounts to four
separate situations:

 1. Off-site processors that provide their own checkout wizards
    (Google Checkout, ClickandBuy, PagSeguro, PayPal)

 2. Off-site processors that let Plone collect the shipping info
    (Ogone, PxPay)

 3. Normal payment processors that need a few payment fields adjusted
    (Luottokunta, Verkkomaksut)

 4. Normal payment processors that can use the default payment page
    (nullpayment, AuthorizeDotNet, PayFlowPro, PaymentTech)

So, what about the "multiplepaymentprocessors" work?

Multiple Payment Processors
---------------------------

The "multiple payment processors" branches seem focused on processor
categories 3 and 4, as given in the above list: the processors that
require GetPaid to ask for the credit card number on-site.  For these,
it introduces a new registration system.  Instead of simply registering
themselves as providing IPaymentProcessor, these new-style payment
processors would have to actually use a custom ZCML registration that
looks like:

        <paymentprocessors:registerProcessor
           name="..."
           i18n_name="..."
           selection_view="..."
           review_pay_view="..."
           thank_you_view="..."
           settings_view="..."
           />

The basic idea here is that many payment processors can register
themselves this way.  The store owner will be shown all of them on his
Site Setup page, and be asked which he wants to use.  If he only selects
one, then that is the one that users are sent to directly.  But if he
selects several, then a new @@checkout-payment-method step is placed
before the final @@checkout-review-pay step, and the user is shown a
pretty button for each available checkout method (that's what the
"selection_view" shown above is for), and has to select one.

And the final checkout form, when they get there, can be possibly
customized to have extra or different fields from the normal one.  That
seems to be what the "review_pay_view" means in the registration example
above: it's a way, without an override, for a payment processor like
Luottokunta or Verkkomaksut to adjust what the final view contains.

I am not sure what I think about special ZCML declarations; it seems to
me like the same information could be provided much more simply through
the adapter-registration mechanism we already use, and that class
properties with names like "thank_you_view" and "settings_view" would do
a fine job of pointing at the views that the payment processor wanted to
provide, without needing any ZCML.  But that's probably my Grok
background showing through, and maybe there are view configurations done
behind the scenes here that I've not studied yet.

What does everyone think?  If something can be done simply enough in
code, should ZCML be used or not?

Either way, it looks to me like the "multiplepaymentprocessors" work is
a great candidate for getting rid of the overrides required by category
3 payment processors, as given above, and for letting users choose their
favorite payment processor when it's okay with the site owner.

That, of course, just leaves categories 1 and 2 as problems.

Handling Off-Site Checkout Wizards
----------------------------------

Right now, the series of steps that the on-site checkout wizard supports
seems kind of hard-coded; in the "CheckoutController" it says (in the
new "multiplepaymentprocessors" branch):

    steps = ['checkout-address-info', 'checkout-select-shipping',
             'checkout-payment-method', 'checkout-review-pay']

What if this list was something built dynamically?  What if the default
was something like a series of views like this:

   100 'checkout-address-info'
   200 'checkout-select-shipping'
   300 'checkout-payment-method'
   400 'checkout-review-pay'

where plug-ins could provide steps of their own, like:

   150 'checkout-shipping-company'

Maybe inequality invariants would be better than static numbers; but
static numbers work pretty well for the Ubuntu package managers who
build all those /etc directories, I'll note.  But whichever way we
established order among the steps, the act of building the list
dynamically could have several advantages.

One of them would be that none of the remaining payment processor types
would need overrides!  "ClickandBuy", for example, could just say:

  050 'checkout-clickandbuy-button'

and the view that shows the "Check out at ClickandBuy" button would show
up in front of the user before anything else in the wizard - and then
the button could take them far away and not return them, meaning they
wouldn't encounter any of the rest of the wizard.  This could handle
Google Checkout, ClickandBuy, PagSeguro, *and* PayPal, *if* we were
willing to tell the people using Google Checkout and PayPal that they
have to use a you're-leaving-the-site splash screen like ClickandBuy and
PagSeguro.  Actually, maybe that wouldn't be necessary; maybe they could
provide a set of "redirect" views that could be dropped in place if the
store owner opted not to have a two-click process where one would work:

  050 'checkout-paypal-redirect'

Processors like Ogone and PxPay would put their redirect (or
leaving-this-site click-this-button) page later in the process, after
the shipping information has been collected:

  350 'checkout-pxpay-redirect'

The one thing that such a pluggable-pages scheme would *not* fix is the
situation, like the Google Checkout design today, where the "Checkout"
buttons on the shopping cart view and shopping cart portlet both need to
be re-drawn to "look like" the checkout buttons for the selected payment
processor.  If we want to support that, then we need to do work
"outside" the standard wizard, like the work I announced last week that
I did experimentally in my "brandon-no-overrides" buildout: we need
those buttons to become views that payment processors can override.

Big Questions That I Need Feedback On
-------------------------------------

The big outstanding questions I have right now are:

  * It's neat for users to be able to choose among payment processors
    like AuthorizeDotNet and PayFlowPro, that both plug in right at the
    end of the normal checkout wizard.  But what about off-site services
    that provide their own wizards, like Google Checkout?  Should users
    be able to choose between Google Checkout and AuthorizeDotNet?

    I would *love* for this to be answered as a usability question,
    *not* a technical one; a good design is possible here either way we
    answer the question.  The usability question that is raised is this:
    the choice between AuthorizeDotNet and PayFlowPro can be asked right
    at the end of the wizard, before the credit card information is
    demanded.  But the choice given to the user between using Google
    Checkout and AuthorizeDotNet would have to be asked immediately,
    before the first normal page of the wizard was even shown.  Should
    users in a multiple-payment-processors situation (a) *always* be
    asked which they want to use *before* entering the wizard, or (b)
    should the question "float" so that it's asked right before it
    becomes important, but otherwise as late as possible, so that it's
    asked before the other wizard steps if, say, Google Checkout is one
    of the options, but late in the process if it's just AuthorizeDotNet
    and PayFlowPro?

    I'd love to do (b) because it seems the "real answer" that, if done
    right, lets store owners give users the maximum amount of choice
    when it's what's wanted.  But I'm open to counter-arguments.

  * Again, a usability question: do store owners deserve the right to
    avoid a "click here" screen by having the cart "Checkout" button say
    "Check out with Google Checkout" and go right there?  Conversely:
    should every off-site processor support, if the store owner wants
    it, a separate "click here to leave" screen that follows the normal
    "Checkout" button instead of subverting it?

    I think this answer should probably be "yes", and I think a
    reasonable design will result.  Let me know if this matches what
    other people want; you guys have used this a lot more than I have!

  * I assume that users partway through the checkout process with
    PayFlowPro should be able to back up and re-start with Google
    Checkout if they suddenly realize that's what they want to do?

IF the above usability questions are answered like I expect, then I
might have to rescind the distinction that I tried to draw in my
"brandon-no-overrides" experiment between Wizards and Processors,
because that would create an ugly situation where users whose store
owners had been very liberal would have to *first* choose their Wizard,
*then* choose their back-end Payment Processor if they choose the
built-in Wizard; or an ugly combined form would have to be created.  I
would like to avoid either of these situations!  If we decide we want
true flexibility here, then I think we need to abandon my "Wizard"
concept and just talk about "Payment Processors that redirect the user
somewhere else" versus "Payment Processors that let GetPaid do most of
the work, then just verify the credit card."

Apologies For Stuff I've Gotten Wrong
-------------------------------------

Today was my first time looking at most of these payment processors, and
I've probably gotten a bit mixed up about how some of them work.  If you
find I've misunderstood anything above, then just reply and point out
how this first impression of my differs from the reality.  I want to
make sure that all reasonable payment processor designs become easy as
we re-factor how they are registered and deployed!

Next Steps
----------

As usual, given the size of this email and the questions it asks, I'll
wait a day or two so that people have time to digest it and respond.
Then, unless the responses really surprise me and merit debate and
discussion, I think my next step will be to try inventing and
documenting - but maybe not implementing until I get feedback on the
draft documentation? - a straightforward registration scheme that
supports all the kinds of payment processor we've got.  I'll call the
draft "How to Write a Payment Processor", with the idea that we focus on
exactly that: what will payment processors look like to the person who's
*writing* them, in a way that's flexible enough to support the whole
spectrum of techniques we've listed above.  Then it'll be a simple
matter of adjusting the GetPaid core, the Plone Product, and the various
change processors to match the new standard. :-)

--
Brandon Craig Rhodes   brandon@...   http://rhodesmill.org/brandon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Christopher Johnson-7 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Wow, lots of good info and direction forward. Will try to dive into usability thoughts on processors shortly. For now, just one usability issue that seems like it may be a proper place to address in the refactoring, and that is the use of the name "getpaid" in the URLs of the system. I see you are suggesting a replacement view of the checkout wizard, and it seems like a good opportunity to rename it to just @@checkout-wizard (dropping the getpaid). As some people have mentioned, it's really unnecessary to name the product in the ecommerce functionality...

Thanks for the great work on this!

Best,
Chris

On Wed, Aug 26, 2009 at 3:36 AM, Mikko Ohtamaa <mikko@...> wrote:
Chaos - who wouldn't love that? ;)


  * PayPal:

  This also provides its own @@getpaid-checkout-wizard, but the view
  simply does an immediate redirect to PayPal.

  OPEN QUESTION: Like with Google Checkout, the question is, why?
  Because it was easier than overriding the "Checkout" buttons?  Or
  because the extra step lets a few last variables and parameters be
  put into place before the customer arrives at PayPal?

I guess this is just because there were no 1) proper hooks 2) proper documentation how to create payment processors. See also one PayPal comment below.

Little related to this open question PayPal and some other payment processors did (still do?) order handling in a bit wrong order by not destroying the cart on completion or destroying the cart prematurely when the checkout button is pressed.
 

So, there you have it!  No fewer than six separate techniques are used
in GetPaid payment processors for what are really amounts to four
separate situations:

 1. Off-site processors that provide their own checkout wizards
   (Google Checkout, ClickandBuy, PagSeguro, PayPal)

PayPal and Google Checkout support also more tightly integration with the existing web site without 1) taking the user away from the site 2) managing the order on the payment processor site. The current plug-ins don't use them, however.

Multiple Payment Processors
---------------------------

The "multiple payment processors" branches seem focused on processor
categories 3 and 4, as given in the above list: the processors that
require GetPaid to ask for the credit card number on-site.  

Multiple payment processor branch also should support off-site processors.

Since off-site processors like PayPal need the action initiated by a form submission, the current multiple payment processor setup simply creates a prefilled form with hidden inputs and submits it using Javascript. I am not sure whether this is the best practice and promoted by PayPal, but it provides very easy integration for those crappy websites built with PHP :)
 
For these,
it introduces a new registration system.  Instead of simply registering
themselves as providing IPaymentProcessor, these new-style payment
processors would have to actually use a custom ZCML registration that
looks like:

IPaymentProcessor interface should have not references to the user-inteface or Plone (Products.PloneGetPaid) so that other Zope frameworks (BFG, Grok) can use it.


       <paymentprocessors:registerProcessor
          name="..."
          i18n_name="..."
          selection_view="..."
          review_pay_view="..."
          thank_you_view="..."
          settings_view="..."
          />
 

I am not sure what I think about special ZCML declarations; it seems to
me like the same information could be provided much more simply through
the adapter-registration mechanism we already use, and that class
properties with names like "thank_you_view" and "settings_view" would do
a fine job of pointing at the views that the payment processor wanted to
provide, without needing any ZCML.  But that's probably my Grok
background showing through, and maybe there are view configurations done
behind the scenes here that I've not studied yet.

Well... I thought everything in Plone should be configured through ZCML nowadays... are we redesigning the system *again*? ;)

ZCML is teh shit, but "Grokking" packages is not widely available or well-enough documented to be used with Plone yet. We are barely getting rid of things *before* ZCML. Also note that the list of active payment processors is stored in portal_properties.payment_processor_properties. Very old fashioned, but Witchert recommended this practice in some plone-dev@ email, because 1) settings are very visible in ZMI 2) settings can be changed even if the payment processor breaks the site user interface.

Everything could be done using adapter and actually I think that's a preferred method. It should quite trivial to change getpaid.multiplepaymentprocessors to use adapters internally instead of managing its own registry. On the other hand, the registry is very easy to read and understand, instead of hunting down adapters with "some parameters" and "stored somewhere".

When adapters are used internally, payment processors opt-in to using Grokked packages if they want to depend on Grok.

Also, I suggest adding "layer" attribute to ZCML declaration. This way payment processor customizations could be site specific by using a policy product layer.

> That, of course, just leaves categories 1 and 2 as problems.

multiplepaymentprocessor branch should handle those case as well. I think it's just matter of having more view hooks available (checkout button,etc.).


> What if this list was something built dynamically?  What if the default
was something like a series of views like this:

Could we use those Plone browser layers here somehow? It is the recommended practice to do site customizations.


>  100 'checkout-address-info'
>  200 'checkout-select-shipping'
>  300 'checkout-payment-method'
>  400 'checkout-review-pay'

I was thinking of rewriting getpaid.wizard first to have support for reordering and dynamic step conditions (now hardcoded in Products.PloneGetPaid.checkout), but it was little too much work for just one shop.

>Maybe inequality invariants would be better than static numbers; but
>static numbers work pretty well for the Ubuntu package managers who
>build all those /etc directories, I'll note.  But whichever way we
>established order among the steps, the act of building the list
>dynamically could have several advantages.

Note that static numbers are not enough, but you will need to disable or enable pages depending on the external conditions. E.g. show checkout-payment-method if len(payment_processors) >= 2.


> * It's neat for users to be able to choose among payment processors
 > like AuthorizeDotNet and PayFlowPro, that both plug in right at the
  >  end of the normal checkout wizard.  But what about off-site services
   >that provide their own wizards, like Google Checkout?  Should users
   >be able to choose between Google Checkout and AuthorizeDotNet?

Yes as there might be demographic reasons for this. For example, the shop could use a local bank processor (off-site) and global PayPal.



>The usability question that is raised is this:
> the choice between AuthorizeDotNet and PayFlowPro can be asked right
 >  at the end of the wizard, before the credit card information is
 >demanded. 

I might be thinking little different here, but this is actually the easiest usability question I have faced :)

1. In real-world the payment is done just right before you are leaving the shop as the last thing of your shopping experience

2. Thus, the payment method shoul be chosen as the last step of the wizard because people are familiar with this best practice

To support my rationale, all major webshops are doing the payment method as the last choice. If you need to see example, try registering a domain in GoDaddy.



  > Again, a usability question: do store owners deserve the right to
  >avoid a "click here" screen by having the cart "Checkout" button say
  > "Check out with Google Checkout" and go right there?  Conversely:
  > should every off-site processor support, if the store owner wants
   >it, a separate "click here to leave" screen that follows the normal
   >"Checkout" button instead of subverting it?

Yes as there might be order customizations which offsite payment processor can't handle.


  > I assume that users partway through the checkout process with
  > PayFlowPro should be able to back up and re-start with Google
  > Checkout if they suddenly realize that's what they want to do?

The cart and checkout wizard information should be preserved as session data until the checkout is complete.



>As usual, given the size of this email and the questions it asks, I'll
>wait a day or two so that people have time to digest it and respond.
>Then, unless the responses really surprise me and merit debate and
>discussion, I think my next step will be to try inventing and
>documenting - but maybe not implementing until I get feedback on the
>draft documentation? - a straightforward registration scheme that
>supports all the kinds of payment processor we've got.  I'll call the
>draft "How to Write a Payment Processor", with the idea that we focus on
>exactly that: what will payment processors look like to the person who's
>*writing* them, in a way that's flexible enough to support the whole
>spectrum of techniques we've listed above.

As starting point for the documentation I already tried to scape together something to getpaid.paymentprocessors README.

I and my colleagues may support the effort, but we cannot invest into this very heavily, since we are not actively in ecommerce business and we have just one getpaid projects thus far.

Cheers,
Mikko

--
Mikko Ohtamaa
http://www.twinapex.com - Python professionals for hire





--
Cofounder and CEO
ifPeople - Innovation for People
www.ifpeople.net
t: 678-608-3408
130 Boulevard NE, #6
Atlanta, GA 30312

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Brandon Craig Rhodes :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


Mikko Ohtamaa <mikko@...> writes:

>> seems like a good opportunity to rename it to just @@checkout-wizard
>
> You can also drop @@. ... So you can just have:
>
> /enter-shipping-address
> /choose-payment-method
> /final-checkout

Which brings up some great questions! :-)

First, just to make sure everyone knows the difference, the URL
"/Plone/foo" can mean *two* different things.  First, it might bring up
some item inside of the container "Plone", like a Folder or Page or
whatever, that happens to have the ID of "foo".  Second, it might find
that there is a view registered for "Plone" objects that is named
"@@foo", and display that view of the Plone object instead.

So the @@-less URLs suggested above will only work if we can *guarantee*
that Plone users will not create top-level folders with the same name.
Try it on a sample instance of your own: first, observe that
"/Plone/empty-cart" shows you the GetPaid empty-cart view, just like the
similar URL "/Plone/@@empty-cart".  Now, go into "/Plone" and create a
Page named "Empty Cart".  What happens?  The "/Plone/empty-cart" URL now
shows the page you've created, and GetPaid, if it was relying on that
URL, would be broken.

But, of course, even if we can stay out of the way of users, we still
have other developers to contend with!  What if (I'm being a bit silly,
just for illustration) someone writes an Oregon Trail clone for Plone
and has an "empty-cart" view for what happens if your supplies get
stolen?  Then, depending on which view gets registered first (or would a
conflict message be generated by the duplicate registration?), only one
of the two plugins could work on any given site.

I suspect that this is why the best of the GetPaid views have their
names start with "getpaid-*"; that way, they're pretty much guaranteed
to be unique, and not conflict with other views that other products have
registered.  And if we do conflict, we can easily blame the other guy,
for starting their view with the name of our product. :-)

All of this raises a second question besides the question of whether
names are likely to collide: does it strike anyone else besides me as
extremely strange that the "@@empty-cart" view is *not* merely a view of
a Plone site, but a view of *any possible object whatsoever*?  Try it!
Create a new Plone instance with GetPaid, and try visiting these URLs:

    "/Plone/@@empty-cart"
    "/Plone/news/@@empty-cart"
    "/Plone/events/@@empty-cart"
    "/Plone/front-page/@@empty-cart"

These are *all* aliases for the same view!  Why?  Because, crazily, the
ZCML declaration for "empty-cart" goes like this:

  <browser:page
     for="*"
     template="templates/cart-empty.pt"
     name="empty-cart"
     permission="zope2.View"
     />

In other words, the "empty-cart" view is declared to be an appropriate
view for "*": for ANYTHING!

This seems to be an utter abuse of the concept of a view.  Views are
supposed to get data, and display it.  Like these views:

    "/Plone/@@folder_contents"
    "/Plone/@@manage-content-rules"
    "/Plone/@@sharing"

These are views that are working as they ought!  They are taking an
object for which they are designed - in this case, the "Plone" container
- and render it in some useful fashion that lets it be viewed or
modified.

Why does "@@empty-cart" need to exist everywhere beneath a site?  Why
should hundreds of URLs for it be valid?

This approach will also get us into trouble if we want to make these
views available under other frameworks, like Pylons or Turbogears or
Django.  Under those systems, when someone wants to provide a plug-in
system, they generally just ask for *one* plug-in point beneath which
their URLs exist.  The integrator gets to select "/getpaid", or
"/cart", or "/checkout", or whatever they want, and then all of the
plug-in URLs exist under there.  The Plone approach - of creating dozens
of new views, of "dumping" them into the global URL space and just
hoping there will be no collisions, and of registering them for every
object under the sun so that they exist beneath every valid URL on the
web site - is, if I can use this word politely, just bizarre. :-)

What solution might we consider for this chaos?

It occurs to me that views can be registered against all objects that
happen to offer a certain interface.  What if we registered all of our
views for="IStore"?  All sorts of good results would seem to follow:

  (a) Only those sites in a Zope instance that have actually installed
      GetPaid and been marked as IStores will have the GetPaid views
      activated for them.  If "/Plone1" has GetPaid installed but
      "/Plone2" does not, then only the former will have a shopping-cart
      view.

  (b) GetPaid views will only exist at each store's root, rather than
      being crazily duplicated everywhere.

  (c) When we go to offer GetPaid as a solution under more conventional
      web frameworks, all we've got to offer is a way for, say,
      "/getpaid" under their Django site to return a Zope "IStore"
      object and start up the Zope traversal logic, and, bingo!, all of
      our views will be accessible as "/getpaid/checkout",
      "/getpaid/enter-shipping-address", and so forth.  Trust me: no
      normal web framework will consider us unless we can behave nicely,
      by placing all of the new URLs we create down beneath a designated
      name the guarantees we won't conflict with their other URLs. :-)

This doesn't seem to solve the problem of keeping our view names from
colliding with other site-level views in a Plone site; but, Plone people
seem to be living with the current situation without complain, so I'm
fine for now if no one else has a problem with "/Plone/@@checkout" being
where we put the checkout wizard.  Heck, I guess if we document the
restriction, we could even have "/Plone/checkout" be what we named it.

Maybe Plone people are much more careful and alert about not creating
conflicting names than I'm imagining, and it's not something we should
worry about?  Speak up, Plone folks, and help me understand how you live
with all of this.

--
Brandon Craig Rhodes   brandon@...   http://rhodesmill.org/brandon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Brandon Craig Rhodes :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


Mikko Ohtamaa <mikko@...> writes:

> Brandon, I like your enthusiasm towards Plone :)

Thanks. :-)

> My rationale ...
> * Web best practices say that friendly URLs are must.
> * @@ does not look friendly in URL.
> * /getpaid-checkout does not look as friendly as /checkout in URL

You're right, "getpaid" does look pretty ugly in a URL.

You've convinced me!  Let's go with new, simple URLs like "/checkout".
I'd still love to give shop owners the *option* to move our views "one
level down" in their Plone site, beneath a URL of their own choosing
like "/shop" or whatever, which would also prepare us for web frameworks
like Turbogears and Django that want products to live beneath their own
URL prefix.

How would this work, technically?

Currently, our code does something like this to figure out where the
checkout wizard lives:

    portal = getToolByName( self.context, 'portal_url').getPortalObject()
    url = portal.absolute_url() + '/@@getpaid-checkout-wizard'

Now, this tells us right off the bat that, as I observed yesterday, the
registration for this view is overly broad:

  <browser:page
     for="*"
     name="getpaid-checkout-wizard"
     class=".checkout.CheckoutWizard"
     permission="zope2.View"
     />

Since, as the above code demonstrates, we always expect the checkout
wizard to live right beneath the Plone site's main URL, there's no
reason not to tighten the view registration up and say this instead:

     for="IStore"

so that every other object in the Plone site doesn't gain a
"@@getpaid-checkout-wizard" view that it doesn't need.

How could we change the assumption built into the above code - that our
views always live on the portal root - so that we also have the option
of living beneath another URL instead?  I can think of three ways:

 1. We currently slap an "IStore" marker interface on the Plone portal
    itself.  If we treated it instead as a utility that was merely
    registered with the current portal, then it could live anywhere
    instead.  To find the store, you just ask for the "IStore" utility.
    If the site owner is using the default setting, then the portal
    itself will have had the "IStore" marker placed on it, and the
    checkout page will live at "/Plone/checkout".  If the site owner
    instead said to put everything under "/shop", then a simple "shop"
    object will have been created inside the Plone site and *it* will
    have been registered as the "IStore" utility.  Under other web
    frameworks, we would just jury-rig a global utility registration
    pointing at an IStore object that knows where in the Django or
    Turbogears URL tree GetPaid has been installed.

 2. But maybe it would be a Bad Thing to move the "IStore" marker off of
    the portal, since that would both move all of our configuration
    views down on to the "/shop" URL instead, if the site owner asked
    for one?  On the one hand, having the configuration URLs move around
    might be not just nice, but necessary, if we think that a TurboGears
    integration of GetPaid would re-use our configuration screens, since
    they can't there live at the site root very comfortably.  But, in
    Plone, it seems plain awkward for site-configuration views to live
    anywhere else but on the Plone object itself.  Plus, a quick search
    of the source code suggests that "IStore" is used two ways that are
    incompatible with moving it away from the portal root: it's used as
    the name for the Store's adapter-and-interface registry where
    various GetPaid products register themselves, and the installation
    of the IStore marker interface is used as an event trigger by at
    least some of the shipping code.  So, if all of the above reasons
    are good ones for leaving "IStore" alone and keeping it as the name
    of the portal root and Zope 3 registration point, then we would want
    to create another utility name - maybe "IStoreContext"? - that
    serves as the base URL off of which our checkout views live.  There
    would be three situations:

    a. In a default Plone install, the portal root would be both an
       IStore and an IStoreContext, making it both where you go to see
       configuration views, and also where you go to find the cart and
       checkout views.

    b. In a Plone install where the owner asks us to live beneath
       "/shop", their portal root remains an IStore, and so all of their
       Site Setup views would continue to live at places like
       "/Plone/@@manage-getpaid-content-types" (note that we allow
       ourselves here both the "@@" eyes looking at us and also the name
       "getpaid" in the view name, since this URL will be seen, if at
       all, only by site admins that expect that sort of thing), but the
       "/Plone/shop" object we create is both marked as an IStoreContext
       and also registered as the site's IStoreContext utility.  That
       way, URLs that say "/Plone/shop/checkout" would traverse to the
       IStoreContext and find the "checkout" view waiting beneath it,
       and checkout code that needs to generate URLs would just ask for
       the IStoreContext utility, then ask it for its URL, then slap
       "/cart" or whatever on their end.

    c. In a non-Zope framework, "/shop" would be configured using the
       host's URL registration framework as pointing at a special object
       that is itself marked as both an IStore and an IStoreContext, and
       that launches Zope traversal when a URL lands on it.  Any config
       views that survive in the other framework, as well as all of our
       customer-facing views, would live under "/shop" in the web site.
       We would create a global registry at start-up that advertises our
       "shop" object as both the registered IStore utility and
       IStoreContext utility.

 3. We abandon the modern idea of utility registration for the older
    idea we're currently using of merely being able to find our global
    options and ask it questions.  Recall that at the moment we find
    things out about the current payment processor, when that knowledge
    is needed, with code like this:

        portal = self.context.portal_url.getPortalObject()
        manage_options = IGetPaidManagementOptions( portal )
        processor_name = manage_options.payment_processor

    In other words, instead of asking for a utility, we ask for the
    portal root and then forcefully adapt it to the Options interface;
    that is, we pretty much insist, through the way we write our code,
    that configuration be stored on the site root itself, *not*
    somewhere else.  I would argue against using such code, since, if
    we're not in Plone, (a) there might not exist a site root, and (b)
    even if there were, it might not be the best place to put our
    configuration information.  I like the idea of an IStore utility
    that configures us and an IStoreContext utility where our views live
    because they are so abstract: they don't insist that we're part of a
    Plone site at all, they just require that whatever kind of web
    framework we're in be supplied with the ability to provide us with
    both of these utilities.

    Anyway, as I was saying: if we don't want to use utilities, we could
    hang everything off of the ability to get at the global options
    object (and, in other web frameworks, somehow fake things so that
    something that looked like a portal could be found through something
    like the above code).  So instead of asking for where the current
    IStoreContext is, we could just grab the global options, and ask
    them what URL our checkout views should live beneath; and the global
    options would have that information because it's through changing
    the global options that the store owner expresses their will.

Okay, enough options for right now. :-)

Now, it might be that I'm creating an artificial distinction here
between the "pretty" option of having an IStore and an IStoreContext
utility, and the "ugly" option of grabbing the portal root and making it
adapt to IGetPaidManagementOptions, because there is a third way between
these two extremes: we could register the options themselves as a
utility, and demand that utility as the first act we perform when we
need to learn where GetPaid lives or where it is wired up.

I admit, as I noted in an earlier email, that the idea of starting
*every* question about GetPaid with "go get the options" is very pretty,
because, that way, everything GetPaid needs to know is stored one place.
With my suggestion above that we store *two* different utilities in
every Plone site that uses GetPaid, we start the process of scattering
our configuration information far and wide, where lots of things
(site-wide registrations, for example) have to be changed or cleaned up
when GetPaid is reconfigured or uninstalled.

So, maybe the way to go it to turn the Options into a utility, so that
we can get to it without needing a portal-root to exist, and give *it*
an option that knows where in the site GetPaid is supposed to exist?
Hmm.  That wouldn't completely solve the problem, though, since for the
URL "/Plone/shop/checkout" to resolve, there's *got* to be a "shop"
object that actually, really exists in the site.  So I'm not sure we can
get away without having *some* site-wide registrations or object
creations going on, unless we give up the idea of Plone users being able
to put our URLs beneath some sub-URL.

And maybe that's an idea we should give up for now, and just try to nail
the two more "obvious" use-cases of Plone with our views living right
beneath the root, and of other web frameworks having us live beneath a
sub-URL, and in both cases we just ask for our Options and find out from
them how to compute our URL?

Hmm.

I should think about this more.

One last option: I think it is *possible*, last I checked, to have views
that (gulp!) live inside of other views.  So instead of creating an
actual "shop" *object* in the "/Plone" container of a site owner that
wanted our URLs to live under "/Plone/shop", which would then need to be
cleaned up or deleted later (and which the shop owner would see in their
table of contents and might delete by hand if they didn't know how it
had gotten there), we could register a named view on "IStore" named
"shop" that, itself, has views like "checkout" and "cart" registered for
it.  I think that the "vice" RSS syndication system did something like
that to construct interesting multi-part URLs beneath Plone folders
(like ".../recent/rss" and ".../all/atom"), but I'd have to go read
their code to remember how they worked.

--
Brandon Craig Rhodes   brandon@...   http://rhodesmill.org/brandon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Peter Fraterdeus-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message



On 27 Aug 2009, at 9:26 AM, Brandon Craig Rhodes wrote:

>
> Mikko Ohtamaa <mikko@...> writes:
>
>> Brandon, I like your enthusiasm towards Plone :)
>
> Thanks. :-)
>

As a lurker, I just wanna say I'm enjoying this immensely, and  
learning a hell of a lot about both getpaid and about Plone...

Thanks, all, and thanks Brandon for your explicit thought process!

Eventually, I'll integrate getpaid into my own site.

I've also got a "PrintShopOrder" product sketched out in ArgoUML, but  
that's going to take a few months of 'stimulus money' for me to ever  
make any progress on it ;-)

Cheers

Peter

Peter Fraterdeus
Exquisite Letterpress from Slow Print Studios
http://slowprint.com/


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Brandon Craig Rhodes :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


Mikko Ohtamaa <mikko@...> writes:

> Multiple payment processor branch also should support off-site
> processors.

Agreed!

> IPaymentProcessor interface should have not references to the
> user-inteface or Plone (Products.PloneGetPaid) so that other Zope
> frameworks (BFG, Grok) can use it.

Agreed; if possible, payment processors should simply *provide* views,
they should try very hard to avoid importing and fiddling with views
from the existing checkout process.

On the other hand, I think that our checkout forms need to be pulled out
of Products.PloneGetPaid and put somewhere else - maybe in a new package
named "getpaid.checkout"? - so that other frameworks can use them.
There's no reason that our forms shouldn't be able to travel to other
frameworks, I don't think.

> Well... I thought everything in Plone should be configured through
> ZCML nowadays... are we redesigning the system *again*? ;) ZCML is teh
> shit...

Having looked over some Plone documentation to remember exactly how
Plone people think about ZCML (as opposed to how pure Zope 3 people
think about it, or Grok people think about it), it looks like ZCML is a
way to create an ephemeral object that never exists in Python code.  To
(maybe) make the difference clear, let me try writing a single example
registration in two different ways:

 1. With a custom ZCML directive:

    <paymentprocessors:registerProcessor
      name="Off-site Google Payment Processor"
      selection_view="google-selection-view"
      review_pay_view="google-review-pay-view"
      />

 2. With a utility registration pointing to a class:

    <utility
      provides="getpaid.paymentprocessor.interfaces.IPaymentProcessor"
      factory="getpaid.googlecheckout.processor.GooglePaymentProcessor"
      />

    class GooglePaymentProcessor(PaymentProcessor):
        name = u"Off-site Google Payment Processor"
        selection_view = "google-selection-view"
        review_pay_view = "google-review-pay-view"

So, what are the important differences between these two registrations?

The most notable feature that I can see is that the first one creates a
"ghost" object that you can't actually import anywhere in Python code.
Clearly it creates something called a Payment Processor, that's made up
of other pieces; but where does this information go?  Clearly, into some
sort of special registry - that, if I want to introspect, I have to go
read about and learn the API of and write special, customized code to
interact with.

The second registration, by contrast, creates a registration that merely
*points* at an existing entity that I've already gone to the trouble of
creating in code.  This object has a name, "GooglePaymentProcessor";
it's clear where it lives (you could, in another application or in other
code, import it and do things with it); and it can be introspected using
good old-fashioned Python mechanisms that every experienced-enough
Python programmer knows how to use.

I have no problem with having GetPaid use ZCML to register payment
processors.  But I would like us to use approach #2, where the payment
processor actually *exists* outside of ZCML, inside of regular Python
code, because:

 (a) This makes our classes available to other frameworks.

 (b) This means that unit tests can proceed without always having to
     start up and run the ZCML and create an object registry, since the
     payment processor object "already exists" and can just be imported.

 (c) It means that in a distant future, if the Grok methodology ever
     wins the day, we can just remove the ZCML and have our classes
     auto-discovered instead - since all the real information, about
     what views and settings the payment processor needs and so forth,
     will already be in Python code and not trapped in XML.

> multiplepaymentprocessor branch should handle those case as well. I
> think it's just matter of having more view hooks available (checkout
> button,etc.).

Having stared at the use cases for a few hours, I finally think it's
safe to agree with you: by providing just a few more plug-in points, I
think we can indeed eliminate all special overrides that currently exist
in payment processor registrations.

> 1. In real-world the payment is done just right before you are leaving the
> shop as the last thing of your shopping experience
> 2. Thus, the payment method shoul be chosen as the last step of the wizard
> because people are familiar with this best practice
>
> To support my rationale, all major webshops are doing the payment method as
> the last choice. If you need to see example, try registering a domain in
> GoDaddy.

The choice between payment processors can only be made as the last step
if both of the payment processors that you are choosing between only
send you off-site for the last step.  If one of the choices your store
owner has enabled is Google Checkout with both the address and shipping
info collected off-site, then you have to ask whether they want to use
Google Checkout several steps *before* the last step:

               CHOICE
 checkout ---> Google Checkout ---> Google Checkout address, shipping, etc
  button       AuthorizeDotNet ---> @@address-info ---> @@review-pay --> ...

Having sketched out many scenarios, I am now convinced that, when the
store owner has selected more than one possible payment processor for
users to choose between, the checkout system needs to:

 1. Look at the possible payment processors.

 2. Look up the step on which each payment processor sends the user
    off-site (if any).

 3. One step *before* that step - which might put the choice on the
    cart's Checkout button itself! - ask which payment processor the
    user wants to use.

Essentially, the cart will have to be smart enough to "diff" the
available checkout processors, see how "early" in the checkout process
the first difference is, and ask right before it hits that step.

--
Brandon Craig Rhodes   brandon@...   http://rhodesmill.org/brandon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Michael Dunstan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Wed, Aug 26, 2009 at 1:04 PM, Brandon Craig
Rhodes<brandon@...> wrote:

>
> As Michael suggested last week, I have studied how all of the payment
> processors besides Google Checkout integrate with GetPaid; Michael, it
> was a great suggestion!  And, thanks to guidance which Christopher
> Johnson provided when we talked on Friday, I have also read through the
> various branches in GetPaid labeled "multiplepaymentprocessors".
>
>   (USABILITY PEOPLE: You can skip down to the "Big Questions That I
>                      Need Feedback On" section farther down if you want
>                      to skip the technical discussion.)
>
> As a result, I now have a more complete picture of how the various
> GetPaid payment processors are designed.  To keep things straight, I
> sketched out this rough diagram in Inkscape this afternoon:
>
>    http://rhodesmill.org/brandon/static/2009/getpaid-overrides.png
>
> Red text in the diagram indicates the presence of a ZCML override of
> some sort.  Note that I have left out extra steps that might be present
> in the wizard, like shipping options; when present, such steps would sit
> between the "Address Info" and the "Review & Pay" pages in the diagram.
>
> Payment Processors, and How They Work
> -------------------------------------
>
> Taking the diagram from top to bottom:
>
>  * Google Checkout:
>
>   The payment processor provides its own copies of the shopping cart
>   view and shopping cart portlet, so that it can draw a pretty "Google
>   Checkout" button on each of them.  The buttons point at a view named
>   "/google-checkout" on the Plone site, which has no associated
>   template but instead redirects directly to Google itself.
>
>   OPEN QUESTION: It is not clear to me why the checkout buttons do not
>   point at Google directly instead of going through the extra view, but
>   I see Google Analytics parameters being constructed on the way that
>   might have something to do with it.

The strongest reason is that the site itself needs to communicate with
Google Checkout before redirecting the shopper to Google Checkout.

It could be arranged that is communication is done when rendering the
checkout button and the result somehow used in that rendering. But
that would mean having to communicate with googlecheckout each time
the checkout button was rendered for the shopper.


>  * ClickandBuy / PagSeguro:
>
>   These both provide their own @@getpaid-checkout-wizard view that
>   removes all of the normal functionality, and shows a big button that,
>   when pushed, sends the user to an off-site checkout process.
>
>   OPEN QUESTION: This approach competes directly with the Google
>   Checkout approach.  Which is better: providing a special checkout
>   button for "Checkout"?  Or, letting them visit our normal checkout
>   URL then asking them to click off-site?  The former requires one less
>   click from the user; the latter gives them a bit more warning that
>   they are leaving.  Should we rewrite all three payment processors to
>   use a common approach, and then provide an admin config option for
>   whether a store owner wants the extra "Click here to checkout" step
>   added or not?

I think less clicking is for the shopper is good. And I think that
these off site processors try to provide strongly branded checkout
buttons which gives the shopper just enough warning. So you don't need
the intermediate page.



>  * PayPal:
>
>   This also provides its own @@getpaid-checkout-wizard, but the view
>   simply does an immediate redirect to PayPal.
>
>   OPEN QUESTION: Like with Google Checkout, the question is, why?
>   Because it was easier than overriding the "Checkout" buttons?  Or
>   because the extra step lets a few last variables and parameters be
>   put into place before the customer arrives at PayPal?

I don't know. Might be the same reasoning as the Google Checkout.



--
Michael Dunstan

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: overrides, chaos, and multiplepaymentprocessors

by Michael Dunstan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Wed, Aug 26, 2009 at 7:36 PM, Mikko Ohtamaa<mikko@...> wrote:
>>  1. Off-site processors that provide their own checkout wizards
>>    (Google Checkout, ClickandBuy, PagSeguro, PayPal)
>
> PayPal and Google Checkout support also more tightly integration with the
> existing web site without 1) taking the user away from the site

I don't think that is the case for Google Checkout. I don't think
Google offer that kind of service - yet.


> 2) managing
> the order on the payment processor site. The current plug-ins don't use
> them, however.

For Google Checkout - correct. (The processor is receiving
notifications - just nothing other than timely discarding of shoppers
carts is being done with those notifications.)



--
Michael Dunstan

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "getpaid-dev" group.
To post to this group, send email to getpaid-dev@...
To unsubscribe from this group, send email to getpaid-dev+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/getpaid-dev?hl=en
-~----------~----~----~----~------~----~------~--~---