godaddy-wordpress / wc-plugin-framework

The official SkyVerge WooCommerce plugin framework
Other
138 stars 42 forks source link

Multi-Currency Support for payment gateways #71

Open maxrice opened 9 years ago

maxrice commented 9 years ago

Multi-currency support is becoming a fairly popular request for our gateways. Let's investigate how we can implement a nice abstracted approach to multi-currency.

Gateways that support multi-currency

5 gateways that require separate credentials per currency. None that change API endpoints. Some of those accept a lot of currencies (Braintree supports over 100+), so I think our implementation would have to assume the currently entered credentials are for the store's base currency, and allow the admin to dynamically add additional credential fields for a selected currency. These should also be keyed to the environment field.

The framework doesn't have any concept of a credential field so this would have to be abstracted into perhaps a method that returns an array of those credential fields given a specific currency (either defined or from an order) so implementing gateways can parse them into whatever credentials it needs.

maxrice commented 9 years ago

moving to 3.3 milestone

maxrice commented 9 years ago

@ragulka @justinstern would be curious for your thoughts on the best way to implement this, let's chat next week

ragulka commented 9 years ago

My use case is a bit different - it's not multi-currency per-se, but multi-country. However, I believe it has some similarities. Swedbank is a gateway that operates in 3 countries - Estonia, Latvia, Lithuania. Each country has a different endpoint and credentials. But then there are a few others as well, such as SEB and Nordea, where the credential fields are different per country. For example, SEB Estonia has only 2 credential fields, whereas SEB Latvia has 4. That being said, each country might have a different currency as well.

I imagine that since those use cases are similar, they could be abstracted in some way - call it configuration data grouping, for example. The idea would be that a gateway may have different sets of credentials and/or endpoints. That set could be country, currency, etc. It should be left up to the gateway implementation whether these groups are predefined/hard-coded or if they could be dynamically added (as suggested for Braintree).

justinstern commented 9 years ago

I don't think it's necessary to go so far as trying to define an abstract "credential field" as mentioned in the issue description, I don't think there would be any value in doing so. There's not really anything special about credential fields; as @ragulka said this is really about grouping configuration settings, regardless of whether they're credential fields or not.

I think in general the tact we want to take is allowing/requiring the gateway/api classes to determine the best endpoint, credentials, etc, based on all the information it has available. So for instance we could already easily today use a different endpoint based on the particular country/currency, simply by implementing the concrete gateway/api to do so.

Similarly I'm not sure whether we could/should abstract the notion of country/currency-specific credential fields into the framework itself. This stuff is always tricky for me to imagine until I'm actually playing around in the code, but it seems to me that the general approach should be to make it easy when creating the plugin settings admin to define fields that are conditional/associated with another field (e.g. country, or currency), and then have the gateway/api classes worry about which set of credentials, etc, to use based on the order information, or whatever other data is available and pertinent to it.

We already have the environment facility to look at for inspiration, and I see these country/currency configurations being similar, but different.

For instance from a UI/UX perspective, the environment setting is a single-select: you select the current environment you want the plugin to operate in, enter the credentials/settings that are available for that, and that's what's used. It's also been straightforward to hardcode these environment-based credential options into the settings array, suffixed by environment name, since there's a limited number.

These countries/currencies would have to be done as multi-selects: perhaps a merchant wants to support transactions in GBP, USD, and EUR, so for Authorize.net they would select those three from the list of available currencies, and they would get three sets of API login ID/transaction key setting pairs displayed, one for each currency. The framework would have to take care of dynamically adding these currency setting fields via JavaScript in the admin when the new currency is first selected. We'd also have to deal with dynamically adding these fields to the settings array in PHP so that they're handled correctly by the WC settings API. We'd need a new way of getting them from the gateway class since we wouldn't be hardcoding each and every currency/credential pair as we currently are able to do with the limited number of environment-dependent fields.

Since we couldn't enumerate all of the credential fields for the 100+ currencies supported by Braintree, we'd need some way of defining a set of "prototype" fields which can be used for any selected currency.

Country-specific fields I'd think would be handled differently from the currency ones, probably more similar to our current environment fields. For instance in @ragulka's example of Swedbank operating in 3 countries - Estonia, Latvia, and Lithuania, each with their own specific set of fields, I'd see us defining a country list with those three values in the settings array. Then we might hard-code all of the individual Estonia, Latvia, and Lithuania settings, and associate them with their respective country, similar to how the environment-specific fields are defined currently. The difference again being that more than one country could be selected at once, so multiple sets of fields could be shown, instead of the single set that environment-based ones give you. But at least from the sounds of it we wouldn't need to get into the dynamic generation of fields as with the currency ones.

so I think our implementation would have to assume the currently entered credentials are for the store's base currency, and allow the admin to dynamically add additional credential fields for a selected currency.

This makes sense to me

Some open questions for me:

ragulka commented 9 years ago

What would the interaction between environment fields and these conditional fields look like?

In Swedbank, I've implemented it very similar to the environment field. The environment field applies to all countries, and is not country-specific. So if you change the environment to Test, all the countries will use the test environment. Of course, in my use-case, the country field is a single-select, so you can only use one country at a time.

I think it makes sense to apply environment to all countries/currencies, as you're not likely to test one currency and be live with another (what would be the use case for that anyway?).

Actually I'm hoping that dealing with different configuration options for various currencies within a particular country, is not something that we have to even worry about as it would seem to complicate matters quite a bit.

I agree and I actually do not think that there are a lot of use cases where a gateway would even support multiple currencies per country, right? I would assume that if a gateway is country-specific, then it will only accept payments in that country's official currency.

Can we get away with supporting a single of these "configuration groupings"?

Unless there are gateways out there that require both, I'd say we can. I cannot think of any gateways that would require both.

justinstern commented 9 years ago

Of course, in my use-case, the country field is a single-select, so you can only use one country at a time

Oh ok, this is good to know, so we'd want to make sure that our generic conditional field type can be either single-select (as you did for countries), or multiselect (as we would need for currencies)