beam-community / stripity-stripe Goto Github PK
View Code? Open in Web Editor NEWAn Elixir Library for Stripe
License: Other
An Elixir Library for Stripe
License: Other
Progress on #115.
Replacements for:
Since I don't have admin rights, can you register the repo here and give me the URL? https://coveralls.io/
Thanks.
Can not rune mix.deps.get
in my project.
This is my file Mix.config
defmodule Delivr.Mixfile do
use Mix.Project
def project do
[app: :delivr,
version: "0.0.1",
elixir: "~> 1.2",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix, :gettext] ++ Mix.compilers,
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
aliases: aliases(),
deps: deps()]
end
# Configuration for the OTP application.
#
# Type `mix help compile.app` for more information.
def application do
[mod: {Delivr, []},
applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,
:phoenix_ecto, :postgrex, :sendgrid, :calendar, :stripity_stripe]]
end
# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
defp elixirc_paths(_), do: ["lib", "web"]
# Specifies your project dependencies.
#
# Type `mix help deps` for examples and options.
defp deps do
[{:phoenix, "~> 1.2.0"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.0"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.6"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:exrm, "~> 1.0.6"},
{:timex, "~> 2.1.0"},
{:timex_ecto, "~> 1.1.0"},
{:number, "~> 0.4.2"},
{:httpoison, "~> 0.9.0"},
{:floki, "~> 0.9.0"},
{:sendgrid, "~> 1.0.2"},
{:hashids, "~> 2.0"},
{:json, "~> 0.3.0"},
{:calendar, "~> 0.14.2"},
{:stripity_stripe, "~> 1.4.0"}]
end
# Aliases are shortcuts or tasks specific to the current project.
# For example, to create, migrate and run the seeds file at once:
#
# $ mix ecto.setup
#
# See the documentation for `Mix` for more info on aliases.
defp aliases do
["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
"test": ["ecto.create --quiet", "ecto.migrate", "test"]]
end
end
This is my error
Failed to use "hackney" (version 1.6.1) because
httpoison (version 0.9.0) requires ~> 1.6.0
stripity_stripe (version 1.4.0) requires ~> 1.4.8
tzdata (version 0.5.8) requires ~> 1.0
Locked to 1.6.1 in your mix.lock
** (Mix) Hex dependency resolution failed, relax the version requirements of your dependencies or unlock them (by using mix deps.update or mix deps.unlock)
We should generally update the codebase wherever possible to replace dicts with maps where it makes sense:
Progress on #115.
Stripity Stripe is in need of an upgrade to provide a more flexible Elixir wrapper for the amazing Stripe API. There are two main goals of this rewrite. First, Stripity Stripe 2.0 should have a consistent and easy to understand high level API. Given that Elixir was born of Ruby, we will seek to closely match the documented Ruby Interface. Second, there should be full test coverage which does not rely on recorded responses of a specific version of the API. We want to be testing our functionality, not that of the Stripe API itself.
The initial desire for 2.0 came out of a conversation between Dan Matthews of Strumber and Josh Smith of Code Corps. Both teams were/are implementing on top of Stripe's Connect functionality and found the existing version's endpoint modules to be generally lacking best practice support for the Stripe-Account header. Additionally, we discovered unnecessary dependencies, unsupported endpoints, inconsistent variable names and a good deal of repetition.
Rob Conery was gracious enough to hand us the reins several days ago.
HTTPoison will be replaced with Hackney at the suggestion of @DavidAntaramian in Issue #100. Along this thought line, dependencies should be generally avoided unless absolutely necessary. Whenever possible, the lowest level dependency should be used.
Stripe provides a common endpoint structure for each primary endpoint. We will attempt to handle the majority of them through the use of common modules supplemented by heavy documentation. An example of this is as follows:
defmodule Stripe.Customers do
@moduledoc """
High Level API for working with Stripe Customers.
Through this API you can:
-Create a customer [ref](https://stripe.com/docs/api#create_customer)
-Retrieve a customer [ref](https://stripe.com/docs/api#retrieve_customer)
-Update a customer [ref](https://stripe.com/docs/api#update_customer)
-Delete a customer [ref](https://stripe.com/docs/api#delete_customer)
-List all customers [ref](https://stripe.com/docs/api#list_customers)
"""
@endpoint "/customers"
@doc """
Creates a Customer. Valid attributes can be found in the
[Stripe API Customer Docs](https://stripe.com/docs/api#create_customer).
At the time of this writing, all were optional.
[ref](https://stripe.com/docs/api#create_customer)
## Example
`` `
attributes = %{
email: "[email protected]",
description: "Customer for [email protected]",
source: "tok_18pPRnBMvBFPIRGmeLiOEwlm"
}
{:ok, customer} = Stripe.Customers.create(attributes)
`` `
"""
use Stripe.API.Create
@doc """
Retrieves a Customer by ID.
[ref](https://stripe.com/docs/api/ruby#retrieve_customer)
## Example
`` `
{:ok, customer} = Stripe.Customers.retrieve("cus_9McRYFr9fAGdKm")
# or
{:ok, customer} = Stripe.Customers.retrieve(%{id: "cus_9McRYFr9fAGdKm"})
`` `
"""
use Stripe.API.Retrieve
# ...
end
We will also only provide functions documented in the official Stripe API libraries. No additional functions are allowed directly on the High Level API modules.
Some endpoints have dependency ids in their URLs. These values will be extracted from the attributes map.
defmodule Stripe.Card do
@endpoint "/customers/{customer_id}/sources"
# ...
end
attributes = %{
id: "card_196RVbBMvBFPIRGmREqyLEUh",
customer_id: "cus_9McRYFr9fAGdKm"
}
{:ok, customer} = Stripe.Card.retrieve(attributes)
All public functions on High Level API modules have an arity of 4, the last 3 of which are optional.
def create(
attributes,
headers \\ %{},
hackney_options \\ %{},
secret_key \\ ""
) do
# ...
end
There is next to no backwards compatibility with Stripity Stripe Version 1. We're singularizing nearly every module name (Stripe.Customers
becomes Stripe.Customer
), leaving out (moving?) courtesy functions such as all
and changing the arity of almost every function that is kept.
We're also intentionally treating fewer dependancies and DRYness as higher priority concerns than code readability when we choose to use Erlang modules such as Hackney directly, abandoning the syntactic sugar and familiarity of their Elixir wrappers. This choice may deter wider contributions in those areas of the code.
Should we provide some or all of the non-standard functions from version 1 in a Stripe.Utils
module? The main one I personally find useful is the all
function which iterates over the list
endpoint.
I added `{:stripity_stripe, "~> 1.4.0"} to my mix.exs, but dependency resolution is failing:
mix deps.get
Running dependency resolution
Failed to use "hackney" because
stripity_stripe (version 1.4.0) requires ~> 1.4.8
mix.lock specifies 1.6.2
Not sure why this it says it requires ~> 1.4.8, since the stripity-stripe mix.lock file says:
"hackney": {:hex, :hackney, "1.6.1", ...}
Anyone wanting to tackle this? I'm on the canadian side and don't have a US account yet. The feature is still limited to the US accounts. I want to be ready to use that feature as soon as I can + it would be a nice addition feature-wise.
Thoughts?
The URI Encoding tests use multiple assertions per test, which makes it difficult to understand what's going on. There's also a test failure that needs to be fixed.
Using v1.3.0.
If I submit a Stripe.Charge
with an ampersand in the description field, I would expect this library to properly escape values to submit via the API.
This is a pretty serious issue leaving the library open to query-parameter injection / tampering.
Example:
{:ok, uncaptured_payment} = Stripe.Charges.create(amount, [
capture: false,
description: "Item1 & Item2",
receipt_email: appointment.client.email,
customer: appointment.client.stripe_customer_id,
source: card_id,
metadata: %{
business_id: business.id,
appointment_id: appointment.id,
client_name: appointment.client.first_name <> " " <> appointment.client.last_name
}
])
** (MatchError) no match of right hand side value: {:error, %{"error" => %{"message" => "Received unknown parameter: Item2", "param" => " Item2", "type" => "invalid_request_error"}}}
โฏ STRIPE_SECRET_KEY=mykey mix test --trace
Excluding tags: [disabled: true]
warning: this check/guard will always yield the same result
test/stripe/connect_test.exs:50
warning: this check/guard will always yield the same result
test/stripe/stripe_test.exs:40
Stripe.SubscriptionTest
It just hangs there. I waited over 4 minutes before aborting.
(Elixir 1.3.1, OS X 10.11.6)
Progress on #115.
I am looking for a way to list invoices of a given customer. (https://stripe.com/docs/api/ruby#list_invoices)
Looking at the documentation, my first guess was that Stripe.Invoices.list might be usable for this purpose, but as far as I can see this function does not take customer id as a (optional) parameter.
Stripe.Invoices.get specifies the following (as an example):
{:ok, cust} = Stripe.Invoices.get "customer_id", key
Which looks promising, but it seems to be a copy-paste error from some other part of the documentation as Stripe.Invoice.get actually takes an invoice-id and not a customer-id as parameter.
Looking in the source code for the Stripe.Invoices module (https://github.com/code-corps/stripity-stripe/blob/master/lib/stripe/invoices.ex) I can see that there is an commented-out function called "get_invoices". This seemed to be what I was looking for at first glance, but as mentioned this function has been commented out and just copy-pasting the function (to make my own implementation) won't work:
#def get_invoices(id, params, key) do
# params = Keyword.put_new params, :limit, 10 #params are never used
# params = Keyword.put_new params, :customer, id #params are never used
# Stripe.Util.list "invoices", key, starting_after, limit #params not used
#end
Params containing the customer id is never used (Stripe.Util.list does not support a params keyword list).
So how does one get a list of invoices for a specific customer?
Can you activate https://travis-ci.org/ for this project? Thanks ๐บ
Does this support stripe connect?
The Stripe.URI module code should be reviewed. It's a small issue, but right now it uses ++
with the left operand as the accumulator and the right operand as the added value. ++
is implemented such that the left operand is always copied, so our accumulator is always copied (which is what we don't want). See The Eight Myths of Erlang Performance
When acting on behalf of a Connected account, Stripe recommends using the Stripe-Account
header as seen here: https://stripe.com/docs/connect/payments-fees#charging-directly
I propose updating the class functions to expose headers in the following way:
This:
def create(params, key) do
Stripe.make_request_with_key(:post, @endpoint, key, params)
|> Stripe.Util.handle_stripe_response
end
would become this:
def create(params, key_or_headers) when is_bitstring(key_or_headers) do
Stripe.make_request_with_key(:post, @endpoint, key_or_headers, params)
|> Stripe.Util.handle_stripe_response
end
def create(params, key_or_headers) when is_map(key_or_headers) do
Stripe.make_request_with_key(
:post, @endpoint, Stripe.config_or_env_key, params, key_or_headers
)
|> Stripe.Util.handle_stripe_response
end
This still allows a key override but also supports passing any headers required without using the low level API on the Stripe
class. With your approval, I'll submit a PR in the next week or so.
HTTPoison doesn't offer any benefits over using Hackney directly, and it just adds another dependency to pin against; often times the use of HTTPoison will obfuscate the lower level support provided by Hackney. I would recommend swapping out HTTPoison for Hackney, which is a very simple process. I'm willing to do it if we're in agreement.
Hey,
I see there is a Connect module implemented for standalone accounts and I was wondering if you are planning on adding managed accounts to the connect module?
Thanks,
Alaister
Progress on #115.
To use your latest version, github needs to be referenced, like this:
{:stripity_stripe, "~> 1.0.0", github: "robconery/stripity-stripe"},
Last hex package is 0.5.0 (it may also make sense to bump httpoison dependency to 0.8.0 - I had to downgrade my dependency in mix.exs).
I'm trying to advise someone on how to use this library, but I don't understand why you're starting a supervision tree. When I actually open the supervisor module's definition, nothing is being supervised. And from what I can tell, no children are ever dynamically added to the supervision tree.
You're also informing consumers that they need to insert Stripe into their own supervision tree, but that's incorrect as you have already started a supervision tree for the application which exists external the to the consumer's application at run-time.
As far as I can tell, a consumer should only have to add :stripity_stripe
to their applications
list, solely to have the application controller load :httpoison
as an application. This also means there's no need to call HTTPoison.start/1
inside your code since this will be handled by ERTS/BEAM.
I'd appreciate pointers on why it's necessary to start a supervisor and what actually is being supervised. Thanks!
With Subscriptions.change() I am unable to change other options (something other than the plan id) of a subscription.
This causes Stripe(.com) to return an error message:
Stripe.Subscriptions.change(user.stripe_customer_id, sub_id, [quantity: 3])
...
%{"error" => %{"message" => "Invalid string: {\"quantity\"=>\"3\"}",
"param" => "plan", "type" => "invalid_request_error"}}
This works:
Stripe.make_request_with_key(
:post,
"customers/#{user.stripe_customer_id}/subscriptions/#{sub_id}",
Stripe.config_or_env_key,
[quantity: 3]
) |> Stripe.Util.handle_stripe_response
Hi There,
Please forgive me for posting a question here. Is there a way to assign multiple credit cards to an existing customer?
URL:
/v1/customers//sources
Parsed Request POST Body
{
card: "tok_17sFEyDzqcXKnkcafX4jkf2s"
}
Response body
{
id: card_17sFEyDzqcXKnkcaO95Kg2iY
object: "card"
address_city: null
address_country: null
address_line1: null
address_line1_check: null
address_line2: null
address_state: null
address_zip: null
address_zip_check: null
brand: "MasterCard"
country: "US"
customer: cus_88S0F9EAp18omi
cvc_check: "pass"
dynamic_last4: null
exp_month: 3
exp_year: 2017
fingerprint: "Pj4eCYNJpZuIHY9i"
funding: "credit"
last4: "4444"
metadata:
name: null
tokenization_method: null
}
Failed to use "hackney" because
stripity_stripe (version 1.4.0) requires ~> 1.4.8
Locked to 1.6.1 in your mix.lock
Is the solution to use the override: true option in my mix.exs?
In mix.exs
Shouldn't :earmark be dev only like :ex_doc?
There's some further configuration we can add into the query string for /oauth/authorize
. See here: https://stripe.com/docs/connect/reference#get-authorize-request
Not a source issue of course, but I found that the easiest thing to point it out:
Hex is pointing to https://github.com/robconery/stripe-elixir rather than to https://github.com/robconery/stripity-stripe
We should be able to mock
the requests and also if we specify, run all the tests directly into stripe. So that way we're going to be able to use Travis CI.
https://stripe.com/docs/api#idempotent_requests
I'd like to be able to take advantage of idempotency keys, but there doesn't appear to be a way to do that through stripity-stripe.
Have you considered implementing this?
Hello,
I'm brand new to elixir, so pretty sure I'm doing something wrong.
I added config :usestripe, secret_key: "sk_test_XXX"
to config/config.exs
usestripe_test.exs look like:
defmodule UsestripeTest do
use ExUnit.Case
doctest Usestripe
test "Customers are listed" do
{:ok, customers} = Stripe.Customers.list
assert length(customers) > 0
end
end
test_helper.exs looks like:
ExUnit.start()
Stripe.start
mix test
results in:
$ mix test
1) test Customers are listed (UsestripeTest)
test/usestripe_test.exs:5
** (MatchError) no match of right hand side value:
{:error, "You did not provide an API key, though you did set your Authorization header to \"Bearer\".
Using Bearer auth, your Authorization header should look something like 'Authorization: Bearer
YOUR_SECRET_KEY'. See https://stripe.com/docs/api#authentication for details, or we can help at
https://support.stripe.com/."}
stacktrace:
test/usestripe_test.exs:6
Finished in 0.7 seconds (0.05s on load, 0.7s on tests)
1 test, 1 failure
Randomized with seed 333290
What am I missing? On elixir v1.1.0
We should do a runthrough of this package to:
If we don't find any, then we can just close this issue.
Right now many of the tests either hit Stripe or have VCR cassettes. The more mocking or dependency injection or any other technique we can use to reduce the external reliance on hitting Stripe, the better.
Broad issue name, but the codebase is old and it shows. I'd like to clean things up, move to v2 and see if we can streamline some things.
https://github.com/robconery/stripity-stripe/blob/master/test/stripe/subscription_test.exs#L155
For the life of me I can't figure out what I have to do to get this working in my app. Explicitly calling Stripe.start works great, but in my app's supervisor, what am I supposed to add as the worker? Can you address this in the readme please?
Elixir is at 1.2.1 now, but this package depends on ~> 1.1.1
. Could it maybe be relaxed to ~> 1.1
, or >= 1.1.1
?
When I try to list charges with anything other than a integer limit it throws and error thats not very useful in tracking down whats happening.
Stripe.Charges.list(customer: "customer_number")
** (ArgumentError) cannot convert the given list to a string.
To be converted to a string, a list must contain only:
* strings
* integers representing Unicode codepoints
* or a list containing one of these three elements
Please check the given list or call inspect/1 to get the list representation, got:
[customer: "customer_number"]
(elixir) lib/list.ex:624: List.to_string/1
(stripity_stripe) lib/stripe/charges.ex:87: Stripe.Charges.list/2
any idea whats going on?
Progress on #115.
Now that CodeCorps and Strumber are taking over maintenance...
Primarily for @joshsmith: what are your support commitments for non-current versions of Elixir? The latest Elixir release is 1.3, and I would say that 1.2 is the farthest back things should be supported, as 1.2 released a lot of improvements for the ecosystem.
Right now there are basically two occurrences of the same method:
This means that we have to have:
I think this can be significantly improved & cleaned up:
make_request_with_key/1
.Example
Current implementation of Stripe.Customer.get
is done with two functions:
@doc """
Retrieves a given Customer with the specified ID. Returns 404 if not found.
## Example
{:ok, cust} = Stripe.Customers.get "customer_id"
"""
def get(id) do
get id, Stripe.config_or_env_key
end
@doc """
Retrieves a given Customer with the specified ID. Returns 404 if not found.
Using a given stripe key to apply against the account associated.
## Example
{:ok, cust} = Stripe.Customers.get "customer_id", key
"""
def get(id, key) do
Stripe.make_request_with_key(:get, "#{@endpoint}/#{id}", key)
|> Stripe.Util.handle_stripe_response
end
This can be reduced to:
@doc """
Retrieves a given Customer with the specified ID. Returns 404 if not found. Custom can be used as a second optional argument.
## Examples
{:ok, customer} = Stripe.Customers.get("customer_id")
{:ok, customer} = Stripe.Customers.get("customer_id", custom_key)
"""
def get(id, key \\ nil) do
Stripe.make_request_with_key(:get, "#{@endpoint}/#{id}", key)
|> Stripe.Util.handle_stripe_response
end
With the above, Stripe.make_request_with_key
function would check if the key passed is a nil
, and if so โ rely on configuration key.
See docs: https://stripe.com/docs/api#update_card
Code at: https://github.com/robconery/stripity-stripe/blob/master/lib/stripe/cards.ex#L81
I was planning to send you a patch, but #62, and the number of open pull requests suggests I should look elsewhere. ๐ฆ
It would be great to publish the docs for the most recent version released on hex :)
Progress on #115.
TL;DR. Let's stop making API calls in setup_all
blocks. Running tests against real API will become much slower, but contribution and running tests locally / on travis will remain as fast as they are right now.
Right now adding new API tests is somewhat overly complicated. Below I will show what it took to add a test for Stripe.Subscription.change/4 (see #70):
write the test,
write the code that fixes it,
run the test against actual API, say:
STRIPE_SECRET_KEY=qr0yuBikLRa5yJkMGuEO4DNblKhEJqkw mix test test/stripe/subscription_test.exs:134
the test will fail. Here's why:
remove responses for the test in question (I just recorded it, but it contains invalid response), as well as setup/teardown;
rm fixture/vcr_cassettes/subscription_test/{setup,teardown,general_change}.json
run the test again (same as step 3),
see the test succeed,
cleanup now-changed setup/teardown cassettes:
git checkout fixture/vcr_cassettes/subscription_test/{setup,teardown}.json
re-run the test to see it fail again:
1) test General change works (Stripe.SubscriptionTest)
test/stripe/subscription_test.exs:134
** (ExVCR.RequestNotMatchError) Request did not match with any one in the current cassette: fixture/vcr_cassettes/subscription_test/general_change.json.
Delete the current cassette with [mix vcr.delete] and re-record.
stacktrace:
lib/exvcr/handler.ex:142: ExVCR.Handler.raise_error_if_cassette_already_exists/1
lib/exvcr/handler.ex:126: ExVCR.Handler.get_response_from_server/2
(hackney) :hackney.request(:post, "https://api.stripe.com/v1/customers/cus_8y7pX6pDygSSFA/subscriptions/sub_8y7po6D0QD5y9V", [{"Authorization", "Bearer qr0yuBikLRa5yJkMGuEO4DNblKhEJqkw"}, {"Content-Type", "application/x-www-form-urlencoded"}, {"User-Agent", "Stripe/v1 stripity-stripe/1.4.0"}], "plan[quantity]=3", [])
(httpoison) lib/httpoison/base.ex:396: HTTPoison.Base.request/9
(stripity_stripe) lib/stripe.ex:95: Stripe.make_request_with_key/6
(stripity_stripe) lib/stripe/subscriptions.ex:113: Stripe.Subscriptions.change/4
test/stripe/subscription_test.exs:136: (test)
based on the failure, adjust the cassette recorded for the test so it now relies on old entities from setup.json, e.g. make these changes:
- "url": "https://api.stripe.com/v1/customers/cus_8y7pX6pDygSSFA/subscriptions/sub_8y7po6D0QD5y9V"
+ "url": "https://api.stripe.com/v1/customers/cus_8r2iLZ3me9G4hU/subscriptions/sub_8r2ikymYYXhIlq"
and
- "Authorization": "Bearer qr0yuBikLRa5yJkMGuEO4DNblKhEJqkw",
+ "Authorization": "Bearer non_empty_secret_key_string",
I think this can and should be changed. I'm proposing we get rid of making API calls in setup_all blocks throughout the suite. If we do this, and assume that each test lives on its own, contributing new tests and fixing existing tests, will become significantly easier.
Instead of following the procedure above, ideally I should be doing it this:
To sum up:
Progress on #115.
Hi just to let you know that your hex.pm
github is pointing to:
https://github.com/robconery/stripe-elixir
instead of
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.