# `PhoenixKitEcommerce.Shopify.AdminClient`
[🔗](https://github.com/BeamLabEU/phoenix_kit_ecommerce/blob/0.5.10/lib/phoenix_kit_ecommerce/shopify/admin_client.ex#L1)

Thin REST client for the Shopify Admin API's `products.json` endpoint.

Resolves the shop domain and access token from the `PhoenixKit.Integrations`
connection identified by `integration_uuid` — never from application env
(see `PhoenixKitEcommerce.Shopify.Provider` for why). Authenticates with
the `X-Shopify-Access-Token` header, NOT `Authorization: Bearer` — that is
what the REST Admin API expects for a Custom App's static token.

# `fetch_collection_product_ids`

```elixir
@spec fetch_collection_product_ids(String.t() | integer(), keyword()) ::
  {:ok, [term()]} | {:error, :invalid_collection_id | term()}
```

Fetches the product ids of `collection_id`, in the order Shopify
returns them — Shopify applies the collection's own sort order to this
endpoint, so no client-side sorting happens here; `CollectionSync` reads
this order directly as `item.position` within the category. Paginated
like `fetch_products/2`.

## Options

Same as `fetch_collections/1`.

# `fetch_collections`

```elixir
@spec fetch_collections(keyword()) :: {:ok, [map()]} | {:error, term()}
```

Fetches every collection from the connected store — `custom_collections`
and `smart_collections` concatenated, each paginated like
`fetch_products/2`. Each returned collection carries `"kind"` (`"custom"`
or `"smart"`, which endpoint it came from) and `"position"` — a running
index across BOTH lists, in API order (custom first, then smart) — this
is the order `CollectionSync` writes as `category.position`.

## Options

  * `:integration_uuid` — required; resolves the shop domain/access
    token the same way `fetch_products/2` does.
  * `:req_options` — as `fetch_products/2`.

# `fetch_product`

```elixir
@spec fetch_product(String.t(), String.t() | integer(), keyword()) ::
  {:ok, map()} | {:error, :invalid_product_id | term()}
```

Fetches a single product from the Shopify store connected via
`integration_uuid`, given its Shopify `product_id` — a single,
unpaginated `GET /admin/api/<version>/products/{id}.json` request. This
is the point lookup a per-product panel needs instead of pulling the
whole catalog through `fetch_products/2` to check one item.

Reuses `resolve_client/2` (credential resolution/auth), the same
`@product_fields` field set, and the same 429/401/403 handling as
`fetch_products/2` — but is NOT built on top of `fetch_all/5`: that
helper is shaped around a paginated LIST response (`Link: rel="next"`,
an accumulator), while this endpoint returns exactly one `"product"`
map and never paginates. The two diverge on the 404 case too — see
below — so a shared status-handling core was not worth the added
indirection for what is otherwise a short, linear match.

A 404 here means the given `product_id` doesn't exist in this store
and maps to `:not_found` — deliberately NOT `:shop_not_found`
(`fetch_products/2`'s 404, meaning the *shop* domain itself doesn't
resolve): the shop answered fine, it just has no such product.

## Options

  * `:req_options` — as `fetch_products/2`.

# `fetch_products`

```elixir
@spec fetch_products(String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}
```

Fetches every product from the Shopify store connected via
`integration_uuid`, following `Link: rel="next"` pagination.

## Options

  * `:req_options` — keyword list merged into `Req.new/1` (e.g. `plug:`
    to stub the transport in tests).

# `fetch_shop`

```elixir
@spec fetch_shop(String.t(), keyword()) :: {:ok, map()} | {:error, term()}
```

Fetches the connected store's own `shop.json` — its name, domain, and
crucially its `currency`. Per the per-domain-currency design (§7.5), a
Shopify sync must be able to check the store's own currency against
the base currency and refuse price updates on a mismatch rather than
silently reimporting numbers that no longer mean what they used to.

A single, unpaginated request — unlike `fetch_products/2` and its
siblings, there is only ever one shop.

## Options

  * `:req_options` — as `fetch_products/2`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
