Git Product home page Git Product logo

cashier's Introduction

Cashier

Build Status Deps Status Hex Version Join the chat at https://gitter.im/swelham/cashier Open Source Helpers

Cashier is an Elixir library that aims to be an easy to use payment gateway, whilst offering the fault tolerance and scalability benefits of being built on top of Erlang/OTP

Usage

The following are basic usage examples on how to use cashier in it's current state. This library is being activily developed and is likely to change as we move towards the first release.

Setup

Add cashier as a dependency

defp deps do
  {:cashier, "~> 0.2.0"}
end

Make sure the cashier application gets started

def application do
  [applications: [:cashier]]
end

Config options

use Mix.Config

# cashier options
config :cashier, :cashier,
  defaults: [
    currency: "USD",
    gateway: :paypal,
    timeout: 20_000 # this option is the OTP timeout setting
  ],
  # this option is passed directly into HTTPoison and can contain any
  # of the valid options listed here - https://hexdocs.pm/httpoison/HTTPoison.html#request/5
  http: [
    recv_timeout: 20_000
  ]

# PayPal specific config
config :cashier, :paypal,
  # Please note the PayPal gateway currently only supports the /v1 endpoint
  # and this is automattically added for you
  url: "https://api.sandbox.paypal.com",
  client_id: "<paypal_client_id>",
  client_secret: "<paypal_client_secret>"

Cashier request examples

alias Cashier.Address
alias Cashier.PaymentCard

address = %Address{
    line1: "123",
    line2: "Main",
    city: "New York",
    state: "New York",
    country_code: "US",
    postal_code: "10004"
}

card = %PaymentCard{
    holder: {"John", "Smith"},
    brand: "visa",
    number: "4032030901103714",
    expiry: {11, 2021},
    cvv: "123"
}

# Note: The result return type for each request is currently the decoded
#       data returned from the payment provider, this will change in the future.

# Purchase request
# the card parameter can be either a %PaymentCard or stored card id 
case Cashier.purchase(9.99, card, [billing_address: address]) do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

# Authorize request
# the card parameter can be either a %PaymentCard or stored card id 
case Cashier.authorize(9.99, card, [billing_address: address]) do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

# Capture request
case Cashier.capture("<capture_id>", 19.45, [final_capture: true]) do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

#Void request
case Cashier.void("<void_id>") do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

#Refund request
case Cashier.refund("<refund_id>", [amount: 9.99]) do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

#Store request
case Cashier.store(card, [billing_address: address]) do
    {:ok, result}     -> IO.inspect result
    {:error, reason}  -> IO.inspect reason
end

#Unstore request
case Cashier.unstore("<card_id>") do
    :ok               -> IO.puts "card unstored"
    {:error, reason}  -> IO.inspect reason
end

Todo

All current todo items are listed on the issues page.

Please add any issues, suggestions or feature requests to this page.

cashier's People

Contributors

codetriage-readme-bot avatar gitter-badger avatar swelham avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cashier's Issues

Store card information with service

Services such as PayPal and Stripe provide ways to store a customer's credit card information securely with the service. It would be nice to have API in Gateways supporting storing/deleting such records.

Support for subscriptions

Major gateway portals include support for subscriptions, see:

https://developer.paypal.com/docs/classic/use-cases/uc_subscriptions-subscription-payments/
https://stripe.com/docs/subscriptions/quickstart

Some significant questions for subscription support:

  • how to handle the async notifications (e.g. success/failure of payment); as these are implemented as service-specific web-hooks, it perhaps makes sense to provide per-gateway Plug.Router implementations that can be used for this with an interface defined for callbacks that must be implemented by the host application (since database access, email generation, service cancellation, etc need to be handled but are all application specific).

  • should Subscriptions be a new module, or added to the existing gateway interface? My gut feeling is that this should be a new module that uses a gateway, but which provides the various bits of plumbing for Subscriptions. This way, those who do not need Subscriptions can ignore them, and one has a nice separation of concerns between payment mechanics and subscription/recurring payments

Currency based routing

Currency based routing will allow the client to configure cashier to automatically select which payment gateway to use for a given currency. This configuration should allow for multiple payment gateways to be specified per currency, which will determine the failover order should one of the gateways exceed the maximum number of allowed failed attempts. This config will override the application level gateway failover config.

Common response format

