Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Unable to Create Draft Order with Custom Prices Using GraphQL API

Unable to Create Draft Order with Custom Prices Using GraphQL API

EdgeCodes
Shopify Partner
1 0 0

Hello Shopify Community,

 

I'm encountering an issue while trying to create a draft order with Custom Prices using the GraphQL API. Despite following the documentation, I'm relentlessly running into an issue that I don't understand why it’s happening.

Here is the most relevant part of terminal response:

00:29:23 │                     remix │ Line items: [
00:29:23 │                     remix │   {
00:29:23 │                     remix │     "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │     "quantity": 1,
00:29:23 │                     remix │     "originalUnitPrice": {
00:29:23 │                     remix │       "amount": "3.50",
00:29:23 │                     remix │       "currencyCode": "GBP"
00:29:23 │                     remix │     }
00:29:23 │                     remix │   },
00:29:23 │                     remix │   {
00:29:23 │                     remix │     "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │     "quantity": 1,
00:29:23 │                     remix │     "originalUnitPrice": {
00:29:23 │                     remix │       "amount": "8.00",
00:29:23 │                     remix │       "currencyCode": "GBP"
00:29:23 │                     remix │     }
00:29:23 │                     remix │   }
00:29:23 │                     remix │ ]
00:29:23 │                     remix │ GraphQL Variables: {
00:29:23 │                     remix │   "input": {
00:29:23 │                     remix │     "lineItems": [
00:29:23 │                     remix │       {
00:29:23 │                     remix │         "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │         "quantity": 1,
00:29:23 │                     remix │         "originalUnitPrice": {
00:29:23 │                     remix │           "amount": "3.50",
00:29:23 │                     remix │           "currencyCode": "GBP"
00:29:23 │                     remix │         }
00:29:23 │                     remix │       },
00:29:23 │                     remix │       {
00:29:23 │                     remix │         "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │         "quantity": 1,
00:29:23 │                     remix │         "originalUnitPrice": {
00:29:23 │                     remix │           "amount": "8.00",
00:29:23 │                     remix │           "currencyCode": "GBP"
00:29:23 │                     remix │         }
00:29:23 │                     remix │       }
00:29:23 │                     remix │     ],
00:29:23 │                     remix │     "useCustomerDefaultAddress": true
00:29:23 │                     remix │   }
00:29:23 │                     remix │ }
00:29:23 │                     remix │ Serialized Payload before sending: {"query":"\n      mutation createDraftOrder($input: DraftOrderInput!) {\n
draftOrderCreate(input: $input) {\n          draftOrder {\n            id\n            invoiceUrl\n          }\n          userErrors {\n            field\n
message\n          }\n        }\n      }\n
","variables":{"input":{"lineItems":[{"variantId":"gid://shopify/ProductVariant/49595202273618","quantity":1,"originalUnitPrice":{"amount":"3.50","currencyCode":"GBP"}},{"vari
antId":"gid://shopify/ProductVariant/49595202404690","quantity":1,"originalUnitPrice":{"amount":"8.00","currencyCode":"GBP"}}],"useCustomerDefaultAddress":true}}}
00:29:23 │                     remix │ Draft Order Response: {
00:29:23 │                     remix │   "errors": [
00:29:23 │                     remix │     {
00:29:23 │                     remix │       "message": "Variable $input of type DraftOrderInput! was provided invalid value for lineItems.0.originalUnitPrice (invalid money  
'{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'), lineItems.1.originalUnitPrice (invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}')",
00:29:23 │                     remix │       "locations": [
00:29:23 │                     remix │         {
00:29:23 │                     remix │           "line": 2,
00:29:23 │                     remix │           "column": 33
00:29:23 │                     remix │         }
00:29:23 │                     remix │       ],
00:29:23 │                     remix │       "extensions": {
00:29:23 │                     remix │         "value": {
00:29:23 │                     remix │           "lineItems": [
00:29:23 │                     remix │             {
00:29:23 │                     remix │               "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │               "quantity": 1,
00:29:23 │                     remix │               "originalUnitPrice": {
00:29:23 │                     remix │                 "amount": "3.50",
00:29:23 │                     remix │                 "currencyCode": "GBP"
00:29:23 │                     remix │               }
00:29:23 │                     remix │             },
00:29:23 │                     remix │             {
00:29:23 │                     remix │               "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │               "quantity": 1,
00:29:23 │                     remix │               "originalUnitPrice": {
00:29:23 │                     remix │                 "amount": "8.00",
00:29:23 │                     remix │                 "currencyCode": "GBP"
00:29:23 │                     remix │               }
00:29:23 │                     remix │             }
00:29:23 │                     remix │           ],
00:29:23 │                     remix │           "useCustomerDefaultAddress": true
00:29:23 │                     remix │         },
00:29:23 │                     remix │         "problems": [
00:29:23 │                     remix │           {
00:29:23 │                     remix │             "path": [
00:29:23 │                     remix │               "lineItems",
00:29:23 │                     remix │               0,
00:29:23 │                     remix │               "originalUnitPrice"
00:29:23 │                     remix │             ],
00:29:23 │                     remix │             "explanation": "invalid money '{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'",
00:29:23 │                     remix │             "message": "invalid money '{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'"
00:29:23 │                     remix │           },
00:29:23 │                     remix │           {
00:29:23 │                     remix │             "path": [
00:29:23 │                     remix │               "lineItems",
00:29:23 │                     remix │               1,
00:29:23 │                     remix │               "originalUnitPrice"
00:29:23 │                     remix │             ],
00:29:23 │                     remix │             "explanation": "invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}'",
00:29:23 │                     remix │             "message": "invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}'"
00:29:23 │                     remix │           }
00:29:23 │                     remix │         ]
00:29:23 │                     remix │       }
00:29:23 │                     remix │     }
00:29:23 │                     remix │   ]
00:29:23 │                     remix │ }

