wc-plugin-framework icon indicating copy to clipboard operation
wc-plugin-framework copied to clipboard

Multi-Currency Support for payment gateways

Open maxrice opened this issue 10 years ago • 6 comments

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

  • Authorize.net (AIM/CIM) - requires a separate merchant account (API login ID/transaction key) per currency. API endpoint does not change. See WooIdea
  • Beanstream - requires a separate merchant account (ID) per currency. API endpoint does not change.
  • Braintree - requires a separate merchant account (ID) per currency. API endpoint does not change.
  • CyberSource - no special configuration required, include currency in transaction request.
  • Elavon Converge - no special configuration required, include currency in transaction request.
  • First Data/Payeezy - no special configuration required, include currency in transaction request.
  • Moneris - requires a separate merchant account (store ID/API token) per currency. API endpoint does not change.
  • PayPal Express - no special configuration required, include currency in transaction request. Details here
  • Realex - no special configuration required, include currency in transaction request.
  • USA ePay - requires a separate merchant account (source key) per currency. API endpoint does not change.

Summary

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 avatar Jan 28 '15 20:01 maxrice

moving to 3.3 milestone

maxrice avatar May 05 '15 23:05 maxrice

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

maxrice avatar Aug 01 '15 03:08 maxrice

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).

ragulka avatar Aug 03 '15 18:08 ragulka

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:

  • What would the interaction between environment fields and these conditional fields look like? Would our hypothetical multi-currency Authorize.net plugin have a single environment field that is used for all three currencies/accounts, or would each currency/account also have its own independent environment field? I guess a related question is whether for payment methods like SEB, can you assume that all countries even use the same environment field? So is it always possible to have a single environment setting for a gateway regardless of the country/currency being used?
  • I have no idea what the interaction between both a country option and currency option would look like, as alluded to by @ragulka. 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.
  • Can we get away with supporting a single of these "configuration groupings"? Seems like it would be easier if we say that the gateway author can define a list of currencies and related settings, or a list of countries (or whatever) and related settings, but not both.

justinstern avatar Aug 05 '15 04:08 justinstern

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.

ragulka avatar Aug 05 '15 06:08 ragulka

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)

justinstern avatar Aug 05 '15 19:08 justinstern