On Aug 18, 2009, at 12:54 PM, Brandon Craig Rhodes wrote:

> Okay!  I've done a bit of successful work, and now need some serious
> input from the community to tidy it up.

Thanks for your effort so far, and the time taken to explain the  
choices you're facing.

> 1. How should we "look up" the active Payment Processor?
> ---------------------------------------------------------
>
> I was rather surprised when I saw the code that determines which  
> payment
> processor is the currently selected or active processor for the site.
> One instance of the code lives in ``PloneGetPaid/browser/ 
> checkout.py``::
>
>        siteroot = getToolByName(self.context,  
> "portal_url").getPortalObject()
>        manage_options = IGetPaidManagementOptions(siteroot)
>        processor_name = manage_options.payment_processor
>
>        if not processor_name:
>            raise RuntimeError( "No Payment Processor Specified" )
>
>        processor = component.getAdapter( siteroot,
>                                          interfaces.IPaymentProcessor,
>                                          processor_name )
>
> There are three steps here.  First, something called a "tool" is  
> used to
> select the "siteroot" by using an ASCII name string.  This is a bit  
> odd
> to someone like me from the Grok world, where we find the current site
> root by calling "get_application(current_context)", but if it's the
> normal way of doing things in Plone then I'm cool with it.

In Plone you can do:
   from zope.app.component.hooks import getSite
   portal = getSite()
as long as you're in code that is running after publish traversal (the  
current site gets set in a module thread local during traversal)

(In Zope 2.12 this moves to zope.location.getSite IIRC)

But using the portal_url tool as is done here is not uncommon.

> Second, the GetPaid "Manage Options" are pulled out of the site: these
> are all of the options controlled by the "Site Setup -> GetPaid"  
> series
> of forms.  I find it a bit odd that they're not available as a  
> utility,
> so that you could just ask for "IGetPaidManagementOptions" and have  
> the
> site lookup occur automatically as part of utility resolution.  But,
> again, if this is the Normal Plone Way then it's fine.
>
> Finally, we pull out the value of the "payment_processor" option and
> then manually look up the IPaymentProcessor registered as an adapter  
> of
> the siteroot under that name.  This is where things are just weird  
> to me. :-)
>
> Isn't the whole point of the site root, and of local utilities in Zope
> in general, that you can ask for a resource that - without your having
> to know - is customized within the current site, and you just  
> magically
> get the right answer without knowing that?  This stunt, of adapting  
> the
> site root to an interface of a particular name that the user has  
> chosen,
> just seems like a long-way-'round procedure to simulate what happens
> more naturally if you just create an ISelectedPaymentProcessor  
> interface
> and have the payment processor selection form register the chosen
> processor as a site-wide utility uniquely providing that interface.
>
> Using a utility - if, indeed, they exist under Plone the way we're  
> using
> Plone here; maybe utilities are a Zope 3 feature that's not fully
> supported under Plone yet, and that's the reason for these idioms? -
> would also make the ZCML registrations of each payment processor less
> odd.  Currently, the Google Checkout processor, to take one example,
> says things like this in its ZCML::
>
>   <adapter
>      for="Products.CMFCore.interfaces.ISiteRoot"
>      provides="getpaid.core.interfaces.IPaymentProcessor"
>      name="Google Checkout"
>      factory=".googlecheckout.GoogleCheckoutProcessor" />
>
> In what sense, we have to ask when reading this declaration, is a
> payment processor an adapter of an ISiteRoot?  Normally an adapter  
> takes
> a feature-rich object, and gives it new behaviors.  But does a site  
> root
> really provide behaviors which an IPaymentProcessor is improving  
> upon so
> that they are able to process payments?  It's a very odd way of  
> looking
> at the problem.  Declaring an IPaymentProcessor to be an adapter  
> seems,
> again, to be a way to do manually what utilities do automatically; it
> seems to be a way to open a window to the site so that things like its
> URL can be grabbed.  But surely a payment processor isn't really a  
> more
> specific adaptation of a site root, just because it needs the site's
> URL?

Utilities are certainly supported in Plone, and I agree that a utility  
would probably be more appropriate in this case. It's possible that  
this was an attempt to avoid using persistent local component  
registrations.

