0

Create Order

by
Published Nov 8, 2023

Create an order from variants or custom items, optionally recording it as paid.

Script shopify Verified

The script

Submitted by hugo989 Typescript (fetch-only)
Verified 2 hours ago
1
//native
2

3
/**
4
 * Create Order
5
 * Create an order directly (no checkout). Each line item is either a variant ({ variant_id, quantity }) or a custom item ({ title, price, quantity }; price in currency, default the shop currency). financial_status PAID records a manual payment for the full amount (so the order can be refunded later); other statuses are only recorded. No card is charged. Inventory is not decremented unless inventory_behaviour says so.
6
 */
7
export async function main(
8
  auth: RT.Shopify,
9
  line_items: {
10
    variant_id?: string
11
    title?: string
12
    price?: number
13
    quantity: number
14
  }[],
15
  customer_id: string | undefined,
16
  email: string | undefined,
17
  financial_status:
18
    | "PAID"
19
    | "PENDING"
20
    | "AUTHORIZED"
21
    | "PARTIALLY_PAID"
22
    | "PARTIALLY_REFUNDED"
23
    | "REFUNDED"
24
    | "VOIDED"
25
    | "EXPIRED"
26
    | undefined,
27
  shipping_address:
28
    | {
29
        firstName?: string
30
        lastName?: string
31
        company?: string
32
        address1?: string
33
        address2?: string
34
        city?: string
35
        provinceCode?: string
36
        zip?: string
37
        countryCode?: string
38
        phone?: string
39
      }
40
    | undefined,
41
  note: string | undefined,
42
  tags: string[] | undefined,
43
  currency: string | undefined,
44
  inventory_behaviour:
45
    | "BYPASS"
46
    | "DECREMENT_OBEYING_POLICY"
47
    | "DECREMENT_IGNORING_POLICY"
48
    | undefined,
49
  send_receipt: boolean | undefined,
50
  test: boolean | undefined
51
) {
52
  const endpoint = `https://${auth.store_name}.myshopify.com/admin/api/2026-10/graphql.json`
53
  const call = async (body: { query: string; variables?: object }) => {
54
    const response = await fetch(endpoint, {
55
      method: "POST",
56
      headers: {
57
        "X-Shopify-Access-Token": auth.token,
58
        "Content-Type": "application/json",
59
        Accept: "application/json",
60
      },
61
      body: JSON.stringify(body),
62
    })
63
    if (!response.ok) {
64
      throw new Error(`${response.status} ${await response.text()}`)
65
    }
66
    const { data, errors } = await response.json()
67
    if (errors) {
68
      throw new Error(JSON.stringify(errors))
69
    }
70
    return data
71
  }
72
  const gid = (value: string, type: string) =>
73
    value.startsWith("gid://") ? value : `gid://shopify/${type}/${value}`
74

75
  // Custom-item prices need a currency; default to the shop's.
76
  const currencyCode =
77
    currency ||
78
    (line_items.some((item) => item.price != null)
79
      ? (await call({ query: `query { shop { currencyCode } }` })).shop
80
          .currencyCode
81
      : undefined)
82

83
  const data = await call({
84
    query: `mutation ($order: OrderCreateOrderInput!, $options: OrderCreateOptionsInput) { orderCreate(order: $order, options: $options) { order { id } userErrors { field message } } }`,
85
    variables: {
86
      order: {
87
        lineItems: line_items.map((item) => ({
88
          variantId: item.variant_id
89
            ? gid(item.variant_id, "ProductVariant")
90
            : undefined,
91
          title: item.title || undefined,
92
          priceSet:
93
            item.price != null
94
              ? { shopMoney: { amount: item.price, currencyCode } }
95
              : undefined,
96
          quantity: item.quantity,
97
        })),
98
        customer: customer_id
99
          ? { toAssociate: { id: gid(customer_id, "Customer") } }
100
          : undefined,
101
        email: email || undefined,
102
        // A bare PAID status has no transaction behind it (nothing to refund); orderMarkAsPaid below records one.
103
        financialStatus:
104
          financial_status && financial_status !== "PAID"
105
            ? financial_status
106
            : undefined,
107
        shippingAddress: shipping_address || undefined,
108
        note: note || undefined,
109
        tags: tags?.length ? tags : undefined,
110
        currency: currency || undefined,
111
        test: test || undefined,
112
      },
113
      options: {
114
        inventoryBehaviour: inventory_behaviour || undefined,
115
        sendReceipt: send_receipt || undefined,
116
      },
117
    },
118
  })
119
  if (data.orderCreate.userErrors.length) {
120
    throw new Error(JSON.stringify(data.orderCreate.userErrors))
121
  }
122
  const id = data.orderCreate.order.id
123

124
  if (financial_status === "PAID") {
125
    // A new order can be "temporarily unavailable to be modified" for a few seconds (e.g. while inventory is claimed).
126
    for (let attempt = 1; ; attempt++) {
127
      const paid = await call({
128
        query: `mutation ($input: OrderMarkAsPaidInput!) { orderMarkAsPaid(input: $input) { userErrors { field message } } }`,
129
        variables: { input: { id } },
130
      })
131
      const userErrors = paid.orderMarkAsPaid.userErrors
132
      if (!userErrors.length) break
133
      if (
134
        attempt === 10 ||
135
        !/temporarily unavailable/i.test(userErrors[0].message)
136
      ) {
137
        throw new Error(JSON.stringify(userErrors))
138
      }
139
      await new Promise((resolve) => setTimeout(resolve, 1000))
140
    }
141
  }
142

143
  return (
144
    await call({
145
      query: `query ($id: ID!) { order(id: $id) { id legacyResourceId name email createdAt displayFinancialStatus displayFulfillmentStatus tags note totalPriceSet { shopMoney { amount currencyCode } } totalOutstandingSet { shopMoney { amount currencyCode } } customer { id displayName } lineItems(first: 50) { nodes { id name quantity variant { id } originalUnitPriceSet { shopMoney { amount currencyCode } } } } } }`,
146
      variables: { id },
147
    })
148
  ).order
149
}
150

Other submissions
  • Submitted by hugo697 Typescript (fetch-only)
    Created 528 days ago
    1
    type Shopify = {
    2
      token: string;
    3
      store_name: string;
    4
    };
    5
    /**
    6
     * Creates an order
    7
     * Creates an order.
    8
     */
    9
    export async function main(
    10
      auth: Shopify,
    11
      api_version: string = "2023-10",
    12
      body: {
    13
        order?: {
    14
          email?: string;
    15
          fulfillment_status?: string;
    16
          fulfillments?: { location_id?: number; [k: string]: unknown }[];
    17
          line_items?: {
    18
            quantity?: number;
    19
            variant_id?: number;
    20
            [k: string]: unknown;
    21
          }[];
    22
          [k: string]: unknown;
    23
        };
    24
        [k: string]: unknown;
    25
      }
    26
    ) {
    27
      const url = new URL(
    28
        `https://${auth.store_name}.myshopify.com/admin/api/${api_version}/orders.json`
    29
      );
    30
    
    
    31
      const response = await fetch(url, {
    32
        method: "POST",
    33
        headers: {
    34
          "Content-Type": "application/json",
    35
          "X-Shopify-Access-Token": auth.token,
    36
        },
    37
        body: JSON.stringify(body),
    38
      });
    39
      if (!response.ok) {
    40
        const text = await response.text();
    41
        throw new Error(`${response.status} ${text}`);
    42
      }
    43
      return await response.json();
    44
    }
    45