/**
* Admin API operations (store management).
* Uses the Shopify Admin API via GraphQL.
*/
import type { Order, DiscountOffer } from '../types.js';
import type { ShopifyClient } from './client.js';
import type {
ShopifyOrder,
ShopifyDiscountCodeNode,
} from './types.js';
import { toUCPOrder, toUCPDiscount } from './types.js';
// ─── GraphQL Queries ───
const ORDER_FIELDS_FRAGMENT = /* GraphQL */ `
fragment OrderFields on Order {
id
name
displayFinancialStatus
displayFulfillmentStatus
currencyCode
createdAt
updatedAt
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
subtotalPriceSet {
shopMoney {
amount
currencyCode
}
}
totalTaxSet {
shopMoney {
amount
currencyCode
}
}
totalShippingPriceSet {
shopMoney {
amount
currencyCode
}
}
totalDiscountsSet {
shopMoney {
amount
currencyCode
}
}
lineItems(first: 50) {
edges {
node {
id
title
quantity
sku
variant {
id
product {
id
}
image {
url
altText
width
height
}
price
}
originalTotalSet {
shopMoney {
amount
currencyCode
}
}
discountedTotalSet {
shopMoney {
amount
currencyCode
}
}
}
}
}
fulfillments {
status
trackingInfo {
number
url
company
}
estimatedDeliveryAt
}
}
`;
const GET_ORDER_QUERY = /* GraphQL */ `
${ORDER_FIELDS_FRAGMENT}
query GetOrder($id: ID!) {
order(id: $id) {
...OrderFields
}
}
`;
const GET_ORDERS_QUERY = /* GraphQL */ `
${ORDER_FIELDS_FRAGMENT}
query GetOrders($first: Int!) {
orders(first: $first, sortKey: CREATED_AT, reverse: true) {
edges {
node {
...OrderFields
}
}
}
}
`;
const GET_INVENTORY_LEVEL_QUERY = /* GraphQL */ `
query GetInventoryLevel($id: ID!) {
productVariant(id: $id) {
inventoryQuantity
inventoryItem {
id
}
}
}
`;
const GET_DISCOUNT_CODES_QUERY = /* GraphQL */ `
query GetDiscountCodes($first: Int!) {
codeDiscountNodes(first: $first, query: "status:active") {
edges {
node {
id
codeDiscount {
... on DiscountCodeBasic {
title
summary
codes(first: 1) {
edges {
node {
code
}
}
}
customerGets {
value {
... on DiscountPercentage {
percentage
}
... on DiscountAmount {
amount {
amount
currencyCode
}
}
}
}
minimumRequirement {
... on DiscountMinimumSubtotal {
greaterThanOrEqualToSubtotal {
amount
currencyCode
}
}
}
}
}
}
}
}
}
`;
const APPLY_DISCOUNT_MUTATION = /* GraphQL */ `
mutation ApplyDiscount($checkoutId: ID!, $discountCode: String!) {
checkoutDiscountCodeApplyV2(
checkoutId: $checkoutId
discountCode: $discountCode
) {
checkout {
id
}
checkoutUserErrors {
field
message
code
}
}
}
`;
// ─── Response Types ───
interface GetOrderData {
order: ShopifyOrder | null;
}
interface GetOrdersData {
orders: {
edges: Array<{ node: ShopifyOrder }>;
};
}
interface GetInventoryLevelData {
productVariant: {
inventoryQuantity: number;
inventoryItem: { id: string };
} | null;
}
interface GetDiscountCodesData {
codeDiscountNodes: {
edges: Array<{ node: ShopifyDiscountCodeNode }>;
};
}
interface ApplyDiscountData {
checkoutDiscountCodeApplyV2: {
checkout: { id: string } | null;
checkoutUserErrors: Array<{
field: string[];
message: string;
code: string;
}>;
};
}
// ─── AdminAPI Class ───
export class AdminAPI {
private readonly client: ShopifyClient;
constructor(client: ShopifyClient) {
this.client = client;
}
/**
* Fetch a single order by its Shopify GID.
*/
async getOrder(orderId: string): Promise<Order | null> {
const response = await this.client.adminQuery<GetOrderData>(
GET_ORDER_QUERY,
{ id: orderId },
);
if (response.errors?.length) {
throw new Error(
`Admin getOrder failed: ${response.errors.map((e) => e.message).join(', ')}`,
);
}
if (!response.data?.order) {
return null;
}
return toUCPOrder(response.data.order);
}
/**
* Fetch recent orders, sorted by creation date descending.
*/
async getOrders(first: number = 10): Promise<Order[]> {
const response = await this.client.adminQuery<GetOrdersData>(
GET_ORDERS_QUERY,
{ first },
);
if (response.errors?.length) {
throw new Error(
`Admin getOrders failed: ${response.errors.map((e) => e.message).join(', ')}`,
);
}
if (!response.data) {
return [];
}
return response.data.orders.edges.map(({ node }) => toUCPOrder(node));
}
/**
* Get the current inventory quantity for a product variant.
*/
async getInventoryLevel(variantId: string): Promise<number> {
const response = await this.client.adminQuery<GetInventoryLevelData>(
GET_INVENTORY_LEVEL_QUERY,
{ id: variantId },
);
if (response.errors?.length) {
throw new Error(
`Admin getInventoryLevel failed: ${response.errors.map((e) => e.message).join(', ')}`,
);
}
if (!response.data?.productVariant) {
throw new Error(`Variant not found: ${variantId}`);
}
return response.data.productVariant.inventoryQuantity;
}
/**
* Fetch all active discount codes from the store.
*/
async getDiscountCodes(): Promise<DiscountOffer[]> {
const response = await this.client.adminQuery<GetDiscountCodesData>(
GET_DISCOUNT_CODES_QUERY,
{ first: 50 },
);
if (response.errors?.length) {
throw new Error(
`Admin getDiscountCodes failed: ${response.errors.map((e) => e.message).join(', ')}`,
);
}
if (!response.data) {
return [];
}
return response.data.codeDiscountNodes.edges
.filter(({ node }) => node.codeDiscount && 'codes' in node.codeDiscount)
.map(({ node }) => toUCPDiscount(node));
}
/**
* Apply a discount code to a checkout.
* Returns true if successfully applied, false if the code was invalid.
*/
async applyDiscount(
checkoutId: string,
code: string,
): Promise<boolean> {
const response = await this.client.adminQuery<ApplyDiscountData>(
APPLY_DISCOUNT_MUTATION,
{ checkoutId, discountCode: code },
);
if (response.errors?.length) {
throw new Error(
`Admin applyDiscount failed: ${response.errors.map((e) => e.message).join(', ')}`,
);
}
const result = response.data?.checkoutDiscountCodeApplyV2;
if (!result) {
return false;
}
if (result.checkoutUserErrors.length > 0) {
return false;
}
return result.checkout !== null;
}
}