### Start Token Server Process Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-server.md Starts a new Token Server process with the provided configuration. Use this function to initialize the server in your application. ```elixir iex> {:ok, server} = AppStore.Token.Server.start_link(%{ issuer_id: "57246542-96fe-1a63-e053-0824d011072a", bundle_id: "com.example.testbundleid2021", key: %{ id: "2X9R4HXF34", pem: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" } }) iex> is_pid(server) true ``` -------------------------------- ### Start Token Server with Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Starts a token server with automatic caching and refresh. Requires a configuration map with issuer ID, bundle ID, and key details. ```elixir config = %{ issuer_id: "57246542-96fe-1a63-e053-0824d011072a", bundle_id: "com.example.testbundleid2021", key: %{ id: "2X9R4HXF34", pem: "-----BEGIN PRIVATE KEY-----\nMIGfMA0GCSq...\n-----END PRIVATE KEY-----" } } {:ok, server} = AppStore.Token.Server.start_link(config) ``` -------------------------------- ### Integrate Token Server in Supervision Tree Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-server.md Example of how to include the AppStore.Token.Server in your application's supervision tree. Ensures the token server is started and supervised. ```elixir # In your application.ex def start(_type, _args) do children = [ {AppStore.HTTPClient.DefaultClient, []}, {AppStore.Token.Server, %{ issuer_id: System.fetch_env!("APPSTORE_ISSUER_ID"), bundle_id: System.fetch_env!("APPSTORE_BUNDLE_ID"), key: %{ id: System.fetch_env!("APPSTORE_KEY_ID"), pem: System.fetch_env!("APPSTORE_PRIVATE_KEY") } }}) ] Supervisor.start_link(children, strategy: :one_for_one) end ``` -------------------------------- ### Start Supervisor with AppStore Children Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/INDEX.md This snippet shows how to configure and start a supervisor with the AppStore's HTTP client and token server as children. It requires environment variables for issuer ID, bundle ID, key ID, and private key. ```elixir children = [ {AppStore.HTTPClient.DefaultClient, []}, {AppStore.Token.Server, %{ issuer_id: System.fetch_env!("APPSTORE_ISSUER_ID"), bundle_id: System.fetch_env!("APPSTORE_BUNDLE_ID"), key: %{ id: System.fetch_env!("APPSTORE_KEY_ID"), pem: System.fetch_env!("APPSTORE_PRIVATE_KEY") } }}] Supervisor.start_link(children, strategy: :one_for_one) ``` -------------------------------- ### DefaultClient Start Link with Custom Pools Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/http-client.md Starts the Finch HTTP client pool with custom pool configurations for sandbox and production servers. Allows for adjusting the pool size based on specific needs. ```elixir opts = [ pools: %{ AppStore.API.Config.sandbox_server_url() => [size: 2], AppStore.API.Config.production_server_url() => [size: 20] } ] AppStore.HTTPClient.DefaultClient.start_link(opts) ``` -------------------------------- ### AppStore.HTTPClient.DefaultClient.start_link/1 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/http-client.md Starts the Finch HTTP client pool with optional custom pool configurations. ```APIDOC ## start_link/1 ### Description Start the Finch HTTP client pool. ### Signature ```elixir @spec start_link(keyword()) :: {:ok, pid()} | {:error, any()} ``` ### Parameters #### Parameters - **opts** (keyword()) - Optional - Options passed to `Finch.start_link/1` - **opts[:pools]** (map()) - Optional - Pool configuration for sandbox and production ### Default Pool Configuration ```elixir pools: %{ "https://api.storekit-sandbox.itunes.apple.com" => [size: 1], "https://api.storekit.itunes.apple.com" => [size: 10] } ``` ### Examples Add to supervision tree: ```elixir def start(_type, _args) do children = [ {AppStore.HTTPClient.DefaultClient, []} ] Supervisor.start_link(children, strategy: :one_for_one) end ``` Custom pool configuration: ```elixir opts = [ pools: %{ AppStore.API.Config.sandbox_server_url() => [size: 2], AppStore.API.Config.production_server_url() => [size: 20] } ] AppStore.HTTPClient.DefaultClient.start_link(opts) ``` ``` -------------------------------- ### DefaultClient Start Link with Default Pools Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/http-client.md Starts the Finch HTTP client pool with default pool configurations for sandbox and production servers. This is typically added to a supervision tree. ```elixir def start(_type, _args) do children = [ {AppStore.HTTPClient.DefaultClient, []} ] Supervisor.start_link(children, strategy: :one_for_one) end ``` -------------------------------- ### start_link/1 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-server.md Starts a new Token Server process. This function is used to initialize the server with the necessary configuration for generating JWT tokens. ```APIDOC ## start_link/1 ### Description Start a new Token Server process. ### Method `start_link/1` ### Parameters #### Path Parameters - **config** (map()) - Required - Configuration map with `issuer_id`, `bundle_id`, and `key` - **config.issuer_id** (String.t()) - Required - Issuer ID (UUID) from App Store Connect - **config.bundle_id** (String.t()) - Required - App bundle ID (e.g., "com.example.app") - **config.key** (map()) - Required - Cryptographic key with `id` and `pem` fields - **config.key.id** (String.t()) - Required - Key ID from App Store Connect - **config.key.pem** (String.t()) - Required - Private key in PEM format ### Returns - `{:ok, pid}` — Process started successfully - `{:error, reason}` — Process failed to start - `:ignore` — Process startup ignored (rarely occurs) ### Request Example ```elixir iex> {:ok, server} = AppStore.Token.Server.start_link(%{ issuer_id: "57246542-96fe-1a63-e053-0824d011072a", bundle_id: "com.example.testbundleid2021", key: %{ id: "2X9R4HXF34", pem: "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" } }) iex> is_pid(server) true ``` ``` -------------------------------- ### AppStore.JSON.DefaultCoder decode! Example Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/json-coder.md Decodes a JSON string into a map using the default coder, which relies on the Jason library. This is a concrete example of the decode! callback. ```elixir iex> AppStore.JSON.DefaultCoder.decode!("{\"name\":\"John\",\"age\":30}") %{"name" => "John", "age" => 30} ``` -------------------------------- ### AppStore.JSON.DefaultCoder encode! Example Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/json-coder.md Encodes a map to a JSON string using the default coder, which relies on the Jason library. This is a concrete example of the encode! callback. ```elixir iex> AppStore.JSON.DefaultCoder.encode!(%{"name" => "John", "age" => 30}) "{\"name\":\"John\",\"age\":30}" ``` -------------------------------- ### Build App Store Client Source: https://github.com/linjunpop/app_store/blob/main/README.md Builds the App Store client instance. This example shows the structure of the returned `%AppStore{}` struct. ```elixir iex> app_store = AppStore.build() %AppStore{ api_config: %AppStore.API.Config{ http_client: AppStore.HTTPClient.DefaultClient, json_coder: AppStore.JSON.DefaultCoder, server_url: "https://api.storekit.itunes.apple.com" }, token_config: %AppStore.Token.Config{ json_coder: AppStore.JSON.DefaultCoder } ``` -------------------------------- ### Webhook Validation Example Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/README.md Provides an example of how to validate incoming webhooks from the App Store Server API. This is crucial for ensuring the authenticity and integrity of received data. ```elixir defmodule MyApp.WebhookHandler do @behaviour AppStore.WebhookHandler def handle_notification(notification, _opts) do case AppStore.Webhook.validate(notification) do {:ok, validated_notification} -> # Process the validated notification IO.inspect(validated_notification) {:ok, :processed} {:error, reason} -> # Handle validation error IO.inspect(reason) {:error, reason} end end end # Configuration: # config :app_store, webhook_handler: MyApp.WebhookHandler ``` -------------------------------- ### Custom HTTP Client Example Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/README.md Demonstrates how to implement a custom HTTP client for interacting with the App Store Server API. This is useful for advanced customization of network requests. ```elixir defmodule MyApp.HttpClient do @behaviour AppStore.HttpClient def get(url, opts \\ []) do # Custom GET request logic {:ok, "response body"} end def post(url, body, opts \\ []) do # Custom POST request logic {:ok, "response body"} end end # Usage: # AppStore.build(http_client: MyApp.HttpClient) ``` -------------------------------- ### Created Response with Token (201 Created) Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-response.md Example of a response indicating a resource was created, often returning a token or identifier in the body. ```elixir %AppStore.API.Response{ status: 201, body: "{\"testNotificationToken\":\"abc123def456\"}", headers: [ {"content-type", "application/json"}, ... ], data: nil } ``` -------------------------------- ### Add App Store Dependency to mix.exs Source: https://github.com/linjunpop/app_store/blob/main/README.md To install the package, add `app_store` to your list of dependencies in `mix.exs`. ```elixir def deps do [ {:app_store, "~> 0.4.0"} ] end ``` -------------------------------- ### Supervision Tree Setup for App Store Client Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Integrates the App Store HTTP client pool and token server into the application's supervision tree. Uses environment variables for sensitive configuration. ```elixir # lib/my_app/application.ex defmodule MyApp.Application do use Application @impl true def start(_type, _args) do children = [ # Start HTTP client pool {AppStore.HTTPClient.DefaultClient, [ pools: %{ AppStore.API.Config.sandbox_server_url() => [size: 2], AppStore.API.Config.production_server_url() => [size: 20] } ]}, # Start token server for automatic token refresh {AppStore.Token.Server, %{ issuer_id: System.fetch_env!("APPSTORE_ISSUER_ID"), bundle_id: System.fetch_env!("APPSTORE_BUNDLE_ID"), key: %{ id: System.fetch_env!("APPSTORE_KEY_ID"), pem: System.fetch_env!("APPSTORE_PRIVATE_KEY") } }}) ] Supervisor.start_link(children, strategy: :one_for_one) end end ``` -------------------------------- ### Implement Custom HTTP Client with Timeout Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Provides a custom HTTP client implementation for the App Store library, allowing for specific timeout configurations. This example uses a hypothetical `HTTPClient` module. ```elixir defmodule MyApp.AppStoreHTTPClient do @behaviour AppStore.HTTPClient @timeout_ms 10_000 @impl true def request(method, uri, body, headers) do # Your custom timeout logic case HTTPClient.request(method, uri, body, headers, timeout: @timeout_ms) do {:ok, response} -> {:ok, response} {:error, reason} -> {:error, %{code: :timeout, detail: reason}} end end end AppStore.build([ api: [ http_client: MyApp.AppStoreHTTPClient ] ]) ``` -------------------------------- ### Custom JSON Coder Implementation Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/json-coder.md Provides an example of how to implement a custom JSON coder by defining a module that adheres to the AppStore.JSON behaviour. This allows integration with alternative JSON libraries. ```elixir defmodule MyApp.CustomJSONCoder do @behaviour AppStore.JSON @impl true def encode!(data) do # Use your preferred JSON library MyJSONLibrary.to_json(data) end @impl true def decode!(json_string) do # Use your preferred JSON library MyJSONLibrary.parse(json_string) end end ``` -------------------------------- ### Custom HTTP Client Implementation Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/http-client.md Implement the `AppStore.HTTPClient` behavior to create a custom HTTP client. This example shows the structure for the `request` function, which should handle different HTTP methods, URIs, bodies, and headers, returning a response in the expected format or an error. ```elixir defmodule MyApp.CustomHTTPClient do @behaviour AppStore.HTTPClient @impl true def request(method, uri, body, headers) do # Your HTTP implementation here # Use HTTPoison, Tesla, or any HTTP library case YourHTTPLibrary.request(method, uri, body, headers) do {:ok, response} -> {:ok, %{ status: response.status, headers: response.headers, body: response.body, data: nil }} {:error, reason} -> {:error, %{ code: :custom_error, detail: reason }} end end end ``` -------------------------------- ### Custom JSON Coder Example Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/README.md Illustrates how to create a custom JSON coder for encoding and decoding data when interacting with the App Store Server API. This allows for specific JSON serialization needs. ```elixir defmodule MyApp.JsonCoder do @behaviour AppStore.JsonCoder def encode(data) do # Custom JSON encoding logic Jason.encode!(data) end def decode(json) do # Custom JSON decoding logic {:ok, Jason.decode!(json)} end end # Usage: # AppStore.build(json_coder: MyApp.JsonCoder) ``` -------------------------------- ### Configure Equal HTTP Pool Sizes for Environments Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Sets the same connection pool size for both sandbox and production environments. Ensures consistent performance characteristics across environments. Requires the default Finch client to be started. ```elixir AppStore.HTTPClient.DefaultClient.start_link([ pools: %{ AppStore.API.Config.sandbox_server_url() => [size: 10], AppStore.API.Config.production_server_url() => [size: 10] } ]) ``` -------------------------------- ### Get Sandbox App Store Server URL Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-config.md Retrieves the server URL for the App Store API sandbox environment. ```elixir iex> AppStore.API.Config.sandbox_server_url() "https://api.storekit-sandbox.itunes.apple.com" ``` -------------------------------- ### No Content Response (204 No Content) Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-response.md Example of a response indicating no content was returned, typically used for successful operations that do not require a body. ```elixir %AppStore.API.Response{ status: 204, body: "", headers: [ {"content-type", "application/json"}, ... ], data: nil } ``` -------------------------------- ### Get AppStore Library Version Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/appstore.md Retrieves the current semantic version of the app_store library. ```elixir iex> AppStore.version() "0.4.0" ``` -------------------------------- ### Implement Custom JSON Coder Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Allows integration of a custom JSON encoding and decoding library with the App Store client. This example assumes a `MyJSONLib` module. ```elixir defmodule MyApp.CustomJSONCoder do @behaviour AppStore.JSON @impl true def encode!(data) do # Use your preferred library MyJSONLib.to_json(data) end @impl true def decode!(json) do MyJSONLib.parse(json) end end AppStore.build([ api: [json_coder: MyApp.CustomJSONCoder], token: [json_coder: MyApp.CustomJSONCoder] ]) ``` -------------------------------- ### Successful Transaction History Response (200 OK) Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-response.md Example of a successful response for fetching transaction history, including status, body, and headers. ```elixir %AppStore.API.Response{ status: 200, body: "{\"bundleId\":\"com.example.app\",\"environment\":\"Sandbox\",\"hasMore\":false,\"signedTransactions\":[...]}", headers: [ {"content-type", "application/json"}, {"content-length", "1024"}, ... ], data: nil } ``` -------------------------------- ### Get Production App Store Server URL Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-config.md Retrieves the default production server URL for the App Store API. ```elixir iex> AppStore.API.Config.production_server_url() "https://api.storekit.itunes.apple.com" ``` -------------------------------- ### Configure API Server with Custom URL Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Allows specifying a custom server URL for API calls, useful for specific environments or testing setups. ```elixir AppStore.API.Config.build( server_url: "https://api.storekit-sandbox.itunes.apple.com" ) ``` -------------------------------- ### Increase Production HTTP Pool Size Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Increases the connection pool size for the production environment's HTTP client to handle higher concurrency. Requires the default Finch client to be started. ```elixir AppStore.HTTPClient.DefaultClient.start_link([ pools: %{ AppStore.API.Config.production_server_url() => [size: 30] } ]) ``` -------------------------------- ### Get Subscription Statuses and Validate Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-endpoints.md Fetches all subscription statuses for a customer and validates each signed transaction info. Requires API configuration, a JWT token, and the original transaction ID. Iterates through the 'data' array in the response body. ```elixir {:ok, response} = AppStore.API.SubscriptionStatus.get_subscription_statuses( config, token, "original-txn-123" ) body = Jason.decode!(response.body) subscriptions = body["data"] Enum.each(subscriptions, fn sub -> {:ok, jwt} = AppStore.JWSValidation.validate(sub["signedTransactionInfo"]) IO.inspect(jwt.fields) end) ``` -------------------------------- ### Initialize AppStore with Token Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-config.md Demonstrates how to build the main AppStore client, including custom token configuration. The token configuration can then be accessed via the struct. ```elixir # Via AppStore.build/1 app_store = AppStore.build([ token: [ json_coder: MyApp.CustomJSONCoder ] ]) # Access via struct token_config = app_store.token_config # => %AppStore.Token.Config{json_coder: MyApp.CustomJSONCoder} ``` -------------------------------- ### Initialize App Store Client Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/INDEX.md Builds the main client for interacting with the App Store Server API. Ensure the `api` configuration, including the server URL, is correctly set. ```elixir app_store = AppStore.build([ api: [ server_url: AppStore.API.Config.sandbox_server_url() ] ]) ``` -------------------------------- ### Get Subscription Statuses Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/INDEX.md Retrieves the subscription statuses for a given ID. ```APIDOC ## GET /inApps/v1/subscriptions/{id} ### Description Retrieves the subscription statuses for a given ID. ### Method GET ### Endpoint /inApps/v1/subscriptions/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The ID for which to retrieve subscription statuses. ### Response #### Success Response (200) - **response** (AppStore.API.Response.t) - The subscription statuses response. ### Function `AppStore.API.get_subscription_statuses/3` ``` -------------------------------- ### Get Transaction History Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/INDEX.md Retrieves the transaction history for a given ID. ```APIDOC ## GET /inApps/v1/history/{id} ### Description Retrieves the transaction history for a given ID. ### Method GET ### Endpoint /inApps/v1/history/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The ID for which to retrieve transaction history. ### Response #### Success Response (200) - **response** (AppStore.API.Response.t) - The transaction history response. ### Function `AppStore.API.get_transaction_history/3,4` ``` -------------------------------- ### Get Transaction Info Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/MANIFEST.txt Retrieves detailed information about a specific transaction. ```APIDOC ## GET /inApps/v1/transactions/{id} ### Description Retrieves detailed information about a specific transaction. ### Method GET ### Endpoint /inApps/v1/transactions/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The transaction ID. ``` -------------------------------- ### Configure App Store Environment Variables Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/INDEX.md Sets up environment variables for the App Store API client, including issuer ID, bundle ID, key ID, private key, and environment (sandbox/production). ```bash APPSTORE_ISSUER_ID="57246542-96fe-1a63-e053-0824d011072a" APPSTORE_BUNDLE_ID="com.example.app" APPSTORE_KEY_ID="2X9R4HXF34" APPSTORE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" APPSTORE_ENVIRONMENT="sandbox" ``` -------------------------------- ### get_transaction_info/3 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-main.md Get information about a single transaction using its transaction ID. ```APIDOC ## get_transaction_info/3 ### Description Get information about a single transaction. ### Method Not applicable (Function Call) ### Parameters #### Path Parameters Not applicable #### Query Parameters Not applicable #### Request Body Not applicable ### Request Example ```elixir iex> {:ok, %AppStore.API.Response{body: transaction_data, status: 200}} = AppStore.API.get_transaction_info( app_store.api_config, token, "transaction-id-456" ) ``` ### Response #### Success Response `{:ok, AppStore.API.Response.t()}` on success with transaction details. #### Response Example ```json { "body": { ... transaction details ... }, "status": 200 } ``` #### Error Response `{:error, AppStore.API.Error.t()}` on failure. ``` -------------------------------- ### AppStore.build/1 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/appstore.md Builds and configures an AppStore client struct with customizable options for API and token configurations. Defaults are applied if options are missing or invalid. ```APIDOC ## AppStore.build/1 ### Description Builds and configures an AppStore client struct with customizable options for API and token configurations. Defaults are applied if options are missing or invalid. ### Signature ```elixir @spec build(keyword) :: AppStore.t() def build(opts \ []) ``` ### Parameters #### Keyword Options (`opts`) - **`opts`** (keyword) - Optional - Default: `[]` - Configuration options. - **`opts[:api]`** (keyword) - Optional - Default: `[]` - API configuration options. - **`opts[:api][:server_url]`** (String.t()) - Optional - Default: `"https://api.storekit.itunes.apple.com"` - API server URL (production or sandbox). - **`opts[:api][:http_client]`** (module()) - Optional - Default: `AppStore.HTTPClient.DefaultClient` - HTTP client module implementing `AppStore.HTTPClient`. - **`opts[:api][:json_coder]`** (module()) - Optional - Default: `AppStore.JSON.DefaultCoder` - JSON encoder/decoder module implementing `AppStore.JSON`. - **`opts[:token]`** (keyword) - Optional - Default: `[]` - Token configuration options. - **`opts[:token][:json_coder]`** (module()) - Optional - Default: `AppStore.JSON.DefaultCoder` - JSON encoder/decoder for token operations. ### Returns An `AppStore.t()` struct with configured API and token clients. ### Examples Default configuration with production API: ```elixir iex> app_store = AppStore.build() %AppStore{ api_config: %AppStore.API.Config{ http_client: AppStore.HTTPClient.DefaultClient, json_coder: AppStore.JSON.DefaultCoder, server_url: "https://api.storekit.itunes.apple.com" }, token_config: %AppStore.Token.Config{ json_coder: AppStore.JSON.DefaultCoder } } ``` Custom configuration with sandbox and custom HTTP client: ```elixir app_store = AppStore.build([ api: [ server_url: AppStore.API.Config.sandbox_server_url(), http_client: MyApp.CustomHTTPClient, json_coder: MyApp.CustomJSONCoder ], token: [ json_coder: MyApp.CustomJSONCoder ] ]) ``` ``` -------------------------------- ### Get Subscription Status Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/MANIFEST.txt Retrieves the status of subscriptions for a given application ID. ```APIDOC ## GET /inApps/v1/subscriptions/{id} ### Description Retrieves the status of subscriptions for a given application ID. ### Method GET ### Endpoint /inApps/v1/subscriptions/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The application ID. ``` -------------------------------- ### Build AppStore Client with Default Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/appstore.md Builds an AppStore client struct using default configurations, pointing to the production API server. ```elixir iex> app_store = AppStore.build() %AppStore{ api_config: %AppStore.API.Config{ http_client: AppStore.HTTPClient.DefaultClient, json_coder: AppStore.JSON.DefaultCoder, server_url: "https://api.storekit.itunes.apple.com" }, token_config: %AppStore.Token.Config{ json_coder: AppStore.JSON.DefaultCoder } } ``` -------------------------------- ### Get Transaction History Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/MANIFEST.txt Retrieves the transaction history for a given application ID. ```APIDOC ## GET /inApps/v1/history/{id} ### Description Retrieves the transaction history for a given application ID. ### Method GET ### Endpoint /inApps/v1/history/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The application ID. ``` -------------------------------- ### Build App Store Client with Environment-Specific URL Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Dynamically builds the App Store client, selecting the server URL based on the APPSTORE_ENVIRONMENT variable. Defaults to production if not specified. ```elixir defmodule MyApp.AppStore do def client do environment = System.get_env("APPSTORE_ENVIRONMENT", "production") server_url = case environment do "sandbox" -> AppStore.API.Config.sandbox_server_url() _ -> AppStore.API.Config.production_server_url() end AppStore.build([ api: [server_url: server_url] ]) end end ``` -------------------------------- ### Get Transaction Info Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/endpoints.md Retrieves information about a single transaction using its transaction ID. ```elixir AppStore.API.get_transaction_info(api_config, token, transaction_id) ``` ```json { "signedTransactionInfo": "eyJhbGc..." } ``` -------------------------------- ### GET /inApps/v1/transactions/{transactionId} Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-endpoints.md Fetches information about a single transaction using its transaction ID. ```APIDOC ## GET /inApps/v1/transactions/{transactionId} ### Description Fetches information about a single transaction using its transaction ID. ### Method GET ### Endpoint /inApps/v1/transactions/{transactionId} ### Parameters #### Path Parameters - **transactionId** (string) - Required - The transaction ID to fetch. #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **signedTransactionInfo** (string) - A signed JSON Web Signature (JWS) containing the transaction information. #### Response Example ```json { "signedTransactionInfo": "eyJhbGc..." } ``` ``` -------------------------------- ### Configure App Store Client for Development vs. Production Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Selects the App Store API server URL based on the application's environment setting. Uses the production URL for :prod and sandbox for others. ```elixir defmodule MyApp.Config do def app_store_config do if Application.get_env(:my_app, :environment) == :prod do AppStore.build([ api: [ server_url: AppStore.API.Config.production_server_url() ] ]) else AppStore.build([ api: [ server_url: AppStore.API.Config.sandbox_server_url() ] ]) end end end ``` -------------------------------- ### Using a Custom HTTP Client Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/http-client.md Configure the AppStore client to use your custom HTTP client by passing its module name to the `http_client` option during the build process. ```elixir app_store = AppStore.build( api: [ http_client: MyApp.CustomHTTPClient ] ) ``` -------------------------------- ### Get Test Notification Status Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/MANIFEST.txt Retrieves the status of a previously sent test notification. ```APIDOC ## GET /inApps/v1/notifications/test/{token} ### Description Retrieves the status of a previously sent test notification. ### Method GET ### Endpoint /inApps/v1/notifications/test/{token} ### Parameters #### Path Parameters - **token** (string) - Required - The token of the test notification. ``` -------------------------------- ### API Reference Overview Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/README.md This library provides comprehensive documentation for the App Store Server API, including endpoint details, request/response schemas, error handling, and configuration. ```APIDOC ## API Reference Documentation ### Description This library offers a detailed technical reference for interacting with the App Store Server API. It covers all aspects of the API, including endpoint specifications, data structures, error management, and client configuration. ### Key Documentation Areas: - **Endpoints**: Complete mapping of HTTP endpoints with request and response schemas, including HTTP methods, paths, query parameters, headers, status codes, and error responses for all 6 App Store Server API endpoints. - **Types**: Detailed definitions for all exported types, type aliases, and structure definitions, including field descriptions and type relationships. - **Errors**: Comprehensive documentation on error codes, conditions, causes, handling patterns, retry strategies, and common error scenarios. - **Configuration**: Information on setup, configuration options, environment variables, HTTP client pooling, JSON coder selection, and production vs. sandbox setup. ### Modules and Tasks: - **Initialization**: `AppStore.build/1` (appstore.md) - **API Operations**: `AppStore.API` facade (api-main.md) - **Configuration**: `AppStore.API.Config` (api-config.md) - **Response Handling**: `AppStore.API.Response` (api-response.md) - **Endpoint Implementations**: `AppStore.API.*` (api-endpoints.md) - **Token Generation**: `AppStore.Token` (token.md) - **Token Server**: `AppStore.Token.Server` (token-server.md) - **Token Configuration**: `AppStore.Token.Config` (token-config.md) - **HTTP Client**: `AppStore.HTTPClient` (http-client.md) - **JSON Handling**: `AppStore.JSON` (json-coder.md) - **JWS Validation**: `AppStore.JWSValidation` (jws-validation.md) ### Usage: Refer to the respective markdown files for detailed information on each module and task. The documentation is organized by task and by module for easy navigation. ``` -------------------------------- ### get_subscription_statuses/3 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-main.md Get the statuses for all of a customer's subscriptions, identified by their original transaction ID. ```APIDOC ## get_subscription_statuses/3 ### Description Get the statuses for all of a customer's subscriptions. ### Method Not applicable (Function Call) ### Parameters #### Path Parameters Not applicable #### Query Parameters Not applicable #### Request Body Not applicable ### Request Example ```elixir iex> {:ok, %AppStore.API.Response{body: statuses, status: 200}} = AppStore.API.get_subscription_statuses( app_store.api_config, token, "original-transaction-id-123" ) ``` ### Response #### Success Response `{:ok, AppStore.API.Response.t()}` on success with subscription statuses. #### Response Example ```json { "body": { ... subscription statuses ... }, "status": 200 } ``` #### Error Response `{:error, AppStore.API.Error.t()}` on failure. ``` -------------------------------- ### GET /inApps/v1/subscriptions/{originalTransactionId} Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-endpoints.md Fetches all subscription statuses for a customer, identified by their original transaction ID. ```APIDOC ## GET /inApps/v1/subscriptions/{originalTransactionId} ### Description Fetches all subscription statuses for a customer, identified by their original transaction ID. ### Method GET ### Endpoint /inApps/v1/subscriptions/{originalTransactionId} ### Parameters #### Path Parameters - **originalTransactionId** (string) - Required - The original transaction ID to fetch subscription statuses for. #### Query Parameters None #### Request Body None ### Response #### Success Response (200) - **data** (array) - An array of subscription status objects. - **status** (integer) - The status of the subscription. - **subscriptionGroupIdentifier** (string) - The identifier for the subscription group. - **originalTransactionId** (string) - The original transaction ID. - **signedRenewalInfo** (string) - Signed renewal information. - **signedTransactionInfo** (string) - Signed transaction information. - **environment** (string) - The environment (e.g., Sandbox, Production). #### Response Example ```json { "data": [ { "status": 1, "subscriptionGroupIdentifier": "group-id", "originalTransactionId": "orig-txn", "signedRenewalInfo": "eyJhbGc...", "signedTransactionInfo": "eyJhbGc..." } ], "environment": "Sandbox" } ``` ``` -------------------------------- ### Token Generation with Error Handling Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token.md Demonstrates how to handle potential errors during token generation using a case statement. Logs errors and returns an error tuple if generation fails. ```elixir case AppStore.Token.generate_token(issuer_id, bundle_id, key) do {:ok, token, _claims} -> # Use token for API requests {:ok, token} {:error, reason} -> Logger.error("Token generation failed: #{inspect(reason)}") {:error, reason} end ``` -------------------------------- ### Build AppStore Client with Custom Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/appstore.md Builds an AppStore client struct with custom configurations, including a sandbox server URL and custom HTTP client and JSON coder modules. ```elixir app_store = AppStore.build([ api: [ server_url: AppStore.API.Config.sandbox_server_url(), http_client: MyApp.CustomHTTPClient, json_coder: MyApp.CustomJSONCoder ], token: [ json_coder: MyApp.CustomJSONCoder ] ]) ``` -------------------------------- ### Get Transaction Info Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-main.md Fetch details for a single transaction. Requires API configuration, a JWT token, and the specific transaction ID. ```elixir iex> {:ok, %AppStore.API.Response{body: transaction_data, status: 200}} = AppStore.API.get_transaction_info( app_store.api_config, token, "transaction-id-456" ) ``` -------------------------------- ### AppStore.API.Config.build/1 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-config.md Builds an API configuration struct with customizable options. It allows setting the server URL, HTTP client, and JSON coder, with sensible defaults provided. ```APIDOC ## `AppStore.API.Config.build/1` ### Description Build an API configuration struct with defaults. ### Signature ```elixir @spec build(keyword) :: AppStore.API.Config.t() def build(opts \ []) ``` ### Parameters #### Keyword Options (`opts`) - **`server_url`** (`String.t()`) - Optional - Defaults to `production_server_url()` - The base URL for API requests (production or sandbox). - **`http_client`** (`module()`) - Optional - Defaults to `AppStore.HTTPClient.DefaultClient` - Module implementing `AppStore.HTTPClient` behaviour. - **`json_coder`** (`module()`) - Optional - Defaults to `AppStore.JSON.DefaultCoder` - Module implementing `AppStore.JSON` behaviour. ### Returns An `AppStore.API.Config.t()` struct. ### Examples Default configuration: ```elixir iex> AppStore.API.Config.build() %AppStore.API.Config{ server_url: "https://api.storekit.itunes.apple.com", http_client: AppStore.HTTPClient.DefaultClient, json_coder: AppStore.JSON.DefaultCoder } ``` Custom sandbox configuration: ```elixir AppStore.API.Config.build([ server_url: AppStore.API.Config.sandbox_server_url(), http_client: MyCustomHTTPClient ]) ``` ``` -------------------------------- ### Configure API with Default HTTP Client Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Uses the default Finch-based HTTP client for API requests. Ensure Finch is configured if needed. ```elixir AppStore.API.Config.build() ``` -------------------------------- ### Using Custom JSON Coder in AppStore Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/json-coder.md Demonstrates how to configure the AppStore to use a custom JSON coder module. This is done during the build process for specific components like API or token handling. ```elixir app_store = AppStore.build( api: [ json_coder: MyApp.CustomJSONCoder ], token: [ json_coder: MyApp.CustomJSONCoder ] ) ``` -------------------------------- ### Get Subscription Statuses Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/endpoints.md Retrieves subscription statuses for a customer using their original transaction ID. Returns an array of subscription status objects. ```elixir AppStore.API.get_subscription_statuses(api_config, token, original_transaction_id) ``` ```json { "data": [ { "status": 1, "subscriptionGroupIdentifier": "group_id", "originalTransactionId": "orig_txn_id", "signedRenewalInfo": "eyJhbGc...", "signedTransactionInfo": "eyJhbGc..." } ], "environment": "Sandbox" } ``` -------------------------------- ### Build Default App Store API Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-config.md Builds an AppStore.API.Config struct with default values for server URL, HTTP client, and JSON coder. ```elixir iex> AppStore.API.Config.build() %AppStore.API.Config{ server_url: "https://api.storekit.itunes.apple.com", http_client: AppStore.HTTPClient.DefaultClient, json_coder: AppStore.JSON.DefaultCoder } ``` -------------------------------- ### Get Transaction History Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/endpoints.md Retrieves a customer's transaction history using their original transaction ID. Supports pagination with a revision token. ```elixir AppStore.API.get_transaction_history(api_config, token, original_transaction_id, revision \ nil) ``` ```json { "bundleId": "com.example.app", "environment": "Sandbox", "hasMore": false, "revision": null, "signedTransactions": [ "eyJhbGc..." ] } ``` -------------------------------- ### Build Default Token Configuration Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-config.md Use this function to create a default token configuration struct. It defaults to using AppStore.JSON.DefaultCoder for JSON encoding/decoding. ```elixir iex> AppStore.Token.Config.build() %AppStore.Token.Config{ json_coder: AppStore.JSON.DefaultCoder } ``` -------------------------------- ### Get Subscription Statuses Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-main.md Retrieve the statuses for all of a customer's subscriptions. Requires API configuration, a JWT token, and the original transaction ID. ```elixir iex> {:ok, %AppStore.API.Response{body: statuses, status: 200}} = AppStore.API.get_subscription_statuses( app_store.api_config, token, "original-transaction-id-123" ) ``` -------------------------------- ### Handle Multiple JWS Validation Errors Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/errors.md This example demonstrates how to process a list of JWS validation results, filtering and counting valid and invalid entries. ```elixir signed_transactions = [txn1, txn2, txn3] results = AppStore.JWSValidation.validate(signed_transactions) valid = Enum.filter(results, fn {:ok, _jwt} -> true {:error, _} -> false end) invalid = Enum.filter(results, fn {:error, _} -> true {:ok, _} -> false end) IO.puts("Valid: #{Enum.count(valid)}, Invalid: #{Enum.count(invalid)}") ``` -------------------------------- ### GET /inApps/v1/history/{originalTransactionId} Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-endpoints.md Fetches the transaction history for a customer's original transaction ID. Supports pagination to retrieve subsequent pages of history. ```APIDOC ## GET /inApps/v1/history/{originalTransactionId} ### Description Fetches the transaction history for a customer's original transaction ID. Supports pagination to retrieve subsequent pages of history. ### Method GET ### Endpoint /inApps/v1/history/{originalTransactionId} ### Parameters #### Path Parameters - **originalTransactionId** (string) - Required - The original transaction ID to fetch history for. #### Query Parameters - **revision** (string | nil) - Optional - Pagination token for fetching the next page. Defaults to nil. #### Request Body None ### Response #### Success Response (200) - **bundleId** (string) - The bundle ID of the app. - **environment** (string) - The environment (e.g., Sandbox, Production). - **hasMore** (boolean) - Indicates if there are more transactions to fetch. - **signedTransactions** (array) - An array of signed transaction objects. #### Response Example ```json { "bundleId": "com.example.app", "environment": "Sandbox", "hasMore": false, "signedTransactions": [ // ... transaction objects ... ] } ``` ``` -------------------------------- ### Implement Exponential Backoff for Network Timeouts Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/errors.md Suggests implementing exponential backoff for retries to handle network timeouts. Also recommends checking network connectivity and configuring request timeouts. ```elixir # Implement exponential backoff retry # Check network connectivity # Consider implementing request timeout configuration ``` -------------------------------- ### Generate and Use a Fresh JWT Token Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/configuration.md Demonstrates how to manually generate a fresh JWT token for API requests and then use it to call the App Store API. This is an alternative to the automatic token caching mechanism. ```elixir # Generate fresh token for each request {:ok, token, _} = AppStore.Token.generate_token(issuer_id, bundle_id, key) # Use token for API call AppStore.API.get_transaction_info(config, token, txn_id) ``` -------------------------------- ### Validate Signed Transactions Source: https://github.com/linjunpop/app_store/blob/main/README.md Validates a list of signed transactions using JWS validation. This example checks the bundle ID, environment, and signed date for each transaction. ```elixir iex> [ {:ok, %JOSE.JWT{fields: %{"bundleId" => "com.example", "environment" => "Sandbox", "signedDate" => 1_672_956_154_000}}}, {:ok, %JOSE.JWT{fields: %{"bundleId" => "com.example2", "environment" => "Sandbox", "signedDate" => 1_672_956_154_000}}} ] = AppStore.JWSValidation.validate(signed_transactions) ``` -------------------------------- ### AppStore.Token.Config.build/1 Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/token-config.md Builds a token configuration struct with default settings or custom options. It allows specifying a JSON coder module for JWT token generation. ```APIDOC ## AppStore.Token.Config.build/1 ### Description Build a token configuration struct with defaults. ### Signature ```elixir @spec build(keyword()) :: AppStore.Token.Config.t() def build(opts \ []) ``` ### Parameters #### Keyword Options - **opts** (keyword()) - Optional - Defaults to `[]`. Configuration options. - **opts[:json_coder]** (module()) - Optional - Defaults to `AppStore.JSON.DefaultCoder`. The JSON encoder/decoder module to use. ### Returns An `AppStore.Token.Config.t()` struct with the specified or default JSON coder. ### Examples Default configuration with Jason-based JSON coder: ```elixir iex> AppStore.Token.Config.build() %AppStore.Token.Config{ json_coder: AppStore.JSON.DefaultCoder } ``` Custom JSON coder: ```elixir config = AppStore.Token.Config.build([ json_coder: MyApp.CustomJSONCoder ]) %AppStore.Token.Config{ json_coder: MyApp.CustomJSONCoder } ``` ``` -------------------------------- ### API Configuration for JSON Coder Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/json-coder.md Shows how to configure the JSON coder for API requests and responses using AppStore.API.Config.build. This ensures consistent JSON handling for API interactions. ```elixir AppStore.API.Config.build([ json_coder: MyJSONCoder ]) ``` -------------------------------- ### Handling API Responses with Case Statement Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-response.md Demonstrates how to handle different API response statuses using a case statement, including success, unauthorized, and error scenarios. ```elixir case AppStore.API.get_transaction_info(api_config, token, transaction_id) do {:ok, %AppStore.API.Response{status: 200, body: body}} -> decoded = Jason.decode!(body) # Process transaction information {:ok, %AppStore.API.Response{status: 401}} -> # Unauthorized - token may be invalid {:error, error} -> # Handle request failure IO.inspect(error) end ``` -------------------------------- ### Get Test Notification Status Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-endpoints.md Retrieves the delivery status of a test notification using its unique token. This is useful for verifying if a webhook has successfully received a test notification. ```APIDOC ## GET get_test_notification_status/3 ### Description Get the delivery status of a test notification. ### Method GET ### Endpoint `/get_test_notification_status/{test_notification_token}` ### Parameters #### Path Parameters - **test_notification_token** (string) - Yes - Token from send_test_notification #### Query Parameters - **api_config** (AppStore.API.Config.t()) - Yes - API configuration - **token** (string) - Yes - JWT authentication token ### Response #### Success Response (200) - **signedPayload** (string) - JWS-signed test notification payload - **sendAttempts** (array) - Array of delivery attempt results - **sendAttempts[].attemptDate** (integer) - Unix timestamp in milliseconds - **sendAttempts[].sendAttemptResult** (string) - "SUCCESS", "FAILED", etc. #### Response Example ```json { "signedPayload": "eyJhbGc...", "sendAttempts": [ { "attemptDate": 1672956154000, "sendAttemptResult": "SUCCESS" } ] } ``` ``` -------------------------------- ### Get Test Notification Status Source: https://github.com/linjunpop/app_store/blob/main/_autodocs/api-reference/api-main.md Retrieve the status of a previously sent test notification. You will need your API configuration, JWT token, and the test notification token obtained from send_test_notification. ```elixir iex> {:ok, %AppStore.API.Response{body: status_data, status: 200}} = AppStore.API.get_test_notification_status( app_store.api_config, token, "test-notification-token-xyz" ) ``` -------------------------------- ### Add Default HTTP Client to Application Supervision Tree Source: https://github.com/linjunpop/app_store/blob/main/README.md Add the default HTTP client `AppStore.HTTPClient.DefaultClient` to your application's supervision tree in `lib/your_app/application.ex`. ```elixir # lib/your_app/application.ex def start(_type, _args) do children = [ ... {AppStore.HTTPClient.DefaultClient, []} ] ... ```