-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1d5a868
Showing
19 changed files
with
1,005 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/_build | ||
/deps | ||
erl_crash.dump | ||
*.ez | ||
config |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2014 Shane Logsdon | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of | ||
this software and associated documentation files (the "Software"), to deal in | ||
the Software without restriction, including without limitation the rights to | ||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | ||
the Software, and to permit persons to whom the Software is furnished to do so, | ||
subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | ||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | ||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | ||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Stripe for Elixir | ||
|
||
An Elixir 1.0.5 library for working with Stripe. With this library you can: | ||
|
||
- manage Customers | ||
- Create, list, cancel, update and delete Subscriptions | ||
- Create, list, update and delete Plans | ||
- Create, list, and update Invoices | ||
- And yes, run charges with or without an existing Customer | ||
|
||
Why another Stripe Library? Currently there are a number of them in the Elixir world that are, well just not "done" yet. I started to fork/help but soon it became clear to me that what I wanted was: | ||
|
||
- An existing/better test story | ||
- An API that didn't just mimic a REST interaction | ||
- A library that was up to date with Elixir > 1.0 and would, you know, actually *compile*. | ||
- Function calls that returned a standard `{:ok, result}` or `{:error, message}` response | ||
|
||
As I began digging things up with these other libraries it became rather apparent that I was not only tweaking the API, but also ripping out a lot of the existing code... and that usually means I should probably do my own thing. So I did. | ||
|
||
## Stripe API | ||
|
||
I've tested this library against Stripe API v1 and above. | ||
|
||
## Usage | ||
|
||
First, create a config folder and add a Stripe secret key: | ||
|
||
``` | ||
use Mix.Config | ||
config :stripe, secret_key: "YOUR SECRET KEY" | ||
``` | ||
|
||
Then add Stripe to your supervisor tree or, to play around, make sure you start it up: | ||
|
||
``` | ||
Stripe.start | ||
``` | ||
|
||
## The API | ||
|
||
I've tried to make the API somewhat comprehensive and intuitive. If you'd like to see things in detail be sure to have a look at the tests - they show (generally) the way the API goes together. | ||
|
||
In general, if Stripe requires some information for a given API call, you'll find that as part of the arrity of the given function. For instance if you want to delete a Customer, you'll find that you *must* pass the id along: | ||
|
||
``` | ||
{:ok, result} = Stripe.Customers.delete "some_id" | ||
``` | ||
|
||
For optional arguments, you can send in a Keyword list that will get translated to parameters. So if you want to update a Subscription, for instance, you must send in the `customer_id` and `subscription_id` with the list of changes: | ||
|
||
``` | ||
#Change customer to the Premium subscription | ||
{:ok, result} = Stripe.Customers.change_subscription "customer_id", "sub_id", plan: "premium" | ||
``` | ||
|
||
That's the rule of thumb with this library. If there are any errors with your call, they will bubble up to you in the `{:error, message}` match. | ||
|
||
### Customers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
defmodule Stripe do | ||
@moduledoc """ | ||
A HTTP client for Stripe. | ||
""" | ||
|
||
# Let's build on top of HTTPoison | ||
use Application | ||
use HTTPoison.Base | ||
|
||
def start(_type, _args) do | ||
Stripe.Supervisor.start_link | ||
end | ||
|
||
@doc """ | ||
Creates the URL for our endpoint. | ||
Args: | ||
* endpoint - part of the API we're hitting | ||
Returns string | ||
""" | ||
def process_url(endpoint) do | ||
"https://api.stripe.com/v1/" <> endpoint | ||
end | ||
|
||
@doc """ | ||
Set our request headers for every request. | ||
""" | ||
def req_headers do | ||
HashDict.new | ||
|> Dict.put("Authorization", "Bearer #{key}") | ||
|> Dict.put("User-Agent", "Stripe/v1 stripe-elixir/0.1.0") | ||
|> Dict.put("Content-Type", "application/x-www-form-urlencoded") | ||
end | ||
|
||
@doc """ | ||
Converts the binary keys in our response to atoms. | ||
Args: | ||
* body - string binary response | ||
Returns Record or ArgumentError | ||
""" | ||
def process_response_body(body) do | ||
Poison.decode! body | ||
end | ||
|
||
@doc """ | ||
Boilerplate code to make requests. | ||
Args: | ||
* endpoint - string requested API endpoint | ||
* body - request body | ||
Returns dict | ||
""" | ||
def make_request(method, endpoint, body \\ [], headers \\ [], options \\ []) do | ||
# rb = Enum.map(body, &url_encode_keyvalue(&1)) | ||
# |> Enum.join("&") | ||
rb = Stripe.URI.encode_query(body) | ||
rh = req_headers | ||
|> Dict.merge(headers) | ||
|> Dict.to_list | ||
|
||
|
||
{:ok, response} = case method do | ||
:get -> get( endpoint, rh, options) | ||
:put -> put( endpoint, rb, rh, options) | ||
:head -> head( endpoint, rh, options) | ||
:post -> post( endpoint, rb, rh, options) | ||
:patch -> patch( endpoint, rb, rh, options) | ||
:delete -> delete( endpoint, rh, options) | ||
:options -> options( endpoint, rh, options) | ||
end | ||
|
||
|
||
response.body | ||
end | ||
|
||
@doc """ | ||
Grabs STRIPE_SECRET_KEY from system ENV | ||
Returns binary | ||
""" | ||
def key do | ||
Application.get_env(:stripe, :secret_key) || | ||
System.get_env "STRIPE_SECRET_KEY" | ||
end | ||
|
||
defp url_encode_keyvalue({k, v}) do | ||
key = Atom.to_string(k) | ||
"#{key}=#{v}" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
defmodule Stripe.Charges do | ||
@moduledoc """ | ||
Handles charges to the Stripe API. | ||
""" | ||
|
||
@endpoint "charges" | ||
|
||
|
||
@doc """ | ||
Creates a charge for a customer or card. You must pass in the amount, and also a source for the charge | ||
that can be a token or customer. See the Stripe docs for proper source specs. | ||
## Examples | ||
params = [ | ||
source: [ | ||
object: "card", | ||
number: "4111111111111111", | ||
exp_month: 10, | ||
exp_year: 2020, | ||
country: "US", | ||
name: "Ducky Test", | ||
cvc: 123 | ||
], | ||
description: "1000 Widgets" | ||
] | ||
{:ok, result} = Stripe.Charges.create 1000,params | ||
""" | ||
def create(amount, params) do | ||
#default currency | ||
params = Keyword.put_new params, :currency, "USD" | ||
#drop in the amount | ||
params = Keyword.put_new params, :amount, amount | ||
|
||
Stripe.make_request(:post, @endpoint, params) | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
|
||
@doc """ | ||
Lists out charges from your account with a default limit of 10. You can override this by passing in a limit. | ||
## Examples | ||
{:ok, charges} = Stripe.Charges.list(100) | ||
""" | ||
def list(limit \\ 10) do | ||
Stripe.make_request(:get, "#{@endpoint}?limit=#{limit}") | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
|
||
@doc """ | ||
Updates a charge with changeable information (see the Stripe docs on what you can change) | ||
## Examples | ||
params = [description: "Changed charge"] | ||
Stripe.Charges.change("charge_id", params) | ||
""" | ||
def change(id, params) do | ||
Stripe.make_request(:post, "#{@endpoint}/#{id}",params) | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
@doc """ | ||
Captures a charge that is currently pending. Note: you can default a charge to be automatically captured by setting `capture: true` in the charge create params. | ||
## Example | ||
Stripe.Charges.capture("charge_id") | ||
""" | ||
def capture(id) do | ||
Stripe.make_request(:post, "#{@endpoint}/#{id}/capture") | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
|
||
@doc """ | ||
Retrieves a given charge. | ||
## Example | ||
Stripe.Charges.get("charge_id") | ||
""" | ||
def get(id) do | ||
Stripe.make_request(:get, "#{@endpoint}/#{id}") | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
@doc """ | ||
Refunds a charge completely. Use `refund_partial` if you just want to... you know... partially refund | ||
## Example | ||
Stripe.Charges.refund("charge_id") | ||
""" | ||
def refund(id) do | ||
Stripe.make_request(:post, "#{@endpoint}/#{id}/refunds") | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
|
||
@doc """ | ||
Refunds a charge partially; the amount is required. | ||
## Example | ||
Stripe.Charges.refund_partial("charge_id",500) | ||
""" | ||
def refund_partial(id, amount) do | ||
params = [amount: amount] | ||
Stripe.make_request(:post, "#{@endpoint}/#{id}/refunds", params) | ||
|> Stripe.Util.handle_stripe_response | ||
end | ||
end |
Oops, something went wrong.