by Brandon Rhodes • Home

GetPaid needs customizable forms

Date: 10 September 2009
Tags:computing, plone, python, zope
GetPaid for Plone logo

I would like some advice from Zope and Plone folks about how to create forms that are not only easy for other developers to specialize, but which allow several specializations to be composed together. While I have used zope.formlib and z3c.form before for simple tasks, I have not yet been able to tell whether they support these more advanced kinds of operations.

Some background: I am doing some work on GetPaid for Plone with the generous funding of Derek Richardson who, if his dreams had not carried him away from grad school at the end of the Spring semester, would have tackled this same work as part of the 2009 Google Summer of Code.

The current mechanisms that GetPaid provides for customizing its checkout process are very primitive, and my task is to improve them. That is why I have been thinking about customizing forms.

Plugging in payment processors

When a Plone user has filled their shopping cart with goods and presses the “Checkout” button, GetPaid begins stepping them through a checkout process of several forms that must all be satisfied before, finally, the user's credit card will be charged and their purchase recorded. GetPaid's current definition of a “Payment Processor” requires only that it provide three functions authorize(), capture(), and refund() that talk on GetPaid's behalf to an online merchant account.

This design works fine for traditional payment processors that are comfortable letting Plone handle the credit card information, but these days site owners usually want to delegate the dangerous task of handling card numbers to an “off-site” payment site. And these off-site payment processors turn out to be but one example of the many ways in which GetPaid expansion modules want to customize (or hijack?) the GetPaid checkout wizard. Consider the following scenarios:

So, currently, in pretty much every one of the above situations, the answer to, “How do I tweak the GetPaid checkout process?” is, “Override the entire view or module that you need to customize, by providing your own copy that looks exactly the same as the default one, except for the one thing you need changed.”

If you are curious, I have created a diagram showing which payment processors have to override which parts of GetPaid.

Not only does this wanton overriding mean that modules have to meticulously track the changes that GetPaid makes to the forms they are overriding, but it means that customizations are inherently not composable: you can't put two of them together.

The problem of composability is a big one. Imagine that you live in Argentina and want to install a module that adjusts GetPaid's mailing address form to match the way postal addresses are formatted locally, but that you also want to use an off-site payment processor that needs to insert its custom “Checkout” button on the bottom of the mailing address form. Since the two modules will provide competing versions of the entire mailing address form, only one of them can “win”, and the customization that the other module wants to present will not be displayed.

Four crazy ideas

How might checkout process customizations become operations that could be combined and composed, instead of each one of them being a fragile, blanket replacement of an entire part of GetPaid? Here are four ideas that I have thought through today as I sat with my trusty pencil and yellow pad:

Is there anything here worth trying?

These are just the ideas that have come to me today, as I've scribbled and sketched and sat down to re-read all of the design patterns in the Gang of Four Book looking for inspiration. (You might recognize the third option above as something like the “decorator” design pattern!) Does zope.formlib or z3c.form or anything else like that in the Zope universe provide the kind of services I would need from a form library to implement the ideas outlined above?

If you're a Zope or Plone person with an inkling of an answer, then please let me know!

comments powered by Disqus