The issue consistently appears around how my originalUnitPrice is being processed. The error that persists is:

"message": "Variable $input of type DraftOrderInput! was provided invalid value for lineItems.0.originalUnitPrice (invalid money  
'{\\"amount\\" => \\"3.50\\", \\"currencyCode\\" => \\"GBP\\"}'), lineItems.1.originalUnitPrice (invalid money '{\\"amount\\" => \\"8.00\\", \\"currencyCode\\" => \\"GBP\\"}')"


As you can see, it says that originalUnitPrice is invalid and it clearly points to an issue with how the MoneyInput is being formatted. As far as my understanding goes, it is not being structured in a way that Shopify API expects - a correctly formatted JSON, using colons (:) for key-value pairs. But this error message shows the => format (e.g., "{\"amount\" => 3.5, \"currencyCode\" => \"GBP\"}") despite all my different efforts to serialize the JSON correctly. Clearly, the => notation is the root problem here since the incorrect format is being flagged as invalid. So, it’s something must be wrong with how the input data is being structured and sent to GraphQL API.

 

What I've tried

I've already tried several different approaches, used fetch, used direct JSON.stringify, and now using axios.

I even tried some extreme approaches such as manually building the JSON payload string instead of relying on JSON.stringify, axios, or fetch, etc. Just to make sure the transformation and structure are exactly as required by Shopify.

I’ve tried to manually serialize the JSON payload to eliminate any hidden transformations.

I was hoping that by manually serialising the JSON payload I'll pitentially eliminate any hidden transformations or even get any insight into what's going to happen if I completely bypass any serialization that might be happening within these!

What's insane is that no matter the method I keep getting again and again the exact same error.

The originalUnitPrice is structured as an object with amount and currencyCode fields. I've double-checked that the prices are valid numbers and the currency code is correct.

The variant IDs are correct and exist in my store.

This is how I have structured my code:

