thephpleague / omnipay

A framework agnostic, multi-gateway payment processing library for PHP 5.6+
http://omnipay.thephpleague.com/
MIT License
5.94k stars 928 forks source link

SOAP gateway #388

Open delatbabel opened 8 years ago

delatbabel commented 8 years ago

Not much of an issue, more of an announcement.

I recently had to write a new gateway plugin for a gateway that uses SOAP with WSDL. Previously I had worked on the First Data SOAP gateway but that ended up having to be done manually because the WSDL file emitted by the gateway wasn't in fact valid WSDL and the PHP native SOAP client wouldn't parse it.

So I went looking for another gateway to use as an example and couldn't find one. There was the CyberSource SOAP gateway but it was broken to the extent of not even compiling (I'll have to submit some PRs at some point for that one) and didn't actually send requests. I realise that the PHP native SOAP client is not particularly great but there should be at least one working example of a gateway plugin using that SOAP client in the absence of another better one.

So I built the Allied Wallet SOAP gateway using the PHP native SOAP client and it works (I have a demonstrator script which makes actual transactions on the gateway and they appear on the merchant dashboard). It's available for use/inspection/forking/copy-and-paste here: https://github.com/delatbabel/omnipay-alliedwallet

Also it has tests that make use of phpUnit's ability to mock a SOAP client using the getMockFromWsdl() method. These provide 100% test coverage of the gateway methods without having to make actual SOAP calls to the gateway.

There were a few challenges -- mostly in having to over-ride the AbstractGateway and AbstractRequest constructor to allow passing in a SoapClient object so that the client could be mocked by the test cases. Using a mock httpClient and httpRequest isn't sufficient for SOAP because it uses PHP internal methods that don't allow interception by guzzle, and unless guzzlehttp has a SOAP client along with it we can't mock a SOAP client by mocking methods that it doesn't call. So I guess a possible v3.0 change is to add this SoapClient or an interface that defines it into the base constructors and having it built as required.

Another question is whether we do in fact need an exemplar, or set of exemplar gateway plugins for use by gateway plugin builders to use as base examples -- one for REST, one for SOAP, etc?

barryvdh commented 8 years ago

If we use autowiring in v3, it shouldn't matter which classes you need in the Abstract class. Most of the time, the default PHP SoapClient is used for SOAP, right? So don't think we need to create an interface for that then.

delatbabel commented 8 years ago

Most of the time, the default PHP SoapClient is used for SOAP, right?

Unfortunately I don't think that's the case. The default PHP SOAP client isn't very good and quite a few of the gateways that do SOAP or SOAP-like things have comments in the code in a few places saying things like "the native PHP SOAP client doesn't work for this case so we have to do X instead".

https://github.com/thephpleague/omnipay-firstdata/blob/master/src/WebserviceGateway.php#L22 as a case in point. (follow the trail to the PHP bug -- the core issue is that SOAP allows overloaded functions, PHP does not allow overloaded functions or class methods, PHP SOAP tries to implement SOAP functions as PHP class methods and so whenever a WSDL file defines overloaded functions the PHP native SOAP client b0rks).

There are other SOAP implementations out there which work for this case and other cases -- besoap is one particular implementation.

I suspect that an interface and then an adapter or wrapper class around PHP's native SOAP implementation, as the default implementation, is what is going to be needed. In addition an interface that has an implementation that is stubbable or mockable will be required for unit testing.