defmodule Plaid.Sandbox do @moduledoc """ [Plaid Sandbox API](https://plaid.com/docs/api/sandbox/) calls and schema. > Only used for sandbox testing purposes. None of these calls will work in `development` or `production`. 🏗 I haven'tyet tested the `bank_transfer` endpoints against the actual plaid API because I can't get the `bank_transfers` product from plaid yet. If you test it, let me know and I can remove the in-progress status! """ alias Plaid.Castable defmodule TransactionsOptions do @moduledoc """ [Plaid API /sandbox/public_token/create transactions options schema.](https://plaid.com/docs/api/sandbox/#sandbox-public_token-create-request-transactions) """ @type t :: %__MODULE__{ start_date: String.t(), end_date: String.t() } @derive Jason.Encoder defstruct [:start_date, :end_date] end defmodule CreatePublicTokenResponse do @moduledoc """ [Plaid API /sandbox/public_token/create response schema.](https://plaid.com/docs/api/sandbox/#sandboxpublic_tokencreate) """ @behaviour Castable @type t :: %__MODULE__{ public_token: String.t(), request_id: String.t() } defstruct [:public_token, :request_id] @impl true def cast(generic_map) do %__MODULE__{ public_token: generic_map["public_token"], request_id: generic_map["request_id"] } end end @doc """ Create a valid `public_token` with arbitrary details. Does a `POST /sandbox/public_token/create` call to create a new sandbox public token. Params: * `institution_id` - The ID of the institution the Item will be associated with. * `initial_products` - The products to initially pull for the Item. Options: * `:webhook` - Specify a webhook to associate with the new Item. * `:override_username` - Test username to use for the creation of the Sandbox Item. * `:override_password` - Test password to use for the creation of the Sandbox Item. * `:transactions` - Options for transactions on the new Item. ## Examples Sandbox.create_public_token("ins_1", ["auth"], client_id: "123", secret: "abc") {:ok, %Sandbox.CreatePublicTokenResponse{}} """ @spec create_public_token(String.t(), [String.t()], options, Plaid.config()) :: {:ok, CreatePublicTokenResponse.t()} | {:error, Plaid.Error.t()} when options: %{ optional(:webhook) => String.t(), optional(:override_username) => String.t(), optional(:override_password) => String.t(), optional(:transactions) => TransactionsOptions.t() } def create_public_token(institution_id, initial_products, options \\ %{}, config) do options_payload = Map.take(options, [:webhook, :override_username, :override_password, :transactions]) payload = %{} |> Map.put(:institution_id, institution_id) |> Map.put(:initial_products, initial_products) |> Map.put(:options, options_payload) Plaid.Client.call( "/sandbox/public_token/create", payload, CreatePublicTokenResponse, config ) end defmodule ResetItemLoginResponse do @moduledoc """ [Plaid API /sandbox/item/reset_login response schema.](https://plaid.com/docs/api/sandbox/#sandboxitemreset_login) """ @behaviour Castable @type t :: %__MODULE__{ reset_login: boolean(), request_id: String.t() } defstruct [:reset_login, :request_id] @impl true def cast(generic_map) do %__MODULE__{ reset_login: generic_map["reset_login"], request_id: generic_map["request_id"] } end end @doc """ Force an item into a "login required" state. Does a `POST /sandbox/item/reset_login` call which forces an item into the `ITEM_LOGIN_REQUIRED` state to simulate and item whose login is no longer valid. Params: * `access_token` - The access token associated with the Item to reset the login for. ## Examples Sandbox.reset_item_login("access-prod-123xxx", client_id: "123", secret: "abc") {:ok, %Sandbox.ResetItemLoginResponse{}} """ @spec reset_item_login(String.t(), Plaid.config()) :: {:ok, ResetItemLoginResponse.t()} | {:error, Plaid.Error.t()} def reset_item_login(access_token, config) do Plaid.Client.call( "/sandbox/item/reset_login", %{access_token: access_token}, ResetItemLoginResponse, config ) end @doc """ Change the verification status of an item. Does a `POST /sandbox/item/set_verification_status` call to change the status of an item in order to simulate the Automated Micro-deposit flow. Params: * `access_token` - The access token associated with the Item data is being requested for. * `account_id` - The ID of the account whose verification status is to be modified. * `verification_status` - The verification status to set the account to. ## Examples Sandbox.set_item_verification_status("access-prod-123xxx", "39flxk4ek2xs", "verification_expired", client_id: "123", secret: "abc") {:ok, %Plaid.SimpleResponse{request_id: "9bkemelske"}} """ @spec set_item_verification_status(String.t(), String.t(), String.t(), Plaid.config()) :: {:ok, Plaid.SimpleResponse.t()} | {:error, Plaid.Error.t()} def set_item_verification_status(access_token, account_id, verification_status, config) do payload = %{ access_token: access_token, account_id: account_id, verification_status: verification_status } Plaid.Client.call( "/sandbox/item/set_verification_status", payload, Plaid.SimpleResponse, config ) end defmodule FireItemWebhookResponse do @moduledoc """ [Plaid API /sandbox/item/fire_webhook response schema.](https://plaid.com/docs/api/sandbox/#sandboxitemfire_webhook) """ @behaviour Castable @type t :: %__MODULE__{ webhook_fired: boolean(), request_id: String.t() } defstruct [:webhook_fired, :request_id] @impl true def cast(generic_map) do %__MODULE__{ webhook_fired: generic_map["webhook_fired"], request_id: generic_map["request_id"] } end end @doc """ Fire a fake webhook to an Item's webhook endpoint. Does a `POST /sandbox/item/fire_webhook` call which forces an item into the ITEM_LOGIN_REQUIRED state to simulate and item whose login is no longer valid. Params: * `access_token` - The access token associated with the Item to fire the webhook for. * `webhook_code` - The webhook code to send. > `webhook_code` only supports `DEFAULT_UPDATE` for now. ## Examples Sandbox.fire_item_webhook("access-prod-123xxx", "DEFAULT_UPDATE", client_id: "123", secret: "abc") {:ok, %Sandbox.FireItemWebhookResponse{}} """ @spec fire_item_webhook(String.t(), String.t(), Plaid.config()) :: {:ok, FireItemWebhookResponse.t()} | {:error, Plaid.Error.t()} def fire_item_webhook(access_token, webhook_code, config) do Plaid.Client.call( "/sandbox/item/fire_webhook", %{access_token: access_token, webhook_code: webhook_code}, FireItemWebhookResponse, config ) end @doc """ Simulate a bank transfer event in the Plaid Sandbox. Does a `POST /sandbox/bank_transfer/simulate` call to simulate a bank transfer in the plaid sandbox for testing purposes. Params: * `bank_transfer_id` - Plaid’s unique identifier for a bank transfer. * `event_type` - The asynchronous event to be simulated. May be: posted, failed, or reversed. Options: * `:failure_reason` - The failure reason if the type of this transfer is "failed" or "reversed". ## Examples Sandbox.simulate_bank_transfer("bt_123xxx", "posted", client_id: "123", secret: "abc") {:ok, %Plaid.SimpleResponse{}} """ @spec simulate_bank_transfer(String.t(), String.t(), options, Plaid.config()) :: {:ok, Plaid.SimpleResponse.t()} | {:error, Plaid.Error.t()} when options: %{ optional(:failure_reason) => %{ optional(:ach_return_code) => String.t(), optional(:description) => String.t() } } def simulate_bank_transfer(bank_transfer_id, event_type, options \\ %{}, config) do options_payload = Map.take(options, [:failure_reason]) payload = %{} |> Map.put(:bank_transfer_id, bank_transfer_id) |> Map.put(:event_type, event_type) |> Map.merge(options_payload) Plaid.Client.call( "/sandbox/bank_transfer/simulate", payload, Plaid.SimpleResponse, config ) end @doc """ Manually fire a Bank Transfer webhook. Does a `POST /sandbox/bank_transfer/fire_webhook` call to manually trigger a bank transfer webhook. Params: * `webhook` - The URL to which the webhook should be sent. ## Examples Sandbox.fire_bank_transfer_webhook("https://example.com/webhook", client_id: "123", secret: "abc") {:ok, %Plaid.SimpleResponse{}} """ @spec fire_bank_transfer_webhook(String.t(), Plaid.config()) :: {:ok, Plaid.SimpleResponse.t()} | {:error, Plaid.Error.t()} def fire_bank_transfer_webhook(webhook, config) do Plaid.Client.call( "/sandbox/bank_transfer/fire_webhook", %{webhook: webhook}, Plaid.SimpleResponse, config ) end defmodule CreateProcessorTokenResponse do @moduledoc """ [Plaid API /sandbox/processor_token/create response schema.](https://plaid.com/docs/api/sandbox/#sandboxprocessor_tokencreate) """ @behaviour Castable @type t :: %__MODULE__{ processor_token: String.t(), request_id: String.t() } defstruct [:processor_token, :request_id] @impl true def cast(generic_map) do %__MODULE__{ processor_token: generic_map["processor_token"], request_id: generic_map["request_id"] } end end @doc """ Create a valid `processor_token` for an arbitrary institution ID and test credentials. Does a `POST /sandbox/processor_token/create` call to create a valid `processor_token` to use with all the processor endpoints in the sandbox. Params: * `institution_id` - The ID of the institution the Item will be associated with. Options: * `:override_username` - Test username to use for the creation of the Sandbox Item. * `:override_password` - Test password to use for the creation of the Sandbox Item. ## Examples Sandbox.create_processor_token("ins_1", client_id: "123", secret: "abc") {:ok, %Sandbox.CreateProcessorTokenResponse{}} """ @spec create_processor_token(String.t(), options, Plaid.config()) :: {:ok, CreateProcessorTokenResponse.t()} | {:error, Plaid.Error.t()} when options: %{ optional(:override_username) => String.t(), optional(:override_password) => String.t() } def create_processor_token(institution_id, options \\ %{}, config) do options_payload = Map.take(options, [:override_username, :override_password]) payload = %{institution_id: institution_id, options: options_payload} Plaid.Client.call( "/sandbox/processor_token/create", payload, CreateProcessorTokenResponse, config ) end end