Sorry for being quiet for some time. Regarding the recent discussion about dependency injection and testing, here are my thoughts:
- I think it's a great idea to have 3 different implementations for production, API testing and unit testing. Dependency Injection is the proper pattern to achieve this (I should thank my Software Engineering professor for teaching me this :P)
- I agree with Frank that a subclasses Payment will no longer be needed in this case. Applying the pattern, a payment submodule will contain 3 payment gateway classes: ProductionTestGateway, APITestGateway, and UnitTestGateway, each of which can be injected to the Payment object when a payment is about to be processed. The Payment class will handle the payment info directly since the info is the same for all gateways.
- Following the pattern, we can have a PaymentController class inside each submodule to handle redirection. It will also be injected to the Payment object. I think a default controller should also be included in the Payment module in case the submodule developer doesn't want to customize redirection.
- Should there be a class for processing recurring payment in the submodule as well? If I'm not wrong, each gateway processes recurring payment differently. Using DI we can inject the submodule's RecurringPaymentProcessor to the RecurringPayment object. Let me know what you think about this.
I am aiming to finish my proposal by today. I saw from the Wiki that Frank and Jeremy will be the mentors for this project. Am I allowed to send you my proposal for reviewing before I submit it to GSOC?
Thanks so much for your wonderful supports so far!
@Jeremy Correct me if I'm wrong, I don't think we need to extend Payment in the payment submodules, like it is now, just create the payment gateway classes that can be injected into the Payment class. Then the dependency is used when processPayment() or similar is called in the Payment class. You could have RecurringPayment extend Payment perhaps if there is some shared API/functionality.
Controllers can be quite useful for handling the return URLs like you mention, maybe we could use DI for that as well? Then a developer could inject a controller that will handle redirect back to the paid object or you could have a generic controller for that particular gateway that always redirects back to /x/y/z like Paystation.
I also use PaidObject(), I don't have a problem with storing this info in Payment personally. Unless you want to add a has_one PaidObject to Payment and developers need to extend PaidObject for their Order class or something?
Re collecting the right info and validating it - is there much difference in the info needed by different gateways? Perhaps we could have a set of form fields which validate themselves and get the fields from the gateway service class that is injected? A developer could pull these form fields into their checkout form or similar, different gateways might have a different set of required data like you say.
processPayment() and validate() would use the injected class for that particular gateway or something like that?
One other question, do we need to decorate Member with address fields and have sending receipts as part of this payment module?
The payment class should be able to reference the object that was paid for and the member who paid for it, but often a shipping address and billing address are specified - maybe it's cleaner to decorate Member with has_many Address? And just have a hook for developers to use in order to send receipts.