> 2. How should the "Checkout" button view be rendered?
> -----------------------------------------------------
>
> <snip>
>
> What I would really like to do, each of the two places I need a  
> checkout
> button rendered, is to just go like this::
>
>  <a tal:replace="structure context/@@getpaid_checkout_button"/ 
> >Checkout</a>
>
> The question is: how should the view multiplex so that it winds up
> consulting the currently active payment processor to determine what  
> the
> button should look like?  There are at least two options, and maybe  
> more
> that you'll think of and share with me:
>
> 1. Have a single ``@@getpaid_checkout_button`` view defined by
>    PloneGetPaid.  When called, it does the current-payment-processor
>    dance, gets from the payment processor the path to the template it
>    should use (or, better yet, a class to use as the view), and then
>    returns the rendered result.
>
> 2. Have several ``@@getpaid_checkout_button`` views, one for each
>    payment processor, and have the site owner's selection of payment
>    processor automatically turn on its particular view.  I know that
>    this sort of thing is possible in Zope 3, by having the submission
>    of the "Payment Processor" form trigger the un-registering of the
>    old processor's ``@@getpaid_checkout_button`` view and the new
>    registering of the new processor's view.  But, given the look of  
> the
>    current-payment-processor dance quoted way up above (how long is
>    this email so far, anyway?), it looks like maybe the GetPaid team
>    doesn't like registering and un-registering lots of things - or  
> even
>    a few things - when a new change processor is selected.  I guess
>    that's dangerous because registrations would always have to be
>    persisted across site upgrades and everything?  Which would make it
>    preferable, each time we need the current payment processor, to
>    start with the site root and look it up afresh?  If so, then
>    approach #1, above, is fine; I just wanted to ask in case a
>    view-registration-driven approach hadn't yet been thought about and
>    wound up having advantages.  Not that I can see any. :-)
>
> So: my primitive methods-returning-strings mechanism needs to be
> replaced with something fancier like a view.  I like option #1, I  
> think,
> but wanted to ask since Plone programming is still new territory for  
> me.

Option 1 sounds reasonable.

A variation on option 2 would be to register views and resources  
particular to a certain payment processor to a unique browser layer.   
A browser layer (see plone.browserlayer) is a marker interface which  
gets applied to the request during publish traversal based on the  
existence of a local  
plone.browserlayer.interfaces.ILocalBrowserLayerType utility, and can  
then be used to limit the registration of a view to sites with that  
utility installed.  Typically the utility is installed via a  
GenericSetup profile when an add-on product is installed, but there  
are also helper methods in plone.browserlayer.utils that can be used  
to add/remove layers.  This will cause breakage, though, if the code  
for a product with an installed browserlayer is removed, because their  
is a persistent ZCA registration involved.  So option 1 is probably  
safer.

> 3. Is Google Checkout a Payment Processor?
> ------------------------------------------
>
> <snip>
> We should therefore have two entirely separate classes of object.
>
>   I.  Checkout wizards.
>
>       A. Google Checkout.
>       B. On-site GetPaid Checkout, which needs a Payment Processor.
>
>   II. Payment Processors.
>
>       A. Authorize.Net
>       B. PayPal
>       C. And so forth...
>
> A Checkout Wizard offers, at the very least, to render a button, if
> asked, that will send the user to its first page when clicked.
>
> A Payment Processor takes a credit card number and processes it.

I think this is a helpful distinction.

Note that getpaid.paypal is also really more of a wizard than a  
payment processor -- it uses Paypal's "Website Payments Standard"  
service and sends you off-site to complete the details of your  
payment.  getpaid.payflowpro uses a server-side integration with  
Paypal's PayFlowPro service, and I think would be considered a payment  
processor.

FWIW, we also have another existing wizard of a sort, getpaid.formgen,  
which can be used to populate a getpaid cart and order based on values  
from a PloneFormGen form.  The product supports either being used to  
simply create a custom form for adding items to the cart (after which  
the standard getpaid checkout wizard would be used to check out), or a  
"one shot" mode where getpaid.formgen actually creates a temporary  
cart, places an order, and calls the authorize method of the selected  
payment processor (this is the mode that makes me think it could  
actually be considered a separate checkout wizard).

peace,
David

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

Reply via email to