lib/stripe/core_resources/payment_intent.ex

defmodule Stripe.PaymentIntent do
  @moduledoc """
  Work with [Stripe `payment_intent` objects](https://stripe.com/docs/api/payment_intents).
   You can:
  - [Create a payment_intent](https://stripe.com/docs/api/payment_intents/create)
  - [Retrieve a payment_intent](https://stripe.com/docs/api/payment_intents/retrieve)
  - [Update a payment_intent](https://stripe.com/docs/api/payment_intents/update)
  - [Confirm a payment_intent](https://stripe.com/docs/api/payment_intents/confirm)
  - [Capture a payment_intent](https://stripe.com/docs/api/payment_intents/capture)
  - [Cancel a payment_intent](https://stripe.com/docs/api/payment_intents/cancel)
  - [List all payment_intent](https://stripe.com/docs/api/payment_intents/list)
  - [Search payment_intents] (https://stripe.com/docs/api/payment_intents/search)
  """

  use Stripe.Entity
  import Stripe.Request
  require Stripe.Util

  @type last_payment_error :: %{
          type: String.t(),
          charge: String.t(),
          code: String.t(),
          decline_code: String.t(),
          doc_url: String.t(),
          message: String.t(),
          param: String.t(),
          payment_intent: Stripe.PaymentIntent.t() | map,
          source: Stripe.Card.t() | map
        }

  @type next_action :: %{
          redirect_to_url: redirect_to_url | nil,
          type: String.t(),
          use_stripe_sdk: map | nil
        }

  @type redirect_to_url :: %{
          return_url: String.t(),
          url: String.t()
        }

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

  @type t :: %__MODULE__{
          id: Stripe.id(),
          object: String.t(),
          amount: non_neg_integer,
          amount_capturable: non_neg_integer,
          amount_received: non_neg_integer,
          application: Stripe.id() | nil,
          application_fee_amount: non_neg_integer | nil,
          canceled_at: Stripe.timestamp() | nil,
          cancellation_reason: String.t() | nil,
          capture_method: String.t(),
          charges: Stripe.List.t(Stripe.Charge.t()),
          client_secret: String.t(),
          confirmation_method: String.t(),
          created: Stripe.timestamp(),
          currency: String.t(),
          customer: Stripe.id() | Stripe.Customer.t() | nil,
          description: String.t() | nil,
          invoice: Stripe.id() | Stripe.Invoice.t() | nil,
          last_payment_error: last_payment_error | nil,
          livemode: boolean,
          metadata: Stripe.Types.metadata(),
          next_action: next_action | nil,
          on_behalf_of: Stripe.id() | Stripe.Account.t() | nil,
          payment_method: Stripe.id() | Stripe.PaymentMethod.t() | nil,
          payment_method_options: map,
          payment_method_types: list(String.t()),
          receipt_email: String.t() | nil,
          review: Stripe.id() | Stripe.Review.t() | nil,
          shipping: Stripe.Types.shipping() | nil,
          source: Stripe.Card.t() | map,
          statement_descriptor: String.t() | nil,
          statement_descriptor_suffix: String.t() | nil,
          status: String.t(),
          setup_future_usage: String.t() | nil,
          transfer_data: transfer_data | nil,
          transfer_group: String.t() | nil
        }

  defstruct [
    :id,
    :object,
    :amount,
    :amount_capturable,
    :amount_received,
    :application,
    :application_fee_amount,
    :canceled_at,
    :cancellation_reason,
    :capture_method,
    :charges,
    :client_secret,
    :confirmation_method,
    :created,
    :currency,
    :customer,
    :description,
    :invoice,
    :last_payment_error,
    :livemode,
    :metadata,
    :next_action,
    :on_behalf_of,
    :payment_method,
    :payment_method_options,
    :payment_method_types,
    :receipt_email,
    :review,
    :shipping,
    :source,
    :statement_descriptor,
    :statement_descriptor_suffix,
    :setup_future_usage,
    :status,
    :transfer_data,
    :transfer_group
  ]

  @plural_endpoint "payment_intents"

  @doc """
  Create a payment intent.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/create).
  """
  @spec create(params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 :amount => pos_integer,
                 :currency => String.t(),
                 optional(:application_fee_amount) => non_neg_integer,
                 optional(:capture_method) => String.t(),
                 optional(:confirm) => boolean,
                 optional(:customer) => Stripe.id() | Stripe.Customer.t(),
                 optional(:description) => String.t(),
                 optional(:metadata) => map,
                 optional(:off_session) => boolean,
                 optional(:on_behalf_of) => Stripe.id() | Stripe.Account.t(),
                 optional(:payment_method) => String.t(),
                 optional(:payment_method_options) => map,
                 optional(:payment_method_types) => [Stripe.id()],
                 optional(:receipt_email) => String.t(),
                 optional(:return_url) => String.t(),
                 optional(:save_payment_method) => boolean,
                 optional(:setup_future_usage) => String.t(),
                 optional(:shipping) => Stripe.Types.shipping(),
                 optional(:source) => Stripe.id() | Stripe.Card.t(),
                 optional(:statement_descriptor) => String.t(),
                 optional(:statement_descriptor_suffix) => String.t(),
                 optional(:transfer_data) => transfer_data,
                 optional(:transfer_group) => String.t()
               }
               | %{}
  def create(params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint)
    |> put_params(params)
    |> put_method(:post)
    |> cast_to_id([:on_behalf_of, :customer, :source])
    |> make_request()
  end

  @doc """
  Retrieves the details of a PaymentIntent that has previously been created.
  Client-side retrieval using a publishable key is allowed when the client_secret is provided in the query string.
   When retrieved with a publishable key, only a subset of properties will be returned. Please refer to the payment intent object reference for more details.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/retrieve).
  """
  @spec retrieve(Stripe.id() | t, params, Stripe.options()) ::
          {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 optional(:client_secret) => String.t()
               }
               | %{}
  def retrieve(id, params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}")
    |> put_params(params)
    |> put_method(:get)
    |> make_request()
  end

  @doc """
  Updates a PaymentIntent object.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/update).
  """
  @spec update(Stripe.id() | t, params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 optional(:amount) => non_neg_integer,
                 optional(:application_fee_amount) => non_neg_integer,
                 optional(:currency) => String.t(),
                 optional(:customer) => Stripe.id() | Stripe.Customer.t(),
                 optional(:description) => String.t(),
                 optional(:metadata) => map,
                 optional(:payment_method) => String.t(),
                 optional(:payment_method_types) => [Stripe.id()],
                 optional(:receipt_email) => String.t(),
                 optional(:save_payment_method) => boolean,
                 optional(:setup_future_usage) => String.t(),
                 optional(:shipping) => Stripe.Types.shipping(),
                 optional(:source) => Stripe.id() | Stripe.Card.t(),
                 optional(:statement_descriptor_suffix) => String.t(),
                 optional(:transfer_group) => String.t()
               }
               | %{}
  def update(id, params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}")
    |> put_method(:post)
    |> put_params(params)
    |> make_request()
  end

  @doc """
  Confirm that your customer intends to pay with current or provided source. Upon confirmation,
  the PaymentIntent will attempt to initiate a payment.
  If the selected source requires additional authentication steps, the PaymentIntent will transition to
  the requires_action status and suggest additional actions via next_source_action.
  If payment fails, the PaymentIntent will transition to the requires_payment_method status.
  If payment succeeds, the PaymentIntent will transition to the succeeded status (or requires_capture,
  if capture_method is set to manual). Read the expanded documentation to learn more about server-side confirmation.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/confirm).
  """
  @spec confirm(Stripe.id() | t, params, Stripe.options()) ::
          {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 optional(:client_secret) => String.t(),
                 optional(:receipt_email) => String.t(),
                 optional(:return_url) => String.t(),
                 optional(:save_payment_method) => boolean,
                 optional(:shipping) => Stripe.Types.shipping(),
                 optional(:source) => Stripe.id() | Stripe.Card.t(),
                 optional(:payment_method) => Stripe.id() | Stripe.PaymentMethod.t()
               }
               | %{}
  def confirm(id, params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}" <> "/confirm")
    |> put_method(:post)
    |> put_params(params)
    |> cast_to_id([:source, :payment_method])
    |> make_request()
  end

  @doc """
  Capture the funds of an existing uncaptured PaymentIntent where required_action="requires_capture".
  Uncaptured PaymentIntents will be canceled exactly seven days after they are created.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/capture).
  """
  @spec capture(Stripe.id() | t, params, Stripe.options()) ::
          {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 optional(:amount_to_capture) => non_neg_integer,
                 optional(:application_fee_amount) => non_neg_integer
               }
               | %{}
  def capture(id, params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}/capture")
    |> put_params(params)
    |> put_method(:post)
    |> make_request()
  end

  @doc """
  A PaymentIntent object can be canceled when it is in one of these statuses: requires_payment_method,
  requires_capture, requires_confirmation, requires_action.
  Once canceled, no additional charges will be made by the PaymentIntent and any operations on the PaymentIntent will fail with an error.
  For PaymentIntents with status='requires_capture', the remaining amount_capturable will automatically be refunded.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/cancel).
  """
  @spec cancel(Stripe.id() | t, params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()}
        when params:
               %{
                 optional(:cancellation_reason) => String.t()
               }
               | %{}
  def cancel(id, params, opts \\ []) do
    new_request(opts)
    |> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}" <> "/cancel")
    |> put_method(:post)
    |> put_params(params)
    |> make_request()
  end

  @doc """
  Returns a list of PaymentIntents.
   See the [Stripe docs](https://stripe.com/docs/api/payment_intents/list).
  """
  @spec list(params, Stripe.options()) :: {:ok, Stripe.List.t(t)} | {:error, Stripe.Error.t()}
        when params: %{
               optional(:customer) => Stripe.id() | Stripe.Customer.t(),
               optional(:created) => Stripe.date_query(),
               optional(:ending_before) => t | Stripe.id(),
               optional(:limit) => 1..100,
               optional(:starting_after) => t | Stripe.id()
             }
  def list(params \\ %{}, opts \\ []) do
    new_request(opts)
    |> prefix_expansions()
    |> put_endpoint(@plural_endpoint)
    |> put_method(:get)
    |> put_params(params)
    |> cast_to_id([:ending_before, :starting_after, :customer])
    |> make_request()
  end

  @doc """
  Search PaymentIntents

  See the [Stripe docs](https://stripe.com/docs/api/payment_intents/search).
  """
  @spec search(params, Stripe.options()) ::
          {:ok, Stripe.SearchResult.t(t)} | {:error, Stripe.Error.t()}
        when params: %{
               :query => Stripe.search_query(),
               optional(:limit) => 1..100,
               optional(:page) => String.t()
             }
  def search(params, opts \\ []) do
    new_request(opts)
    |> prefix_expansions()
    |> put_endpoint(@plural_endpoint <> "/search")
    |> put_method(:get)
    |> put_params(params)
    |> make_request()
  end
end