basic printify integration

This commit is contained in:
James Greenwood
2025-11-30 11:05:49 +00:00
parent dab1ffc91f
commit 39e9744eb7
16 changed files with 1181 additions and 199 deletions

View File

@@ -0,0 +1,124 @@
defmodule Simpleshop.Printify.Client do
require Logger
alias Simpleshop.Printify.TokenStore
@base_url "https://api.printify.com/v1"
@user_agent "SimpleshopMVP"
def get_products do
config = Application.get_env(:simpleshop, :printify)
shop_id = Keyword.fetch!(config, :shop_id)
url = "#{@base_url}/shops/#{shop_id}/products.json"
with_token(fn token ->
case Req.get(url, headers: headers(token)) do
{:ok, %{status: 200, body: response}} ->
products = Map.get(response, "data") || response
Logger.info("Successfully fetched #{length(products)} products")
{:ok, products}
{:ok, %{status: status, body: body}} ->
Logger.error("Failed to fetch products: #{status} - #{inspect(body)}")
{:error, "Failed to fetch products: #{status}"}
{:error, error} ->
Logger.error("HTTP error fetching products: #{inspect(error)}")
{:error, "Network error: #{inspect(error)}"}
end
end)
end
def get_product(product_id) do
config = Application.get_env(:simpleshop, :printify)
shop_id = Keyword.fetch!(config, :shop_id)
url = "#{@base_url}/shops/#{shop_id}/products/#{product_id}.json"
with_token(fn token ->
case Req.get(url, headers: headers(token)) do
{:ok, %{status: 200, body: product}} ->
Logger.info("Successfully fetched product #{product_id}")
{:ok, product}
{:ok, %{status: status, body: body}} ->
Logger.error("Failed to fetch product: #{status} - #{inspect(body)}")
{:error, "Failed to fetch product: #{status}"}
{:error, error} ->
Logger.error("HTTP error fetching product: #{inspect(error)}")
{:error, "Network error: #{inspect(error)}"}
end
end)
end
def create_order(product_id, variant_id, quantity \\ 1) do
config = Application.get_env(:simpleshop, :printify)
shop_id = Keyword.fetch!(config, :shop_id)
url = "#{@base_url}/shops/#{shop_id}/orders.json"
order_data = %{
external_id: "simpleshop-#{System.unique_integer([:positive])}",
line_items: [
%{
product_id: product_id,
variant_id: variant_id,
quantity: quantity
}
],
shipping_method: 1,
address_to: test_shipping_address()
}
with_token(fn token ->
case Req.post(url, headers: headers(token), json: order_data) do
{:ok, %{status: status, body: response}} when status in 200..299 ->
Logger.info("Successfully created order: #{inspect(response)}")
{:ok, response}
{:ok, %{status: status, body: body}} ->
Logger.error("Failed to create order: #{status} - #{inspect(body)}")
{:error, "Failed to create order: #{status} - #{inspect(body)}"}
{:error, error} ->
Logger.error("HTTP error creating order: #{inspect(error)}")
{:error, "Network error: #{inspect(error)}"}
end
end)
end
defp with_token(fun) do
case TokenStore.get_access_token() do
{:ok, token} ->
fun.(token)
{:error, :not_configured} ->
Logger.error("Printify Personal Access Token not configured in config/dev.exs")
{:error, :not_configured}
{:error, reason} ->
Logger.error("Failed to get access token: #{inspect(reason)}")
{:error, :authentication_required}
end
end
defp headers(token) do
[
{"Authorization", "Bearer #{token}"},
{"User-Agent", @user_agent},
{"Content-Type", "application/json;charset=utf-8"}
]
end
defp test_shipping_address do
%{
first_name: "John",
last_name: "Doe",
email: "test@example.com",
phone: "555-0123",
country: "US",
region: "CA",
address1: "123 Test St",
city: "San Francisco",
zip: "94102"
}
end
end

View File

@@ -0,0 +1,92 @@
defmodule Simpleshop.Printify.OAuth do
require Logger
alias Simpleshop.Printify.TokenStore
@printify_auth_url "https://printify.com/app/authorize"
@printify_token_url "https://api.printify.com/v1/app/oauth/tokens"
@printify_refresh_url "https://api.printify.com/v1/app/oauth/tokens/refresh"
def authorization_url do
config = Application.get_env(:simpleshop, :printify)
app_id = Keyword.fetch!(config, :app_id)
redirect_uri = Keyword.fetch!(config, :redirect_uri)
query =
URI.encode_query(%{
app_id: app_id,
accept_url: redirect_uri,
decline_url: redirect_uri <> "?error=access_denied"
})
"#{@printify_auth_url}?#{query}"
end
def exchange_code(code) do
config = Application.get_env(:simpleshop, :printify)
app_id = Keyword.fetch!(config, :app_id)
body = %{
app_id: app_id,
code: code
}
case Req.post(@printify_token_url, json: body) do
{:ok, %{status: 200, body: response}} ->
Logger.info("Successfully exchanged OAuth code for tokens")
store_token_response(response)
{:ok, %{status: status, body: body}} ->
Logger.error("Failed to exchange OAuth code: #{status} - #{inspect(body)}")
{:error, "Failed to get access token: #{inspect(body)}"}
{:error, error} ->
Logger.error("HTTP error during token exchange: #{inspect(error)}")
{:error, "Network error: #{inspect(error)}"}
end
end
def refresh_access_token do
case TokenStore.get_refresh_token() do
{:ok, refresh_token} ->
config = Application.get_env(:simpleshop, :printify)
app_id = Keyword.fetch!(config, :app_id)
body = %{
app_id: app_id,
refresh_token: refresh_token
}
case Req.post(@printify_refresh_url, json: body) do
{:ok, %{status: 200, body: response}} ->
Logger.info("Successfully refreshed access token")
store_token_response(response)
{:ok, %{status: status, body: body}} ->
Logger.error("Failed to refresh access token: #{status} - #{inspect(body)}")
{:error, "Failed to refresh token: #{inspect(body)}"}
{:error, error} ->
Logger.error("HTTP error during token refresh: #{inspect(error)}")
{:error, "Network error: #{inspect(error)}"}
end
{:error, :not_found} ->
Logger.error("No refresh token found")
{:error, :no_refresh_token}
end
end
defp store_token_response(response) do
access_token = Map.get(response, "access_token") || Map.get(response, :access_token)
refresh_token = Map.get(response, "refresh_token") || Map.get(response, :refresh_token)
expires_in = Map.get(response, "expires_in") || Map.get(response, :expires_in) || 21_600
if access_token && refresh_token do
TokenStore.store_tokens(access_token, refresh_token, expires_in)
{:ok, access_token}
else
Logger.error("Missing tokens in response: #{inspect(response)}")
{:error, "Invalid token response"}
end
end
end

View File

@@ -0,0 +1,24 @@
defmodule Simpleshop.Printify.TokenStore do
@moduledoc """
Simple module to retrieve the Printify Personal Access Token from config.
No GenServer or ETS needed - just reads from application config.
"""
def get_access_token do
config = Application.get_env(:simpleshop, :printify)
case Keyword.get(config, :access_token) do
nil -> {:error, :not_configured}
"" -> {:error, :not_configured}
"your_personal_access_token_here" -> {:error, :not_configured}
token -> {:ok, token}
end
end
def has_tokens? do
case get_access_token() do
{:ok, _} -> true
_ -> false
end
end
end