export const action: ActionFunction = async ({ request }) => {
  try {
    // Authenticating the app proxy request
    const { admin, session } = await authenticate.public.appProxy(request);
    . . .

    // Ensuring the session has an accessToken
    . . .

    // Extracting the shop domain from the session
    . . .

    // Some other business logic



    // Preparing line items for the draft order
    const lineItems = requestData.items.map((item: DraftOrderItem) => ({
      variantId: `gid://shopify/ProductVariant/${item.variant_id}`,
      quantity: item.quantity,
      originalUnitPrice: {
        amount: parseFloat(item.price).toFixed(2).toString(), // Ensuring amount is formatted as a string with two decimal places
        currencyCode: item.currencyCode || "GBP",
      }
    }));
    console.log('Line items:', JSON.stringify(lineItems, null, 2));

    // GraphQL Variables
    const variables = {
      input: {
        email: requestData.customer_email,
        lineItems: lineItems,
        useCustomerDefaultAddress: true,
      }
    };

    console.log('GraphQL Variables:', JSON.stringify(variables, null, 2));

    // GraphQL mutation
    const query = `
      mutation createDraftOrder($input: DraftOrderInput!) {
        draftOrderCreate(input: $input) {
          draftOrder {
            id
            invoiceUrl
          }
          userErrors {
            field
            message
          }
        }
      }
    `;

    // I'm stringifying the payload directly
    const payload = JSON.stringify({
      query,
      variables
    });
    console.log('Serialized Payload before sending:', payload);

    // Sending the request using axios
    const response = await axios.post(
      `https://${session.shop}/admin/api/2024-07/graphql.json`,
      {
        query,
        variables
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-Shopify-Access-Token': session.accessToken
        }
      }
    );

    const draftOrderData = response.data;
    console.log('Draft Order Response:', JSON.stringify(draftOrderData, null, 2));

    // Checking for user errors in the response
    if (draftOrderData.data?.draftOrderCreate?.userErrors?.length > 0) {
      console.error('User errors:', draftOrderData.data.draftOrderCreate.userErrors);
      return json({ error: draftOrderData.data.draftOrderCreate.userErrors[0].message }, { status: 400 });
    }

    // Extracting the invoice URL from the response
    const invoiceUrl = draftOrderData.data?.draftOrderCreate?.draftOrder?.invoiceUrl;

    return json({ checkout_url: invoiceUrl });
  } catch (error) {
    console.error('Error creating draft order:', error);
    return json({ error: 'An error occurred while creating the draft order' }, { status: 500 });
  }
};

 

Questions:

  1. Are there any known issues or limitations with setting custom prices for draft orders via the GraphQL API?
  2. Is there an alternative approach to create draft orders with custom prices that I should consider?
  3. Why is my originalUnitPrice being converted to Ruby hash syntax in GraphQL mutation LOL? Is it possible that there's some intermediary process that I’m not aware of in my environment or library that's causing this conversion before the data reaches GraphQL API?
  4. Is there a way ensure MoneyInput is correctly formatted for DraftOrderInput in GraphQL API?

 

It would really help to get specific guidance on the expected format and anything really that points to the correct way to structure the MoneyInput for the originalUnitPrice field within the DraftOrderInput.

Any insights or suggestions would be greatly appreciated. Thank you in advance for your help!

Reply 1 (1)

gojiberry-des
Shopify Partner
29 7 5

According to the docs "originalUnitPrice" is deprecated and also the type takes a number without currency.

 

Try either removing currency like the example in the docs.

gojiberrydes_0-1729222291085.png

Or use "originalUnitPriceWithCurrency" instead.

gojiberrydes_1-1729222320485.png

 

 

https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/draftOrderCreate

If you like my suggestion please give it a LIKE or mark it as a solution! ★★★★★

Gojiberry - Best post-purchase survey and feedback app for Shopify | Install on App Store for free
Shopify Community is helpful, BUT... why not start asking your customers for feedback instead?