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 -~----------~----~----~----~------~----~------~--~---