Currently there isn't a common response format returned from the cashier module that would cleanly work across payment providers.

The aim of this issue is for open discussion and suggestions on what data is commonly required by the client across payment providers.

To set some context, currently calling Cashier.purchase(123, some_card, opts) with success will return {:ok, data}. In this case data is just the decoded result that was returned from the payment provider. In order for the client to make use of the result, it would have to know which gateway was used for that request.

The result of this would mean clients who may not care which gateway handled the request or just need to generically log which gateway was used will be forced into detecting this manually and removes the purpose of having a common interface into the gateways.

The response needs to contain common results about the request and provide a way to identify which gateway handled the request. It should also return the raw data received from the payment provider for use cases that may fall outside of the common functionality of cashier.

Gateway failover

Gateway failover will allow selecting an alternative gateway should a given request fail for the primary payment gateway. This will be an optional config option that will allow the client to specify a list of known payment gateways which shall be treated in the order they are defined in the config.

Gateway feature support metadata

Would it make sense to allow gateways to return what features they support? e.g. a simple map like:

%{ methods_accepted: [:mastercard, :visa, :ec, :maestro ],
currencies: :all,
regions: :global,
subscriptions: true,
... }

This would allow automatic routing/deciding in future, as well as being able to know which gateways support things like subscription services? Could be extended in future to include preferences and/or cost information to allow choosing "cheapest, most preferred, available gateway" for a given purchase?

Example projects

There should be an example project for each payment gateway that is implemented. This example should demonstrate using each of the available operations of the gateway, as well as detailing any gateway specific config (such as api keys, secrets, etc...).

Where these examples are to live is still to be decided, however in the immediate future an examples folder at the root of this repo should suffice (however this is open to suggestions).

Bypass race condition

There looks to be a race condition in the paypal tests that occasionally causes the following error.

No HTTP request arrived at Bypass
     stacktrace:
       (bypass) lib/bypass.ex:19: anonymous fn/1 in Bypass.open/1
       (ex_unit) lib/ex_unit/on_exit_handler.ex:98: ExUnit.OnExitHandler.exec_callback/1
       (ex_unit) lib/ex_unit/on_exit_handler.ex:82: ExUnit.OnExitHandler.on_exit_runner_loop/0

This needs to be investigated and fixed.

Gateway process doesn't stop when :stop returned

Returning {:stop, :reason} from a gateway request should cause the gateway process to stop. This currently isn't being handled correctly and the process continues to live on with potentially invalid state.

Adjust the base gateway to handle the stop response and ensure the correct genserver responses are being returned.

Gateway Pooling

Gateway pooling will provide the ability have multiple instances of a single payment gateway running under a single supervisor that will be responsible for selecting a worker to carry out a single request.


Original requirements

  • Change the gateway_supervisor module to start gateway pool supervisors instead of the payment gateways directly
  • Default config option to specify how many workers each gateway pool should create
  • Gateway config option to specify the number of workers the pool should create, this will override the default pool workers config
  • When a gateway returns a non success response, retry the request on another worker within the pool
  • Default config option to specify the maximum number of attempts that should be made for a failed request within a given gateway pool

Add async functions to the cashier module

Currently all available functions on the cashier module are sync functions that wait for the payment gateway to return a result before continuing. Adding an async interface will allow for more advanced usage of cashier such as payments over websockets.

Current thoughts are to add two async interfaces per function.

Example for the purchase function

def async_purchase(amount, card, fun)
def async_purchase(amount, card, module, fun)

Each of the function callbacks would receive the same result as the sync version.

An additional thought is to add an args parameter that would be passed into the callback function, however the payment gateway result would need to be appended to this in some form.

PayPal external_customer_id field for stored cards

PayPal supports an external_customer_id field when storing cards and using them to make an authorisation or purchase.

Add support for this field to the store, authorize and purchase functions on the gateway via the opts parameter.

Documentation

Documentation is needed in the following areas

  • Code documentation
  • Introductory examples in readme (this can be improved)
  • Example usage of each gateway (linked to #10)

Gateway error handling

Error handling of the individual gateway responses needs to be handled and converted into a common shape that the cashier module or calling client can deal with.

Fix gateway tests

Currently the gateways are tested by calling the functions directly on the gateway module. This leaves some gaps in the tests, gateways should be tested through the cashier module using the [gateway: :gateway_name] option.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.