@danielle, @lin_d_hop, @serenity, @Kirsten, @MyriamBoure, @stveep
Preamble
Allowing enterprises to accept payments in an ‘automated’ way (ie. without customer interaction every time) is a fundamental component of the Standing Orders feature. It would also be a very useful addition to the existing checkout flow, allowing customers to checkout quickly without having to enter credentials each time. There are several candidates for allowing such automated payments to be accepted: Credit Card Gateways: (eg. Pin, Stripe, PayPal), e-wallet systems (Mangopay, PayPal) or a more sophisticated customer account credit system managed within the OFN.
The current thinking is Aus is that the although an internal customer account credit system would certainly be a suitable solution for many enterprises, and is a feature that should be built at some stage, such a system is ideally implemented in conjunction with a separate system for automated top-up (credit card / e-wallet), so that enterprises do not need to manage account credit top-up manually.
Mangopay appears to be a popular option in France particularly, but due to it’s current level of availability outside of Europe (ie. none), it does not represent suitable option at present. Pin appears to only work in Australia (?) and so falls into the same basket.
While PayPal has a far more global reach than Stripe, Stripe is available in all countries with an active publicly available instance of OFN, as well as most countries where instances are under active development (with the exception of India, any others?). PayPal is markedly more expensive in most places, and I get the impression it is a fair way down many people’s list of favourite companies…
That leaves us to consider Stripe the prime candidate for an initial cut at this, at present (speak up now if you have a better option). Thankfully, Stripe also has an exceptionally well documented API and an existing and well maintained ActiveMerchant gateway (which is already available for use in the OFN via Spree).
Of course the ideal solution would be to implement other payment gateways (such as MangoPay and Paypal) as well as an account credit feature, and the hope is that we will do this in the future, we’re just looking for a good candidate for an initial cut.
How Stripe Works
Stripe does lots of cool things, but in terms of the specific use case at hand, the most interesting feature (not unique to Stripe) is the ability to store a customer’s credit card and charge it at a later date, without having to store any of the card details on our servers (or even send card details through our server: by using stripe.js). This drastically reduces the amount of work required to comply with the industry body’s standards (PCI compliance). The way this works is that the customer enters their details once and we then send a request to Stripe to store those details on their system. Stripe sends back a single-use token that we can use to either charge that specific customer for a single order, or to ask Stripe to store the associated card details permanently for later use against any number of subsequent orders.
Based on my reading so far, there are two main ways that we can deal with authorisation for doing this on behalf of OFN users.
- Use Stripe Connect, to allow OFN users to authorise the instance to process payments on their behalf.
- Get users to enter their publishable and secret Stripe API keys directly into the OFN (via a payment method), where they will need to be stored in the database, and used to charge customers directly.
Option 1 (Stripe Connect)
This looks like the best option for a platform like the OFN, as it is designed for platforms that wish to process payments on behalf of their users (ie. enterprises). To use this setup, each instance of OFN would need to have a Stripe account, and all of the users of that instance who wish to use Stripe would authorise the instance to process payments on their behalf. NOTE: this does not mean that money travels through the instance’s Stripe account, only that the instance is authorised to charge customers on behalf of it’s users.
The main advantage of the Stripe Connect option is that is massively simplifies the management of security around connected user’s Stripe accounts. Once users have connected their Stripe accounts (a process which Stripe can handle for us) all subsequent requests are authenticated using a combination of their account id (public) and the instance’s secret API key. This means that the only sensitive information we need to handle is the instance’s API key, which can be stored in an ENV variable on the sever, thus rendering any information stored in the database worthless without it.
In theory this means that only way that an attacker can do bad things with payments is if they have shell access to the instance’s server, in which case the instance is in a lot of trouble anyway. NOTE: even in this absolute worst case scenario, OFN customers could not be charged directly by a malicious attacker, the worst that could happen is that an existing customer of an existing enterprise could be charged into that enterprise’s stripe account.
Option 2 (storing API keys)
This would also work fine, as it is the way that Stripe is designed to work for many users (ie. on single-merchant websites). However, when we are thinking about storing a large number of keys in a single database, this becomes a bit risky, or at the very least the stakes are raised. In particular: in the absence of a strategy to encrypt the user’s API keys in the database, this approach would effectively enable anyone who got a copy of an instance’s database to charge an enterprise’s customers with stored details on behalf of that enterprise. ie. not very desirable. This is of course something that we want to avoid at all costs anyway, but we would probably have to consider additional controls around this data if we were to take this route… NOTE: in the same way as Option 1, even in the worst case scenario, OFN customers could not be charged directly by a malicious attacker, the worst that could happen is that an existing customer of an existing enterprise could be charged into that enterprise’s stripe account.
Progress
As mentioned, Spree ships with ActiveMerchant, which provides an out-of-the-box Stripe gateway. I have spent about a day or so looking through the Stripe documentation and playing around with the codebase in an attempt to get the basic ActiveMerchant Gateway working, which I was able to do relatively easily. The work I have done so far is available here.
The main issue that I have come across is that Spree’s implementation of the Stripe gateway is designed around single-merchant shops, and so is set up to use the regular single-user Stripe (Option 2), rather than Stripe Connect (Option 1). This means that out of the box, it requires users to enter their API keys into the configuration for the relevant payment method, where they are stored. As mentioned above, this is likely not an ideal situation because anyone who gets access to the database (or a copy of it) has the ability to charge any stored customers to the relevant enterprise’s Stripe account for any amount the card can handle.
Next steps (round 1)
Round 1 would be getting Stripe Connect set up, and allowing enterpises to offer Stripe-processed credit cards as a payment method.
1. Develop strategy for storing instance API key, and turning on feature
Probably pretty simple, just an ENV variable for the instance’s API key and a feature toggle for Stripe payments?
2. Determine which model to store connected stripe account ids against
Do we store against an enterprise, or a payment method, or against the user? If we want to use Stripe’s connect process, we need a URI that we can redirect to, which may not work so well with either interface…still working on this one.
3. Build UI for connecting accounts
Depends a lot on what we decide for (2). Stripe’s recommended connect process involves redirecting the user to Stripe, where they can confirm that they would like to connect their account, before returning them to a URI we specify…
4. Rebuild the UI for configuration of Stripe payment methods
Again, depends a lot of what we decide for (2), but basically we need to scrap the vanilla UI which ask for a a publishable and a secret API key, and replace it with one that allows us to select an id for a stripe account that has already been connected to the instance’s Stripe account. Should also notify the user if no connected Stripe accounts are available to choose from…
5. Tweak the out-of-the-box setup of the Stripe gateway to work with Stripe Connect
From what I have read, this may not be a serious issue, as the Stripe API appears to be designed to work in much the same way for either option, so hopefully we will just need to tweak the auth headers we send to Stripe in various places. As good as the Stripe docs are, I don’t have a definitive answer to this yet. Difficult to be sure until we actually try.
Next steps (round 2)
Round 2 of work would consist of the work required to allow customers to store their card details against an enterprise, and for the OFN to auto-charge this card where appropriate…
1. Add a model for holding the customers stored credentials (eg. card details) for payment methods they authorise
This could be a generic model, which could potentially be reused for PayPal/MangoPay, etc.
2. Build a UI to allow customers to store their credentials (eg. card details)
Again, if we can design this to be as generic as possible that would be ideal.
The idea would be to redesign the front-end ‘Account’ page, so that in addition to seeing their balance with each shop they engage with, they can also store their things like credit card details for later use, and maybe (later) set preferences about how they interact with each of those shops…
3. Tweak checkout UI to allow use of stored payment credentials where they exist.
Same interface should allow customers to store any card details they enter at the checkout for later use.