Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new Checkout flow #466

Merged
merged 7 commits into from
Mar 5, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ matrix:
sudo: false
env:
global:
- STRIPE_MOCK_VERSION=0.40.0
- STRIPE_MOCK_VERSION=0.47.0
- MIX_ENV=test STRIPE_SECRET_KEY=non_empty_secret_key_string
cache:
directories:
Expand Down
2 changes: 1 addition & 1 deletion lib/stripe/api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ defmodule Stripe.API do
@typep http_failure :: {:error, term}

@pool_name __MODULE__
@api_version "2018-11-08"
@api_version "2018-11-08; checkout_sessions_beta=v1"

@doc """
In config.exs your implicit or expicit configuration is:
Expand Down
104 changes: 104 additions & 0 deletions lib/stripe/checkout/session.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
defmodule Stripe.Session do
@moduledoc """
Work with Stripe Checkout Session objects.

You can:

- Create a new session

Stripe API reference: https://stripe.com/docs/api/checkout/sessions
"""

use Stripe.Entity
import Stripe.Request

@type line_item :: %{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a Stripe.LineItem object. Has everything except images. Don't see it here though 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that might be a new feature for the new Stripe Checkout: https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-line_items

My personal thinking is that the Stripe.LineItem type has a lot of fields on it that won't be accepted by this endpoint, so it would be better to have a second local line_item type that is specific to this use case. If you prefer though, I'm happy to add an images field to Stripe.LineItem and use that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

na we can leave it as is 👍

:amount => integer(),
:currency => String.t(),
:name => String.t(),
:quantity => integer(),
optional(:description) => String.t(),
optional(:images) => list(String.t())
}

@type capture_method :: :automatic | :manual

@type address :: %{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in stripe/types.ex, we have a shipping type

:line1 => String.t(),
optional(:city) => String.t(),
optional(:country) => String.t(),
optional(:line2) => String.t(),
optional(:postal_code) => String.t(),
optional(:state) => String.t()
}

@type shipping_info :: %{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in stripe/types.ex, we have a shipping type

:address => address,
optional(:carrier) => String.t(),
optional(:phone) => String.t(),
optional(:tracking_number) => String.t()
}

@type transfer_data :: %{
:destination => String.t()
}

@type payment_intent_data :: %{
optional(:application_fee_amount) => integer(),
optional(:capture_method) => capture_method,
optional(:description) => String.t(),
optional(:metadata) => map(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r/String.t()/Stripe.Types.metadata()

optional(:on_behalf_of) => String.t(),
optional(:receipt_email) => String.t(),
optional(:shipping) => shipping_info,
optional(:statement_descriptor) => String.t(),
optional(:transfer_data) => transfer_data
}

@type item :: %{
:plan => String.t(),
optional(:quantity) => integer()
}

@type subscription_data :: %{
:items => list(item),
:metadata => map(),
:trial_end => integer(),
:trial_period_days => integer()
}

@type create_params :: %{
:cancel_url => String.t(),
:payment_method_types => list(String.t()),
:success_url => String.t(),
optional(:client_reference_id) => String.t(),
optional(:customer_email) => String.t(),
optional(:line_items) => list(line_item),
optional(:locale) => String.t(),
optional(:payment_intent_data) => payment_intent_data,
optional(:subscription_data) => subscription_data
}

@type t :: %{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need %__MODULE__{ here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch!

:id => Stripe.id(),
:object => String.t(),
:livemode => boolean()
}

defstruct [
:id,
:object,
:livemode
]

@plural_endpoint "checkout/sessions"

@spec create(create_params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()}
def create(params, opts \\ []) do
new_request(opts)
|> put_endpoint(@plural_endpoint)
|> put_params(params)
|> put_method(:post)
|> make_request()
end
end
1 change: 1 addition & 0 deletions lib/stripe/converter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule Stripe.Converter do
bank_account
card
charge
checkout.session
country_spec
coupon
customer
Expand Down
5 changes: 3 additions & 2 deletions lib/stripe/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ defmodule Stripe.Util do
def atomize_key(k), do: k

@spec object_name_to_module(String.t()) :: module
def object_name_to_module("file"), do: object_name_to_module("file_upload")
def object_name_to_module("checkout.session"), do: Stripe.Session
def object_name_to_module("file"), do: Stripe.FileUpload

def object_name_to_module(object_name) do
module_name =
object_name
|> String.split("_")
|> Enum.map_join("", &String.capitalize/1)

Module.concat("Stripe", module_name)
Module.concat(Stripe, module_name)
end

@spec module_to_string(module) :: String.t()
Expand Down
5 changes: 5 additions & 0 deletions test/fixtures/session.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"id": "CdWP8EBmSp1tJNIw4ZLF6w3XKd8MNKkEvlnSK7QmwFlDZ8rrjqBn9VI9vKiVdhfE",
"livemode": false,
"object": "checkout.session"
}
14 changes: 14 additions & 0 deletions test/stripe/checkout/session_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
defmodule Stripe.SessionTest do
use Stripe.StripeCase, async: true

test "is creatable" do
params = %{
cancel_url: "https://stripe.com",
payment_method_types: ["card"],
success_url: "https://stripe.com"
}

assert {:ok, %Stripe.Session{}} = Stripe.Session.create(params)
assert_stripe_requested(:post, "/v1/checkout/sessions")
end
end
13 changes: 13 additions & 0 deletions test/stripe/converter_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,17 @@ defmodule Stripe.ConverterTest do

assert result == expected_result
end

test "converts a checkout.session response properly" do
expected_result = %Stripe.Session{
id: "CdWP8EBmSp1tJNIw4ZLF6w3XKd8MNKkEvlnSK7QmwFlDZ8rrjqBn9VI9vKiVdhfE",
livemode: false,
object: "checkout.session"
}

fixture = Helper.load_fixture("session.json")
result = Converter.convert_result(fixture)

assert result == expected_result
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

end