diff --git a/.credo.exs b/.credo.exs
new file mode 100644
index 0000000..3607e1a
--- /dev/null
+++ b/.credo.exs
@@ -0,0 +1,189 @@
+# This file contains the configuration for Credo and you are probably reading
+# this after creating it with `mix credo.gen.config`.
+#
+# If you find anything wrong or unclear in this file, please report an
+# issue on GitHub: https://github.com/rrrene/credo/issues
+#
+%{
+  #
+  # You can have as many configs as you like in the `configs:` field.
+  configs: [
+    %{
+      #
+      # Run any config using `mix credo -C <name>`. If no config name is given
+      # "default" is used.
+      #
+      name: "default",
+      #
+      # These are the files included in the analysis:
+      files: %{
+        #
+        # You can give explicit globs or simply directories.
+        # In the latter case `**/*.{ex,exs}` will be used.
+        #
+        included: [
+          "lib/",
+          "src/",
+          "test/",
+          "web/",
+          "apps/*/lib/",
+          "apps/*/src/",
+          "apps/*/test/",
+          "apps/*/web/"
+        ],
+        excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
+      },
+      #
+      # Load and configure plugins here:
+      #
+      plugins: [],
+      #
+      # If you create your own checks, you must specify the source files for
+      # them here, so they can be loaded by Credo before running the analysis.
+      #
+      requires: [],
+      #
+      # If you want to enforce a style guide and need a more traditional linting
+      # experience, you can change `strict` to `true` below:
+      #
+      strict: false,
+      #
+      # To modify the timeout for parsing files, change this value:
+      #
+      parse_timeout: 5000,
+      #
+      # If you want to use uncolored output by default, you can change `color`
+      # to `false` below:
+      #
+      color: true,
+      #
+      # You can customize the parameters of any check by adding a second element
+      # to the tuple.
+      #
+      # To disable a check put `false` as second element:
+      #
+      #     {Credo.Check.Design.DuplicatedCode, false}
+      #
+      checks: [
+        #
+        ## Consistency Checks
+        #
+        {Credo.Check.Consistency.ExceptionNames, []},
+        {Credo.Check.Consistency.LineEndings, []},
+        {Credo.Check.Consistency.ParameterPatternMatching, []},
+        {Credo.Check.Consistency.SpaceAroundOperators, []},
+        {Credo.Check.Consistency.SpaceInParentheses, []},
+        {Credo.Check.Consistency.TabsOrSpaces, []},
+
+        #
+        ## Design Checks
+        #
+        # You can customize the priority of any check
+        # Priority values are: `low, normal, high, higher`
+        #
+        {Credo.Check.Design.AliasUsage,
+         [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]},
+        # You can also customize the exit_status of each check.
+        # If you don't want TODO comments to cause `mix credo` to fail, just
+        # set this value to 0 (zero).
+        #
+        {Credo.Check.Design.TagTODO, [exit_status: 2]},
+        {Credo.Check.Design.TagFIXME, []},
+
+        #
+        ## Readability Checks
+        #
+        {Credo.Check.Readability.AliasOrder, []},
+        {Credo.Check.Readability.FunctionNames, []},
+        {Credo.Check.Readability.LargeNumbers, []},
+        {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
+        {Credo.Check.Readability.ModuleAttributeNames, []},
+        {Credo.Check.Readability.ModuleDoc, false},
+        {Credo.Check.Readability.ModuleNames, []},
+        {Credo.Check.Readability.ParenthesesInCondition, []},
+        {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
+        {Credo.Check.Readability.PredicateFunctionNames, []},
+        {Credo.Check.Readability.PreferImplicitTry, []},
+        {Credo.Check.Readability.RedundantBlankLines, []},
+        {Credo.Check.Readability.Semicolons, []},
+        {Credo.Check.Readability.SpaceAfterCommas, []},
+        {Credo.Check.Readability.StringSigils, []},
+        {Credo.Check.Readability.TrailingBlankLine, []},
+        {Credo.Check.Readability.TrailingWhiteSpace, []},
+        {Credo.Check.Readability.UnnecessaryAliasExpansion, []},
+        {Credo.Check.Readability.VariableNames, []},
+
+        #
+        ## Refactoring Opportunities
+        #
+        {Credo.Check.Refactor.CondStatements, []},
+        {Credo.Check.Refactor.CyclomaticComplexity, []},
+        {Credo.Check.Refactor.FunctionArity, []},
+        {Credo.Check.Refactor.LongQuoteBlocks, []},
+        # {Credo.Check.Refactor.MapInto, []},
+        {Credo.Check.Refactor.MatchInCondition, []},
+        {Credo.Check.Refactor.NegatedConditionsInUnless, []},
+        {Credo.Check.Refactor.NegatedConditionsWithElse, []},
+        {Credo.Check.Refactor.Nesting, []},
+        {Credo.Check.Refactor.UnlessWithElse, []},
+        {Credo.Check.Refactor.WithClauses, []},
+
+        #
+        ## Warnings
+        #
+        {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []},
+        {Credo.Check.Warning.BoolOperationOnSameValues, []},
+        {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
+        {Credo.Check.Warning.IExPry, []},
+        {Credo.Check.Warning.IoInspect, []},
+        # {Credo.Check.Warning.LazyLogging, []},
+        {Credo.Check.Warning.MixEnv, false},
+        {Credo.Check.Warning.OperationOnSameValues, []},
+        {Credo.Check.Warning.OperationWithConstantResult, []},
+        {Credo.Check.Warning.RaiseInsideRescue, []},
+        {Credo.Check.Warning.UnusedEnumOperation, []},
+        {Credo.Check.Warning.UnusedFileOperation, []},
+        {Credo.Check.Warning.UnusedKeywordOperation, []},
+        {Credo.Check.Warning.UnusedListOperation, []},
+        {Credo.Check.Warning.UnusedPathOperation, []},
+        {Credo.Check.Warning.UnusedRegexOperation, []},
+        {Credo.Check.Warning.UnusedStringOperation, []},
+        {Credo.Check.Warning.UnusedTupleOperation, []},
+        {Credo.Check.Warning.UnsafeExec, []},
+
+        #
+        # Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`)
+
+        #
+        # Controversial and experimental checks (opt-in, just replace `false` with `[]`)
+        #
+        {Credo.Check.Consistency.MultiAliasImportRequireUse, false},
+        {Credo.Check.Consistency.UnusedVariableNames, false},
+        {Credo.Check.Design.DuplicatedCode, false},
+        {Credo.Check.Readability.AliasAs, false},
+        {Credo.Check.Readability.BlockPipe, false},
+        {Credo.Check.Readability.ImplTrue, false},
+        {Credo.Check.Readability.MultiAlias, false},
+        {Credo.Check.Readability.SeparateAliasRequire, false},
+        {Credo.Check.Readability.SinglePipe, false},
+        {Credo.Check.Readability.Specs, false},
+        {Credo.Check.Readability.StrictModuleLayout, false},
+        {Credo.Check.Readability.WithCustomTaggedTuple, false},
+        {Credo.Check.Refactor.ABCSize, false},
+        {Credo.Check.Refactor.AppendSingleItem, false},
+        {Credo.Check.Refactor.DoubleBooleanNegation, false},
+        {Credo.Check.Refactor.ModuleDependencies, false},
+        {Credo.Check.Refactor.NegatedIsNil, false},
+        {Credo.Check.Refactor.PipeChainStart, false},
+        {Credo.Check.Refactor.VariableRebinding, false},
+        {Credo.Check.Warning.LeakyEnvironment, false},
+        {Credo.Check.Warning.MapGetUnsafePass, false},
+        {Credo.Check.Warning.UnsafeToAtom, false}
+
+        #
+        # Custom checks can be created using `mix credo.gen.check`.
+        #
+      ]
+    }
+  ]
+}
diff --git a/.formatter.exs b/.formatter.exs
new file mode 100644
index 0000000..8a6391c
--- /dev/null
+++ b/.formatter.exs
@@ -0,0 +1,5 @@
+[
+  import_deps: [:ecto, :phoenix],
+  inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"],
+  subdirectories: ["priv/*/migrations"]
+]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..95279e6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+# The directory Mix will write compiled artifacts to.
+/_build/
+
+# If you run "mix test --cover", coverage assets end up here.
+/cover/
+
+# The directory Mix downloads your dependencies sources to.
+/deps/
+
+# Where 3rd-party dependencies like ExDoc output generated docs.
+/doc/
+
+# Ignore .fetch files in case you like to edit your project deps locally.
+/.fetch
+
+# If the VM crashes, it generates a dump, let's ignore it too.
+erl_crash.dump
+
+# Also ignore archive artifacts (built via "mix archive.build").
+*.ez
+
+# Ignore package tarball (built via "mix hex.build").
+rocketpay-*.tar
+
+# Since we are building assets from assets/,
+# we ignore priv/static. You may want to comment
+# this depending on your deployment strategy.
+/priv/static/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4a806a0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Rocketpay
+
+To start your Phoenix server:
+
+  * Install dependencies with `mix deps.get`
+  * Create and migrate your database with `mix ecto.setup`
+  * Start Phoenix endpoint with `mix phx.server`
+
+Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
+
+Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
+
+## Learn more
+
+  * Official website: https://www.phoenixframework.org/
+  * Guides: https://hexdocs.pm/phoenix/overview.html
+  * Docs: https://hexdocs.pm/phoenix
+  * Forum: https://elixirforum.com/c/phoenix-forum
+  * Source: https://github.com/phoenixframework/phoenix
diff --git a/config/config.exs b/config/config.exs
new file mode 100644
index 0000000..4aa4f25
--- /dev/null
+++ b/config/config.exs
@@ -0,0 +1,35 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+#
+# This configuration file is loaded before any dependency and
+# is restricted to this project.
+
+# General application configuration
+use Mix.Config
+
+config :rocketpay,
+  ecto_repos: [Rocketpay.Repo]
+
+# Configures the endpoint
+config :rocketpay, RocketpayWeb.Endpoint,
+  url: [host: "localhost"],
+  secret_key_base: "YC4F013+4lHWU5oaxTEZfDrXRdbLXBeqhI0ma+I4aDSciDPfef2+0tvTmCa1b749",
+  render_errors: [view: RocketpayWeb.ErrorView, accepts: ~w(json), layout: false],
+  pubsub_server: Rocketpay.PubSub,
+  live_view: [signing_salt: "JZ7OW6fN"]
+
+config :rocketpay, Rocketpay.Repo,
+  migration_primary_key: [type: :binary_id],
+  migration_foreign_key: [type: :binary_id]
+
+# Configures Elixir's Logger
+config :logger, :console,
+  format: "$time $metadata[$level] $message\n",
+  metadata: [:request_id]
+
+# Use Jason for JSON parsing in Phoenix
+config :phoenix, :json_library, Jason
+
+# Import environment specific config. This must remain at the bottom
+# of this file so it overrides the configuration defined above.
+import_config "#{Mix.env()}.exs"
diff --git a/config/dev.exs b/config/dev.exs
new file mode 100644
index 0000000..9d4f99e
--- /dev/null
+++ b/config/dev.exs
@@ -0,0 +1,57 @@
+use Mix.Config
+
+# Configure your database
+config :rocketpay, Rocketpay.Repo,
+  username: "postgres",
+  password: "password",
+  database: "rocketpay_dev",
+  hostname: "localhost",
+  show_sensitive_data_on_connection_error: true,
+  pool_size: 10
+
+# For development, we disable any cache and enable
+# debugging and code reloading.
+#
+# The watchers configuration can be used to run external
+# watchers to your application. For example, we use it
+# with webpack to recompile .js and .css sources.
+config :rocketpay, RocketpayWeb.Endpoint,
+  http: [port: 4000],
+  debug_errors: true,
+  code_reloader: true,
+  check_origin: false,
+  watchers: []
+
+# ## SSL Support
+#
+# In order to use HTTPS in development, a self-signed
+# certificate can be generated by running the following
+# Mix task:
+#
+#     mix phx.gen.cert
+#
+# Note that this task requires Erlang/OTP 20 or later.
+# Run `mix help phx.gen.cert` for more information.
+#
+# The `http:` config above can be replaced with:
+#
+#     https: [
+#       port: 4001,
+#       cipher_suite: :strong,
+#       keyfile: "priv/cert/selfsigned_key.pem",
+#       certfile: "priv/cert/selfsigned.pem"
+#     ],
+#
+# If desired, both `http:` and `https:` keys can be
+# configured to run both http and https servers on
+# different ports.
+
+# Do not include metadata nor timestamps in development logs
+config :logger, :console, format: "[$level] $message\n"
+
+# Set a higher stacktrace during development. Avoid configuring such
+# in production as building large stacktraces may be expensive.
+config :phoenix, :stacktrace_depth, 20
+
+# Initialize plugs at runtime for faster development compilation
+config :phoenix, :plug_init_mode, :runtime
diff --git a/config/prod.exs b/config/prod.exs
new file mode 100644
index 0000000..72359fc
--- /dev/null
+++ b/config/prod.exs
@@ -0,0 +1,55 @@
+use Mix.Config
+
+# For production, don't forget to configure the url host
+# to something meaningful, Phoenix uses this information
+# when generating URLs.
+#
+# Note we also include the path to a cache manifest
+# containing the digested version of static files. This
+# manifest is generated by the `mix phx.digest` task,
+# which you should run after static files are built and
+# before starting your production server.
+config :rocketpay, RocketpayWeb.Endpoint,
+  url: [host: "example.com", port: 80],
+  cache_static_manifest: "priv/static/cache_manifest.json"
+
+# Do not print debug messages in production
+config :logger, level: :info
+
+# ## SSL Support
+#
+# To get SSL working, you will need to add the `https` key
+# to the previous section and set your `:url` port to 443:
+#
+#     config :rocketpay, RocketpayWeb.Endpoint,
+#       ...
+#       url: [host: "example.com", port: 443],
+#       https: [
+#         port: 443,
+#         cipher_suite: :strong,
+#         keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
+#         certfile: System.get_env("SOME_APP_SSL_CERT_PATH"),
+#         transport_options: [socket_opts: [:inet6]]
+#       ]
+#
+# The `cipher_suite` is set to `:strong` to support only the
+# latest and more secure SSL ciphers. This means old browsers
+# and clients may not be supported. You can set it to
+# `:compatible` for wider support.
+#
+# `:keyfile` and `:certfile` expect an absolute path to the key
+# and cert in disk or a relative path inside priv, for example
+# "priv/ssl/server.key". For all supported SSL configuration
+# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1
+#
+# We also recommend setting `force_ssl` in your endpoint, ensuring
+# no data is ever sent via http, always redirecting to https:
+#
+#     config :rocketpay, RocketpayWeb.Endpoint,
+#       force_ssl: [hsts: true]
+#
+# Check `Plug.SSL` for all available options in `force_ssl`.
+
+# Finally import the config/prod.secret.exs which loads secrets
+# and configuration from environment variables.
+import_config "prod.secret.exs"
diff --git a/config/prod.secret.exs b/config/prod.secret.exs
new file mode 100644
index 0000000..7dbcff4
--- /dev/null
+++ b/config/prod.secret.exs
@@ -0,0 +1,41 @@
+# In this file, we load production configuration and secrets
+# from environment variables. You can also hardcode secrets,
+# although such is generally not recommended and you have to
+# remember to add this file to your .gitignore.
+use Mix.Config
+
+database_url =
+  System.get_env("DATABASE_URL") ||
+    raise """
+    environment variable DATABASE_URL is missing.
+    For example: ecto://USER:PASS@HOST/DATABASE
+    """
+
+config :rocketpay, Rocketpay.Repo,
+  # ssl: true,
+  url: database_url,
+  pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
+
+secret_key_base =
+  System.get_env("SECRET_KEY_BASE") ||
+    raise """
+    environment variable SECRET_KEY_BASE is missing.
+    You can generate one by calling: mix phx.gen.secret
+    """
+
+config :rocketpay, RocketpayWeb.Endpoint,
+  http: [
+    port: String.to_integer(System.get_env("PORT") || "4000"),
+    transport_options: [socket_opts: [:inet6]]
+  ],
+  secret_key_base: secret_key_base
+
+# ## Using releases (Elixir v1.9+)
+#
+# If you are doing OTP releases, you need to instruct Phoenix
+# to start each relevant endpoint:
+#
+#     config :rocketpay, RocketpayWeb.Endpoint, server: true
+#
+# Then you can assemble a release by calling `mix release`.
+# See `mix help release` for more information.
diff --git a/config/test.exs b/config/test.exs
new file mode 100644
index 0000000..13ff8a1
--- /dev/null
+++ b/config/test.exs
@@ -0,0 +1,22 @@
+use Mix.Config
+
+# Configure your database
+#
+# The MIX_TEST_PARTITION environment variable can be used
+# to provide built-in test partitioning in CI environment.
+# Run `mix help test` for more information.
+config :rocketpay, Rocketpay.Repo,
+  username: "postgres",
+  password: "password",
+  database: "rocketpay_test#{System.get_env("MIX_TEST_PARTITION")}",
+  hostname: "localhost",
+  pool: Ecto.Adapters.SQL.Sandbox
+
+# We don't run a server during test. If one is required,
+# you can enable the server option below.
+config :rocketpay, RocketpayWeb.Endpoint,
+  http: [port: 4002],
+  server: false
+
+# Print only warnings and errors during test
+config :logger, level: :warn
diff --git a/lib/rocketpay.ex b/lib/rocketpay.ex
new file mode 100644
index 0000000..def6357
--- /dev/null
+++ b/lib/rocketpay.ex
@@ -0,0 +1,5 @@
+defmodule Rocketpay do
+  alias Rocketpay.Users.Create, as: UserCreate
+
+  defdelegate create_user(params), to: UserCreate, as: :call
+end
diff --git a/lib/rocketpay/account.ex b/lib/rocketpay/account.ex
new file mode 100644
index 0000000..54fabea
--- /dev/null
+++ b/lib/rocketpay/account.ex
@@ -0,0 +1,24 @@
+defmodule Rocketpay.Account do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  alias Rocketpay.User
+
+  @primary_key {:id, :binary_id, autogenerate: true}
+  @foreign_key_type :binary_id
+  @required_params [:balance, :user_id]
+
+  schema "accounts" do
+    field :balance, :decimal
+    belongs_to :user, User
+
+    timestamps()
+  end
+
+  def changeset(params) do
+    %__MODULE__{}
+      |> cast(params, @required_params)
+      |> validate_required(@required_params)
+      |> check_constraint(:balance, name: :balance_must_be_positive_or_zero)
+  end
+end
diff --git a/lib/rocketpay/application.ex b/lib/rocketpay/application.ex
new file mode 100644
index 0000000..d1fb2fe
--- /dev/null
+++ b/lib/rocketpay/application.ex
@@ -0,0 +1,34 @@
+defmodule Rocketpay.Application do
+  # See https://hexdocs.pm/elixir/Application.html
+  # for more information on OTP Applications
+  @moduledoc false
+
+  use Application
+
+  def start(_type, _args) do
+    children = [
+      # Start the Ecto repository
+      Rocketpay.Repo,
+      # Start the Telemetry supervisor
+      RocketpayWeb.Telemetry,
+      # Start the PubSub system
+      {Phoenix.PubSub, name: Rocketpay.PubSub},
+      # Start the Endpoint (http/https)
+      RocketpayWeb.Endpoint
+      # Start a worker by calling: Rocketpay.Worker.start_link(arg)
+      # {Rocketpay.Worker, arg}
+    ]
+
+    # See https://hexdocs.pm/elixir/Supervisor.html
+    # for other strategies and supported options
+    opts = [strategy: :one_for_one, name: Rocketpay.Supervisor]
+    Supervisor.start_link(children, opts)
+  end
+
+  # Tell Phoenix to update the endpoint configuration
+  # whenever the application is updated.
+  def config_change(changed, _new, removed) do
+    RocketpayWeb.Endpoint.config_change(changed, removed)
+    :ok
+  end
+end
diff --git a/lib/rocketpay/numbers.ex b/lib/rocketpay/numbers.ex
new file mode 100644
index 0000000..1137251
--- /dev/null
+++ b/lib/rocketpay/numbers.ex
@@ -0,0 +1,18 @@
+defmodule Rocketpay.Numbers do
+  def sum_from_file(filename) do
+    "#{filename}.csv"
+    |> File.read()
+    |> handle_file()
+  end
+
+  defp handle_file({:ok, result}) do
+    result =
+      result
+      |> String.split(",")
+      |> Stream.map(fn number -> String.to_integer(number) end)
+      |> Enum.sum()
+    {:ok, %{result: result}}
+  end
+
+  defp handle_file({:error, _reason}), do: {:error, %{message: "fail"}}
+end
diff --git a/lib/rocketpay/repo.ex b/lib/rocketpay/repo.ex
new file mode 100644
index 0000000..e8a057c
--- /dev/null
+++ b/lib/rocketpay/repo.ex
@@ -0,0 +1,5 @@
+defmodule Rocketpay.Repo do
+  use Ecto.Repo,
+    otp_app: :rocketpay,
+    adapter: Ecto.Adapters.Postgres
+end
diff --git a/lib/rocketpay/user.ex b/lib/rocketpay/user.ex
new file mode 100644
index 0000000..955eb4e
--- /dev/null
+++ b/lib/rocketpay/user.ex
@@ -0,0 +1,41 @@
+defmodule Rocketpay.User do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  alias Ecto.Changeset
+  alias Rocketpay.Account
+
+  @primary_key {:id, :binary_id, autogenerate: true}
+  @required_params [:name, :age, :email, :password, :nickname]
+
+  schema "users" do
+    field :name, :string
+    field :age, :integer
+    field :email, :string
+    field :password, :string, virtual: true
+    field :password_hash, :string
+    field :nickname, :string
+
+    has_one :account, Account
+
+    timestamps()
+  end
+
+  def changeset(params) do
+    %__MODULE__{}
+      |> cast(params, @required_params)
+      |> validate_required(@required_params)
+      |> validate_length(:password, min: 6)
+      |> validate_number(:age, greater_than_or_equal_to: 18)
+      |> validate_format(:email, ~r/@/)
+      |> unique_constraint([:email])
+      |> unique_constraint([:nickname])
+      |> put_password_hash()
+  end
+
+  defp put_password_hash(%Changeset{valid?: true, changes: %{password: password}} = changeset) do
+    change(changeset, Bcrypt.add_hash(password))
+  end
+
+  defp put_password_hash(changeset), do: changeset
+end
diff --git a/lib/rocketpay/users/create.ex b/lib/rocketpay/users/create.ex
new file mode 100644
index 0000000..00f4764
--- /dev/null
+++ b/lib/rocketpay/users/create.ex
@@ -0,0 +1,25 @@
+defmodule Rocketpay.Users.Create do
+  alias Rocketpay.{User, Account}
+  alias Ecto.Multi
+
+  def call(params) do
+    Multi.new()
+      |> Multi.insert(:create_user, User.changeset(params))
+      |> Multi.run(:create_account, fn repo, %{create_user: user} ->
+        insert_account(repo, user)
+      end)
+  end
+
+  defp insert_account(repo, user) do
+    user.id
+      |> account_changeset()
+      |> repo.insert()
+  end
+
+  defp account_changeset(user_id) do
+    params = %{user_id: user_id, balance: "0.00"}
+
+    Account.changeset(params)
+
+  end
+end
diff --git a/lib/rocketpay_web.ex b/lib/rocketpay_web.ex
new file mode 100644
index 0000000..d22bf2c
--- /dev/null
+++ b/lib/rocketpay_web.ex
@@ -0,0 +1,78 @@
+defmodule RocketpayWeb do
+  @moduledoc """
+  The entrypoint for defining your web interface, such
+  as controllers, views, channels and so on.
+
+  This can be used in your application as:
+
+      use RocketpayWeb, :controller
+      use RocketpayWeb, :view
+
+  The definitions below will be executed for every view,
+  controller, etc, so keep them short and clean, focused
+  on imports, uses and aliases.
+
+  Do NOT define functions inside the quoted expressions
+  below. Instead, define any helper function in modules
+  and import those modules here.
+  """
+
+  def controller do
+    quote do
+      use Phoenix.Controller, namespace: RocketpayWeb
+
+      import Plug.Conn
+      import RocketpayWeb.Gettext
+      alias RocketpayWeb.Router.Helpers, as: Routes
+    end
+  end
+
+  def view do
+    quote do
+      use Phoenix.View,
+        root: "lib/rocketpay_web/templates",
+        namespace: RocketpayWeb
+
+      # Import convenience functions from controllers
+      import Phoenix.Controller,
+        only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
+
+      # Include shared imports and aliases for views
+      unquote(view_helpers())
+    end
+  end
+
+  def router do
+    quote do
+      use Phoenix.Router
+
+      import Plug.Conn
+      import Phoenix.Controller
+    end
+  end
+
+  def channel do
+    quote do
+      use Phoenix.Channel
+      import RocketpayWeb.Gettext
+    end
+  end
+
+  defp view_helpers do
+    quote do
+      # Import basic rendering functionality (render, render_layout, etc)
+      import Phoenix.View
+
+      import RocketpayWeb.ErrorHelpers
+      import RocketpayWeb.Gettext
+      alias RocketpayWeb.Router.Helpers, as: Routes
+    end
+  end
+
+  @doc """
+  When used, dispatch to the appropriate controller/view/etc.
+  """
+  defmacro __using__(which) when is_atom(which) do
+    apply(__MODULE__, which, [])
+  end
+end
diff --git a/lib/rocketpay_web/channels/user_socket.ex b/lib/rocketpay_web/channels/user_socket.ex
new file mode 100644
index 0000000..b1abd7a
--- /dev/null
+++ b/lib/rocketpay_web/channels/user_socket.ex
@@ -0,0 +1,35 @@
+defmodule RocketpayWeb.UserSocket do
+  use Phoenix.Socket
+
+  ## Channels
+  # channel "room:*", RocketpayWeb.RoomChannel
+
+  # Socket params are passed from the client and can
+  # be used to verify and authenticate a user. After
+  # verification, you can put default assigns into
+  # the socket that will be set for all channels, ie
+  #
+  #     {:ok, assign(socket, :user_id, verified_user_id)}
+  #
+  # To deny connection, return `:error`.
+  #
+  # See `Phoenix.Token` documentation for examples in
+  # performing token verification on connect.
+  @impl true
+  def connect(_params, socket, _connect_info) do
+    {:ok, socket}
+  end
+
+  # Socket id's are topics that allow you to identify all sockets for a given user:
+  #
+  #     def id(socket), do: "user_socket:#{socket.assigns.user_id}"
+  #
+  # Would allow you to broadcast a "disconnect" event and terminate
+  # all active sockets and channels for a given user:
+  #
+  #     RocketpayWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
+  #
+  # Returning `nil` makes this socket anonymous.
+  @impl true
+  def id(_socket), do: nil
+end
diff --git a/lib/rocketpay_web/controllers/fallback_controller.ex b/lib/rocketpay_web/controllers/fallback_controller.ex
new file mode 100644
index 0000000..00e9464
--- /dev/null
+++ b/lib/rocketpay_web/controllers/fallback_controller.ex
@@ -0,0 +1,10 @@
+defmodule RocketpayWeb.FallbackController do
+  use RocketpayWeb, :controller
+
+  def call(conn, {:error, result}) do
+    conn
+      |> put_status(:bad_request)
+      |> put_view(RocketpayWeb.ErrorView)
+      |> render("400.json", result: result)
+  end
+end
diff --git a/lib/rocketpay_web/controllers/users_controller.ex b/lib/rocketpay_web/controllers/users_controller.ex
new file mode 100644
index 0000000..cfe683e
--- /dev/null
+++ b/lib/rocketpay_web/controllers/users_controller.ex
@@ -0,0 +1,15 @@
+defmodule RocketpayWeb.UsersController do
+  use RocketpayWeb, :controller
+
+  alias Rocketpay.User
+
+  action_fallback RocketpayWeb.FallbackController
+
+  def create(conn, params) do
+    with {:ok, %User{} = user} <- Rocketpay.create_user(params) do
+      conn
+      |> put_status(:created)
+      |> render("create.json", user: user)
+    end
+  end
+end
diff --git a/lib/rocketpay_web/controllers/welcome_controller.ex b/lib/rocketpay_web/controllers/welcome_controller.ex
new file mode 100644
index 0000000..da70f46
--- /dev/null
+++ b/lib/rocketpay_web/controllers/welcome_controller.ex
@@ -0,0 +1,24 @@
+defmodule RocketpayWeb.WelcomeController do
+  use RocketpayWeb, :controller
+
+  alias Rocketpay.Numbers
+
+  def index(conn, %{"filename" => filename}) do
+    filename
+    |> Numbers.sum_from_file()
+    |> handle_response(conn)
+    # text(conn, "welcome to the Rocketpay API")
+  end
+
+  defp handle_response({:ok, %{result: result}}, conn) do
+    conn
+      |> put_status(:ok)
+      |> json(%{message: "Welcome to API. Here is your number #{result}"})
+  end
+
+  defp handle_response({:error, reason}, conn) do
+    conn
+      |> put_status(:bad_request)
+      |> json(reason)
+  end
+end
diff --git a/lib/rocketpay_web/endpoint.ex b/lib/rocketpay_web/endpoint.ex
new file mode 100644
index 0000000..eef9528
--- /dev/null
+++ b/lib/rocketpay_web/endpoint.ex
@@ -0,0 +1,52 @@
+defmodule RocketpayWeb.Endpoint do
+  use Phoenix.Endpoint, otp_app: :rocketpay
+
+  # The session will be stored in the cookie and signed,
+  # this means its contents can be read but not tampered with.
+  # Set :encryption_salt if you would also like to encrypt it.
+  @session_options [
+    store: :cookie,
+    key: "_rocketpay_key",
+    signing_salt: "OChs6IbA"
+  ]
+
+  socket "/socket", RocketpayWeb.UserSocket,
+    websocket: true,
+    longpoll: false
+
+  socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
+
+  # Serve at "/" the static files from "priv/static" directory.
+  #
+  # You should set gzip to true if you are running phx.digest
+  # when deploying your static files in production.
+  plug Plug.Static,
+    at: "/",
+    from: :rocketpay,
+    gzip: false,
+    only: ~w(css fonts images js favicon.ico robots.txt)
+
+  # Code reloading can be explicitly enabled under the
+  # :code_reloader configuration of your endpoint.
+  if code_reloading? do
+    plug Phoenix.CodeReloader
+    plug Phoenix.Ecto.CheckRepoStatus, otp_app: :rocketpay
+  end
+
+  plug Phoenix.LiveDashboard.RequestLogger,
+    param_key: "request_logger",
+    cookie_key: "request_logger"
+
+  plug Plug.RequestId
+  plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
+
+  plug Plug.Parsers,
+    parsers: [:urlencoded, :multipart, :json],
+    pass: ["*/*"],
+    json_decoder: Phoenix.json_library()
+
+  plug Plug.MethodOverride
+  plug Plug.Head
+  plug Plug.Session, @session_options
+  plug RocketpayWeb.Router
+end
diff --git a/lib/rocketpay_web/gettext.ex b/lib/rocketpay_web/gettext.ex
new file mode 100644
index 0000000..697f3ed
--- /dev/null
+++ b/lib/rocketpay_web/gettext.ex
@@ -0,0 +1,24 @@
+defmodule RocketpayWeb.Gettext do
+  @moduledoc """
+  A module providing Internationalization with a gettext-based API.
+
+  By using [Gettext](https://hexdocs.pm/gettext),
+  your module gains a set of macros for translations, for example:
+
+      import RocketpayWeb.Gettext
+
+      # Simple translation
+      gettext("Here is the string to translate")
+
+      # Plural translation
+      ngettext("Here is the string to translate",
+               "Here are the strings to translate",
+               3)
+
+      # Domain-based translation
+      dgettext("errors", "Here is the error message to translate")
+
+  See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
+  """
+  use Gettext, otp_app: :rocketpay
+end
diff --git a/lib/rocketpay_web/router.ex b/lib/rocketpay_web/router.ex
new file mode 100644
index 0000000..c71cdf6
--- /dev/null
+++ b/lib/rocketpay_web/router.ex
@@ -0,0 +1,30 @@
+defmodule RocketpayWeb.Router do
+  use RocketpayWeb, :router
+
+  pipeline :api do
+    plug :accepts, ["json"]
+  end
+
+  scope "/api", RocketpayWeb do
+    pipe_through :api
+
+    get "/:filename", WelcomeController, :index
+    post "/users", UsersController, :create
+  end
+
+  # Enables LiveDashboard only for development
+  #
+  # If you want to use the LiveDashboard in production, you should put
+  # it behind authentication and allow only admins to access it.
+  # If your application does not have an admins-only section yet,
+  # you can use Plug.BasicAuth to set up some basic authentication
+  # as long as you are also using SSL (which you should anyway).
+  if Mix.env() in [:dev, :test] do
+    import Phoenix.LiveDashboard.Router
+
+    scope "/" do
+      pipe_through [:fetch_session, :protect_from_forgery]
+      live_dashboard "/dashboard", metrics: RocketpayWeb.Telemetry
+    end
+  end
+end
diff --git a/lib/rocketpay_web/telemetry.ex b/lib/rocketpay_web/telemetry.ex
new file mode 100644
index 0000000..11281f3
--- /dev/null
+++ b/lib/rocketpay_web/telemetry.ex
@@ -0,0 +1,55 @@
+defmodule RocketpayWeb.Telemetry do
+  use Supervisor
+  import Telemetry.Metrics
+
+  def start_link(arg) do
+    Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
+  end
+
+  @impl true
+  def init(_arg) do
+    children = [
+      # Telemetry poller will execute the given period measurements
+      # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
+      {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
+      # Add reporters as children of your supervision tree.
+      # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
+    ]
+
+    Supervisor.init(children, strategy: :one_for_one)
+  end
+
+  def metrics do
+    [
+      # Phoenix Metrics
+      summary("phoenix.endpoint.stop.duration",
+        unit: {:native, :millisecond}
+      ),
+      summary("phoenix.router_dispatch.stop.duration",
+        tags: [:route],
+        unit: {:native, :millisecond}
+      ),
+
+      # Database Metrics
+      summary("rocketpay.repo.query.total_time", unit: {:native, :millisecond}),
+      summary("rocketpay.repo.query.decode_time", unit: {:native, :millisecond}),
+      summary("rocketpay.repo.query.query_time", unit: {:native, :millisecond}),
+      summary("rocketpay.repo.query.queue_time", unit: {:native, :millisecond}),
+      summary("rocketpay.repo.query.idle_time", unit: {:native, :millisecond}),
+
+      # VM Metrics
+      summary("vm.memory.total", unit: {:byte, :kilobyte}),
+      summary("vm.total_run_queue_lengths.total"),
+      summary("vm.total_run_queue_lengths.cpu"),
+      summary("vm.total_run_queue_lengths.io")
+    ]
+  end
+
+  defp periodic_measurements do
+    [
+      # A module, function and arguments to be invoked periodically.
+      # This function must call :telemetry.execute/3 and a metric must be added above.
+      # {RocketpayWeb, :count_users, []}
+    ]
+  end
+end
diff --git a/lib/rocketpay_web/views/error_helpers.ex b/lib/rocketpay_web/views/error_helpers.ex
new file mode 100644
index 0000000..b6a55c4
--- /dev/null
+++ b/lib/rocketpay_web/views/error_helpers.ex
@@ -0,0 +1,33 @@
+defmodule RocketpayWeb.ErrorHelpers do
+  @moduledoc """
+  Conveniences for translating and building error messages.
+  """
+
+  @doc """
+  Translates an error message using gettext.
+  """
+  def translate_error({msg, opts}) do
+    # When using gettext, we typically pass the strings we want
+    # to translate as a static argument:
+    #
+    #     # Translate "is invalid" in the "errors" domain
+    #     dgettext("errors", "is invalid")
+    #
+    #     # Translate the number of files with plural rules
+    #     dngettext("errors", "1 file", "%{count} files", count)
+    #
+    # Because the error messages we show in our forms and APIs
+    # are defined inside Ecto, we need to translate them dynamically.
+    # This requires us to call the Gettext module passing our gettext
+    # backend as first argument.
+    #
+    # Note we use the "errors" domain, which means translations
+    # should be written to the errors.po file. The :count option is
+    # set by Ecto and indicates we should also apply plural rules.
+    if count = opts[:count] do
+      Gettext.dngettext(RocketpayWeb.Gettext, "errors", msg, msg, count, opts)
+    else
+      Gettext.dgettext(RocketpayWeb.Gettext, "errors", msg, opts)
+    end
+  end
+end
diff --git a/lib/rocketpay_web/views/error_view.ex b/lib/rocketpay_web/views/error_view.ex
new file mode 100644
index 0000000..1c26277
--- /dev/null
+++ b/lib/rocketpay_web/views/error_view.ex
@@ -0,0 +1,30 @@
+defmodule RocketpayWeb.ErrorView do
+  use RocketpayWeb, :view
+
+  import Ecto.Changeset, only: [traverse_errors: 2]
+
+  # If you want to customize a particular status code
+  # for a certain format, you may uncomment below.
+  # def render("500.json", _assigns) do
+  #   %{errors: %{detail: "Internal Server Error"}}
+  # end
+
+  # By default, Phoenix returns the status message from
+  # the template name. For example, "404.json" becomes
+  # "Not Found".
+  def template_not_found(template, _assigns) do
+    %{errors: %{detail: Phoenix.Controller.status_message_from_template(template)}}
+  end
+
+  def render("400.json", %{result: %Ecto.Changeset{} = changeset}) do
+    %{message: translate_errors(changeset)}
+  end
+
+  defp translate_errors(changeset) do
+    traverse_errors(changeset, fn {msg, opts} ->
+      Enum.reduce(opts, msg, fn {key, value}, acc ->
+        String.replace(acc, "%{#{key}}", to_string(value))
+      end)
+    end)
+  end
+end
diff --git a/lib/rocketpay_web/views/users_view.ex b/lib/rocketpay_web/views/users_view.ex
new file mode 100644
index 0000000..95f6d87
--- /dev/null
+++ b/lib/rocketpay_web/views/users_view.ex
@@ -0,0 +1,14 @@
+defmodule RocketpayWeb.UsersView do
+  alias Rocketpay.User
+
+  def render("create.json", %{user: %User{id: id, name: name, nickname: nickname}}) do
+    %{
+      message: "User created",
+      user: %{
+        id: id,
+        name: name,
+        nickname: nickname
+      }
+    }
+  end
+end
diff --git a/mix.exs b/mix.exs
new file mode 100644
index 0000000..6fa67d1
--- /dev/null
+++ b/mix.exs
@@ -0,0 +1,65 @@
+defmodule Rocketpay.MixProject do
+  use Mix.Project
+
+  def project do
+    [
+      app: :rocketpay,
+      version: "0.1.0",
+      elixir: "~> 1.7",
+      elixirc_paths: elixirc_paths(Mix.env()),
+      compilers: [:phoenix, :gettext] ++ Mix.compilers(),
+      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: {Rocketpay.Application, []},
+      extra_applications: [:logger, :runtime_tools]
+    ]
+  end
+
+  # Specifies which paths to compile per environment.
+  defp elixirc_paths(:test), do: ["lib", "test/support"]
+  defp elixirc_paths(_), do: ["lib"]
+
+  # Specifies your project dependencies.
+  #
+  # Type `mix help deps` for examples and options.
+  defp deps do
+    [
+      {:phoenix, "~> 1.5.7"},
+      {:phoenix_ecto, "~> 4.1"},
+      {:ecto_sql, "~> 3.4"},
+      {:postgrex, ">= 0.0.0"},
+      {:phoenix_live_dashboard, "~> 0.4"},
+      {:telemetry_metrics, "~> 0.4"},
+      {:telemetry_poller, "~> 0.4"},
+      {:gettext, "~> 0.11"},
+      {:jason, "~> 1.0"},
+      {:plug_cowboy, "~> 2.0"},
+      {:credo, "~> 1.5", only: [:dev, :test], runtime: false},
+      {:bcrypt_elixir, "~> 2.0"}
+    ]
+  end
+
+  # Aliases are shortcuts or tasks specific to the current project.
+  # For example, to install project dependencies and perform other setup tasks, run:
+  #
+  #     $ mix setup
+  #
+  # See the documentation for `Mix` for more info on aliases.
+  defp aliases do
+    [
+      setup: ["deps.get", "ecto.setup"],
+      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
+      "ecto.reset": ["ecto.drop", "ecto.setup"],
+      test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
+    ]
+  end
+end
diff --git a/mix.lock b/mix.lock
new file mode 100644
index 0000000..abbee8a
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,33 @@
+%{
+  "bcrypt_elixir": {:hex, :bcrypt_elixir, "2.3.0", "6cb662d5c1b0a8858801cf20997bd006e7016aa8c52959c9ef80e0f34fb60b7a", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2c81d61d4f6ed0e5cf7bf27a9109b791ff216a1034b3d541327484f46dd43769"},
+  "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
+  "comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
+  "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
+  "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"},
+  "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.3.1", "ebd1a1d7aff97f27c66654e78ece187abdc646992714164380d8a041eda16754", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a6efd3366130eab84ca372cbd4a7d3c3a97bdfcfb4911233b035d117063f0af"},
+  "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"},
+  "credo": {:hex, :credo, "1.5.5", "e8f422026f553bc3bebb81c8e8bf1932f498ca03339856c7fec63d3faac8424b", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dd8623ab7091956a855dc9f3062486add9c52d310dfd62748779c4315d8247de"},
+  "db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"},
+  "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
+  "ecto": {:hex, :ecto, "3.5.8", "8ebf12be6016cb99313348ba7bb4612f4114b9a506d6da79a2adc7ef449340bc", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ea0be182ea8922eb7742e3ae8e71b67ee00ae177de1bf76210299a5f16ba4c77"},
+  "ecto_sql": {:hex, :ecto_sql, "3.5.4", "a9e292c40bd79fff88885f95f1ecd7b2516e09aa99c7dd0201aa84c54d2358e4", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1fff1a28a898d7bbef263f1f3ea425b04ba9f33816d843238c84eff883347343"},
+  "elixir_make": {:hex, :elixir_make, "0.6.2", "7dffacd77dec4c37b39af867cedaabb0b59f6a871f89722c25b28fcd4bd70530", [:mix], [], "hexpm", "03e49eadda22526a7e5279d53321d1cced6552f344ba4e03e619063de75348d9"},
+  "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
+  "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
+  "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
+  "mime": {:hex, :mime, "1.5.0", "203ef35ef3389aae6d361918bf3f952fa17a09e8e43b5aa592b93eba05d0fb8d", [:mix], [], "hexpm", "55a94c0f552249fc1a3dd9cd2d3ab9de9d3c89b559c2bd01121f824834f24746"},
+  "phoenix": {:hex, :phoenix, "1.5.7", "2923bb3af924f184459fe4fa4b100bd25fa6468e69b2803dfae82698269aa5e0", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "774cd64417c5a3788414fdbb2be2eb9bcd0c048d9e6ad11a0c1fd67b7c0d0978"},
+  "phoenix_ecto": {:hex, :phoenix_ecto, "4.2.1", "13f124cf0a3ce0f1948cf24654c7b9f2347169ff75c1123f44674afee6af3b03", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "478a1bae899cac0a6e02be1deec7e2944b7754c04e7d4107fc5a517f877743c0"},
+  "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
+  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.4.0", "87990e68b60213d7487e65814046f9a2bed4a67886c943270125913499b3e5c3", [:mix], [{:ecto_psql_extras, "~> 0.4.1 or ~> 0.5", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0 or ~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "8d52149e58188e9e4497cc0d8900ab94d9b66f96998ec38c47c7a4f8f4f50e57"},
+  "phoenix_live_view": {:hex, :phoenix_live_view, "0.15.4", "86908dc9603cc81c07e84725ee42349b5325cb250c9c20d3533856ff18dbb7dc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 0.5", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "35d78f3c35fe10a995dca5f4ab50165b7a90cbe02e23de245381558f821e9462"},
+  "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
+  "plug": {:hex, :plug, "1.11.0", "f17217525597628298998bc3baed9f8ea1fa3f1160aa9871aee6df47a6e4d38e", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2d9c633f0499f9dc5c2fd069161af4e2e7756890b81adcbb2ceaa074e8308876"},
+  "plug_cowboy": {:hex, :plug_cowboy, "2.4.1", "779ba386c0915027f22e14a48919a9545714f849505fa15af2631a0d298abf0f", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d72113b6dff7b37a7d9b2a5b68892808e3a9a752f2bf7e503240945385b70507"},
+  "plug_crypto": {:hex, :plug_crypto, "1.2.1", "5c854427528bf61d159855cedddffc0625e2228b5f30eff76d5a4de42d896ef4", [:mix], [], "hexpm", "6961c0e17febd9d0bfa89632d391d2545d2e0eb73768f5f50305a23961d8782c"},
+  "postgrex": {:hex, :postgrex, "0.15.8", "f5e782bbe5e8fa178d5e3cd1999c857dc48eda95f0a4d7f7bd92a50e84a0d491", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "698fbfacea34c4cf22c8281abeb5cf68d99628d541874f085520ab3b53d356fe"},
+  "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"},
+  "telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
+  "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.0", "da9d49ee7e6bb1c259d36ce6539cd45ae14d81247a2b0c90edf55e2b50507f7b", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cfe67ad464b243835512aa44321cee91faed6ea868d7fb761d7016e02915c3d"},
+  "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
+}
diff --git a/numbers.csv b/numbers.csv
new file mode 100644
index 0000000..38fd022
--- /dev/null
+++ b/numbers.csv
@@ -0,0 +1 @@
+1,2,3,4,8,9,10
\ No newline at end of file
diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po
new file mode 100644
index 0000000..a589998
--- /dev/null
+++ b/priv/gettext/en/LC_MESSAGES/errors.po
@@ -0,0 +1,97 @@
+## `msgid`s in this file come from POT (.pot) files.
+##
+## Do not add, change, or remove `msgid`s manually here as
+## they're tied to the ones in the corresponding POT file
+## (with the same domain).
+##
+## Use `mix gettext.extract --merge` or `mix gettext.merge`
+## to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: en\n"
+
+## From Ecto.Changeset.cast/4
+msgid "can't be blank"
+msgstr ""
+
+## From Ecto.Changeset.unique_constraint/3
+msgid "has already been taken"
+msgstr ""
+
+## From Ecto.Changeset.put_change/3
+msgid "is invalid"
+msgstr ""
+
+## From Ecto.Changeset.validate_acceptance/3
+msgid "must be accepted"
+msgstr ""
+
+## From Ecto.Changeset.validate_format/3
+msgid "has invalid format"
+msgstr ""
+
+## From Ecto.Changeset.validate_subset/3
+msgid "has an invalid entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_exclusion/3
+msgid "is reserved"
+msgstr ""
+
+## From Ecto.Changeset.validate_confirmation/3
+msgid "does not match confirmation"
+msgstr ""
+
+## From Ecto.Changeset.no_assoc_constraint/3
+msgid "is still associated with this entry"
+msgstr ""
+
+msgid "are still associated with this entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_length/3
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+## From Ecto.Changeset.validate_number/3
+msgid "must be less than %{number}"
+msgstr ""
+
+msgid "must be greater than %{number}"
+msgstr ""
+
+msgid "must be less than or equal to %{number}"
+msgstr ""
+
+msgid "must be greater than or equal to %{number}"
+msgstr ""
+
+msgid "must be equal to %{number}"
+msgstr ""
diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot
new file mode 100644
index 0000000..39a220b
--- /dev/null
+++ b/priv/gettext/errors.pot
@@ -0,0 +1,95 @@
+## This is a PO Template file.
+##
+## `msgid`s here are often extracted from source code.
+## Add new translations manually only if they're dynamic
+## translations that can't be statically extracted.
+##
+## Run `mix gettext.extract` to bring this file up to
+## date. Leave `msgstr`s empty as changing them here has no
+## effect: edit them in PO (`.po`) files instead.
+
+## From Ecto.Changeset.cast/4
+msgid "can't be blank"
+msgstr ""
+
+## From Ecto.Changeset.unique_constraint/3
+msgid "has already been taken"
+msgstr ""
+
+## From Ecto.Changeset.put_change/3
+msgid "is invalid"
+msgstr ""
+
+## From Ecto.Changeset.validate_acceptance/3
+msgid "must be accepted"
+msgstr ""
+
+## From Ecto.Changeset.validate_format/3
+msgid "has invalid format"
+msgstr ""
+
+## From Ecto.Changeset.validate_subset/3
+msgid "has an invalid entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_exclusion/3
+msgid "is reserved"
+msgstr ""
+
+## From Ecto.Changeset.validate_confirmation/3
+msgid "does not match confirmation"
+msgstr ""
+
+## From Ecto.Changeset.no_assoc_constraint/3
+msgid "is still associated with this entry"
+msgstr ""
+
+msgid "are still associated with this entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_length/3
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+## From Ecto.Changeset.validate_number/3
+msgid "must be less than %{number}"
+msgstr ""
+
+msgid "must be greater than %{number}"
+msgstr ""
+
+msgid "must be less than or equal to %{number}"
+msgstr ""
+
+msgid "must be greater than or equal to %{number}"
+msgstr ""
+
+msgid "must be equal to %{number}"
+msgstr ""
diff --git a/priv/repo/migrations/.formatter.exs b/priv/repo/migrations/.formatter.exs
new file mode 100644
index 0000000..49f9151
--- /dev/null
+++ b/priv/repo/migrations/.formatter.exs
@@ -0,0 +1,4 @@
+[
+  import_deps: [:ecto_sql],
+  inputs: ["*.exs"]
+]
diff --git a/priv/repo/migrations/20210223121607_create_user_table.exs b/priv/repo/migrations/20210223121607_create_user_table.exs
new file mode 100644
index 0000000..6b804db
--- /dev/null
+++ b/priv/repo/migrations/20210223121607_create_user_table.exs
@@ -0,0 +1,18 @@
+defmodule Rocketpay.Repo.Migrations.CreateUserTable do
+  use Ecto.Migration
+
+  def change do
+    create table :users do
+      add :name, :string
+      add :age, :integer
+      add :email, :string
+      add :password_hash, :string
+      add :nickname, :string
+
+      timestamps()
+    end
+
+    create unique_index(:users, [:email])
+    create unique_index(:users, [:nickname])
+  end
+end
diff --git a/priv/repo/migrations/20210224163906_create_accounts_table.exs b/priv/repo/migrations/20210224163906_create_accounts_table.exs
new file mode 100644
index 0000000..8d9f728
--- /dev/null
+++ b/priv/repo/migrations/20210224163906_create_accounts_table.exs
@@ -0,0 +1,14 @@
+defmodule Rocketpay.Repo.Migrations.CreateAccountsTable do
+  use Ecto.Migration
+
+  def change do
+    create table :accounts do
+      add :balance, :decimal
+      add :user_id, references(:users, type: :binary_id)
+
+      timestamps()
+    end
+
+    create constraint(:accounts, :balance_must_be_positive_or_zero, check: "balance >= 0")
+  end
+end
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
new file mode 100644
index 0000000..cd631ac
--- /dev/null
+++ b/priv/repo/seeds.exs
@@ -0,0 +1,11 @@
+# Script for populating the database. You can run it as:
+#
+#     mix run priv/repo/seeds.exs
+#
+# Inside the script, you can read and write to any of your
+# repositories directly:
+#
+#     Rocketpay.Repo.insert!(%Rocketpay.SomeSchema{})
+#
+# We recommend using the bang functions (`insert!`, `update!`
+# and so on) as they will fail if something goes wrong.
diff --git a/test/rocketpay/numbers_test.exs b/test/rocketpay/numbers_test.exs
new file mode 100644
index 0000000..d47ac1e
--- /dev/null
+++ b/test/rocketpay/numbers_test.exs
@@ -0,0 +1,23 @@
+defmodule Rocketpay.NumbersTest do
+  use ExUnit.Case
+
+  alias Rocketpay.Numbers
+
+  describe "sum_from_file/1" do
+    test "when there is a file with the given name, returns the sum of numbers" do
+      response = Numbers.sum_from_file("numbers")
+
+      expected_response = {:ok, %{result: 37}}
+
+      assert response  == expected_response
+    end
+
+    test "when there is no a file with the given name, returns an error" do
+      response = Numbers.sum_from_file("banana")
+
+      expected_response = {:error, %{message: "fail"}}
+
+      assert response  == expected_response
+    end
+  end
+end
diff --git a/test/rocketpay_web/views/error_view_test.exs b/test/rocketpay_web/views/error_view_test.exs
new file mode 100644
index 0000000..829d163
--- /dev/null
+++ b/test/rocketpay_web/views/error_view_test.exs
@@ -0,0 +1,15 @@
+defmodule RocketpayWeb.ErrorViewTest do
+  use RocketpayWeb.ConnCase, async: true
+
+  # Bring render/3 and render_to_string/3 for testing custom views
+  import Phoenix.View
+
+  test "renders 404.json" do
+    assert render(RocketpayWeb.ErrorView, "404.json", []) == %{errors: %{detail: "Not Found"}}
+  end
+
+  test "renders 500.json" do
+    assert render(RocketpayWeb.ErrorView, "500.json", []) ==
+             %{errors: %{detail: "Internal Server Error"}}
+  end
+end
diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex
new file mode 100644
index 0000000..7e86660
--- /dev/null
+++ b/test/support/channel_case.ex
@@ -0,0 +1,40 @@
+defmodule RocketpayWeb.ChannelCase do
+  @moduledoc """
+  This module defines the test case to be used by
+  channel tests.
+
+  Such tests rely on `Phoenix.ChannelTest` and also
+  import other functionality to make it easier
+  to build common data structures and query the data layer.
+
+  Finally, if the test case interacts with the database,
+  we enable the SQL sandbox, so changes done to the database
+  are reverted at the end of every test. If you are using
+  PostgreSQL, you can even run database tests asynchronously
+  by setting `use RocketpayWeb.ChannelCase, async: true`, although
+  this option is not recommended for other databases.
+  """
+
+  use ExUnit.CaseTemplate
+
+  using do
+    quote do
+      # Import conveniences for testing with channels
+      import Phoenix.ChannelTest
+      import RocketpayWeb.ChannelCase
+
+      # The default endpoint for testing
+      @endpoint RocketpayWeb.Endpoint
+    end
+  end
+
+  setup tags do
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo)
+
+    unless tags[:async] do
+      Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()})
+    end
+
+    :ok
+  end
+end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
new file mode 100644
index 0000000..1489724
--- /dev/null
+++ b/test/support/conn_case.ex
@@ -0,0 +1,43 @@
+defmodule RocketpayWeb.ConnCase do
+  @moduledoc """
+  This module defines the test case to be used by
+  tests that require setting up a connection.
+
+  Such tests rely on `Phoenix.ConnTest` and also
+  import other functionality to make it easier
+  to build common data structures and query the data layer.
+
+  Finally, if the test case interacts with the database,
+  we enable the SQL sandbox, so changes done to the database
+  are reverted at the end of every test. If you are using
+  PostgreSQL, you can even run database tests asynchronously
+  by setting `use RocketpayWeb.ConnCase, async: true`, although
+  this option is not recommended for other databases.
+  """
+
+  use ExUnit.CaseTemplate
+
+  using do
+    quote do
+      # Import conveniences for testing with connections
+      import Plug.Conn
+      import Phoenix.ConnTest
+      import RocketpayWeb.ConnCase
+
+      alias RocketpayWeb.Router.Helpers, as: Routes
+
+      # The default endpoint for testing
+      @endpoint RocketpayWeb.Endpoint
+    end
+  end
+
+  setup tags do
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo)
+
+    unless tags[:async] do
+      Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()})
+    end
+
+    {:ok, conn: Phoenix.ConnTest.build_conn()}
+  end
+end
diff --git a/test/support/data_case.ex b/test/support/data_case.ex
new file mode 100644
index 0000000..7b0ca21
--- /dev/null
+++ b/test/support/data_case.ex
@@ -0,0 +1,55 @@
+defmodule Rocketpay.DataCase do
+  @moduledoc """
+  This module defines the setup for tests requiring
+  access to the application's data layer.
+
+  You may define functions here to be used as helpers in
+  your tests.
+
+  Finally, if the test case interacts with the database,
+  we enable the SQL sandbox, so changes done to the database
+  are reverted at the end of every test. If you are using
+  PostgreSQL, you can even run database tests asynchronously
+  by setting `use Rocketpay.DataCase, async: true`, although
+  this option is not recommended for other databases.
+  """
+
+  use ExUnit.CaseTemplate
+
+  using do
+    quote do
+      alias Rocketpay.Repo
+
+      import Ecto
+      import Ecto.Changeset
+      import Ecto.Query
+      import Rocketpay.DataCase
+    end
+  end
+
+  setup tags do
+    :ok = Ecto.Adapters.SQL.Sandbox.checkout(Rocketpay.Repo)
+
+    unless tags[:async] do
+      Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, {:shared, self()})
+    end
+
+    :ok
+  end
+
+  @doc """
+  A helper that transforms changeset errors into a map of messages.
+
+      assert {:error, changeset} = Accounts.create_user(%{password: "short"})
+      assert "password is too short" in errors_on(changeset).password
+      assert %{password: ["password is too short"]} = errors_on(changeset)
+
+  """
+  def errors_on(changeset) do
+    Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
+      Regex.replace(~r"%{(\w+)}", message, fn _, key ->
+        opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string()
+      end)
+    end)
+  end
+end
diff --git a/test/test_helper.exs b/test/test_helper.exs
new file mode 100644
index 0000000..49639cb
--- /dev/null
+++ b/test/test_helper.exs
@@ -0,0 +1,2 @@
+ExUnit.start()
+Ecto.Adapters.SQL.Sandbox.mode(Rocketpay.Repo, :manual)