//native
/**
* Create Order
* 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.
*/
export async function main(
auth: RT.Shopify,
line_items: {
variant_id?: string
title?: string
price?: number
quantity: number
}[],
customer_id: string | undefined,
email: string | undefined,
financial_status:
| "PAID"
| "PENDING"
| "AUTHORIZED"
| "PARTIALLY_PAID"
| "PARTIALLY_REFUNDED"
| "REFUNDED"
| "VOIDED"
| "EXPIRED"
| undefined,
shipping_address:
| {
firstName?: string
lastName?: string
company?: string
address1?: string
address2?: string
city?: string
provinceCode?: string
zip?: string
countryCode?: string
phone?: string
}
| undefined,
note: string | undefined,
tags: string[] | undefined,
currency: string | undefined,
inventory_behaviour:
| "BYPASS"
| "DECREMENT_OBEYING_POLICY"
| "DECREMENT_IGNORING_POLICY"
| undefined,
send_receipt: boolean | undefined,
test: boolean | undefined
) {
const endpoint = `https://${auth.store_name}.myshopify.com/admin/api/2026-10/graphql.json`
const call = async (body: { query: string; variables?: object }) => {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"X-Shopify-Access-Token": auth.token,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify(body),
})
if (!response.ok) {
throw new Error(`${response.status} ${await response.text()}`)
}
const { data, errors } = await response.json()
if (errors) {
throw new Error(JSON.stringify(errors))
}
return data
}
const gid = (value: string, type: string) =>
value.startsWith("gid://") ? value : `gid://shopify/${type}/${value}`
// Custom-item prices need a currency; default to the shop's.
const currencyCode =
currency ||
(line_items.some((item) => item.price != null)
? (await call({ query: `query { shop { currencyCode } }` })).shop
.currencyCode
: undefined)
const data = await call({
query: `mutation ($order: OrderCreateOrderInput!, $options: OrderCreateOptionsInput) { orderCreate(order: $order, options: $options) { order { id } userErrors { field message } } }`,
variables: {
order: {
lineItems: line_items.map((item) => ({
variantId: item.variant_id
? gid(item.variant_id, "ProductVariant")
: undefined,
title: item.title || undefined,
priceSet:
item.price != null
? { shopMoney: { amount: item.price, currencyCode } }
: undefined,
quantity: item.quantity,
})),
customer: customer_id
? { toAssociate: { id: gid(customer_id, "Customer") } }
: undefined,
email: email || undefined,
// A bare PAID status has no transaction behind it (nothing to refund); orderMarkAsPaid below records one.
financialStatus:
financial_status && financial_status !== "PAID"
? financial_status
: undefined,
shippingAddress: shipping_address || undefined,
note: note || undefined,
tags: tags?.length ? tags : undefined,
currency: currency || undefined,
test: test || undefined,
},
options: {
inventoryBehaviour: inventory_behaviour || undefined,
sendReceipt: send_receipt || undefined,
},
},
})
if (data.orderCreate.userErrors.length) {
throw new Error(JSON.stringify(data.orderCreate.userErrors))
}
const id = data.orderCreate.order.id
if (financial_status === "PAID") {
// A new order can be "temporarily unavailable to be modified" for a few seconds (e.g. while inventory is claimed).
for (let attempt = 1; ; attempt++) {
const paid = await call({
query: `mutation ($input: OrderMarkAsPaidInput!) { orderMarkAsPaid(input: $input) { userErrors { field message } } }`,
variables: { input: { id } },
})
const userErrors = paid.orderMarkAsPaid.userErrors
if (!userErrors.length) break
if (
attempt === 10 ||
!/temporarily unavailable/i.test(userErrors[0].message)
) {
throw new Error(JSON.stringify(userErrors))
}
await new Promise((resolve) => setTimeout(resolve, 1000))
}
}
return (
await call({
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 } } } } } }`,
variables: { id },
})
).order
}
Submitted by hugo989 3 hours ago