1 | |
2 |
|
3 | |
4 | * Create Draft Order |
5 | * Create a draft order (a quote or invoice to send before payment). Each line item is either a variant ({ variant_id, quantity }) or a custom item ({ title, price, quantity }; price in the shop currency). Send it with Send Draft Order Invoice or turn it into an order with Complete Draft Order. |
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 | shipping_address: |
18 | | { |
19 | firstName?: string |
20 | lastName?: string |
21 | company?: string |
22 | address1?: string |
23 | address2?: string |
24 | city?: string |
25 | provinceCode?: string |
26 | zip?: string |
27 | countryCode?: string |
28 | phone?: string |
29 | } |
30 | | undefined, |
31 | discount: |
32 | | { |
33 | value: number |
34 | value_type: "PERCENTAGE" | "FIXED_AMOUNT" |
35 | title?: string |
36 | } |
37 | | undefined, |
38 | note: string | undefined, |
39 | tags: string[] | undefined, |
40 | use_customer_default_address: boolean | undefined |
41 | ) { |
42 | const endpoint = `https://${auth.store_name}.myshopify.com/admin/api/2026-10/graphql.json` |
43 | const call = async (body: { query: string; variables?: object }) => { |
44 | const response = await fetch(endpoint, { |
45 | method: "POST", |
46 | headers: { |
47 | "X-Shopify-Access-Token": auth.token, |
48 | "Content-Type": "application/json", |
49 | Accept: "application/json", |
50 | }, |
51 | body: JSON.stringify(body), |
52 | }) |
53 | if (!response.ok) { |
54 | throw new Error(`${response.status} ${await response.text()}`) |
55 | } |
56 | const { data, errors } = await response.json() |
57 | if (errors) { |
58 | throw new Error(JSON.stringify(errors)) |
59 | } |
60 | return data |
61 | } |
62 | const gid = (value: string, type: string) => |
63 | value.startsWith("gid://") ? value : `gid://shopify/${type}/${value}` |
64 |
|
65 | |
66 | const currencyCode = line_items.some((item) => item.price != null) |
67 | ? (await call({ query: `query { shop { currencyCode } }` })).shop |
68 | .currencyCode |
69 | : undefined |
70 |
|
71 | const data = await call({ |
72 | query: `mutation ($input: DraftOrderInput!) { draftOrderCreate(input: $input) { draftOrder { id legacyResourceId name status email invoiceUrl tags note2 totalPriceSet { shopMoney { amount currencyCode } } customer { id displayName } lineItems(first: 50) { nodes { id name quantity custom originalUnitPriceSet { shopMoney { amount currencyCode } } variant { id } } } } userErrors { field message } } }`, |
73 | variables: { |
74 | input: { |
75 | lineItems: line_items.map((item) => ({ |
76 | variantId: item.variant_id |
77 | ? gid(item.variant_id, "ProductVariant") |
78 | : undefined, |
79 | title: item.title || undefined, |
80 | originalUnitPriceWithCurrency: |
81 | item.price != null |
82 | ? { amount: item.price, currencyCode } |
83 | : undefined, |
84 | quantity: item.quantity, |
85 | })), |
86 | purchasingEntity: customer_id |
87 | ? { customerId: gid(customer_id, "Customer") } |
88 | : undefined, |
89 | email: email || undefined, |
90 | shippingAddress: shipping_address || undefined, |
91 | appliedDiscount: discount |
92 | ? { |
93 | value: discount.value, |
94 | valueType: discount.value_type, |
95 | title: discount.title || undefined, |
96 | } |
97 | : undefined, |
98 | note: note || undefined, |
99 | tags: tags?.length ? tags : undefined, |
100 | useCustomerDefaultAddress: use_customer_default_address || undefined, |
101 | }, |
102 | }, |
103 | }) |
104 |
|
105 | if (data.draftOrderCreate.userErrors.length) { |
106 | throw new Error(JSON.stringify(data.draftOrderCreate.userErrors)) |
107 | } |
108 |
|
109 | return data.draftOrderCreate |
110 | } |
111 |
|