Order financial_status still paid after order is cancelled and refunded

Hi there,

I cancelled the refunded an order with id=“gid://shopify/Order/6002887098545”

mutation($orderId: ID!) {
  orderCancel(
    orderId: $orderId
    restock: true
    reason: OTHER
    refund: true
  ) {
    job {
      id
      done
    }
    orderCancelUserErrors {
      code
      field
      message
    }
  }
}

response: "{\"orderCancel\" =\u003e {\"job\" =\u003e {\"id\" =\u003e \"gid://shopify/Job/8a4fc9f1-7789-4051-afa7-e120857d5327\", \"done\" =\u003e false}, \"orderCancelUserErrors\" =\u003e []}}"

however, the financial status is still “paid” instead of “refunded”. How do I make sure that the order financial status is “refunded” or “cancelled”?

https://05a2f9-2.myshopify.com/admin/api/2025-01/6002887098545.json

Note that the order is fully refunded. Only the financial status is incorrect.

1 Like

this might be related to Order financial status is still 'paid' after order is cancelled which is still unresolved.

Hey @sengming, I’ve taken a look at the other thread.

To confirm if this is the same behavior, can you tell me how this order was originally processed? When you look at the order timeline, do you see the refund being processed on the order as a result of the mutation?

If you don’t have access to the admin, does the order events return a refund event?

Hey @sengming, Are you still experiencing this issue, or can I mark this as solved?

Hi @KyleG-Shopify no this is still an issue do not mark it as solved. I just got back from vacation and I’ll be replying to your previous mail soon.

1 Like

hi @KyleG-Shopify the order was created with Order - REST

here are the order events

{"order" =>
  {"events" =>
    {"edges" =>
      [{"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135745925087409",
          "action" => "placed",
          "message" => "M***iana A***a B***i C** M** placed this order on Easy Shopee, TikTok & Lazada.",
          "createdAt" => "2025-07-16T15:41:11Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135745925120177",
          "action" => "confirmation_number_generated",
          "message" => "Confirmation #QWIQ0XA7Y was generated for this order.",
          "createdAt" => "2025-07-16T15:41:11Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => false,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135745925218481",
          "action" => "note_created",
          "message" => "Easy Shopee, TikTok & Lazada added a note to this order.",
          "createdAt" => "2025-07-16T15:41:12Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135745986560177",
          "action" => "confirmed",
          "message" =>
           "Received new order <a href=\"https://05a2f9-2.myshopify.com/admin/orders/6002887098545\">#146945</a>.",
          "createdAt" => "2025-07-16T15:43:02Z",
          "appTitle" => nil,
          "attributeToApp" => false,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135746205843633",
          "action" => "refund_restock",
          "message" => "Easy Shopee, TikTok &amp; Lazada restocked 1 item at 1 location.",
          "createdAt" => "2025-07-16T15:49:54Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135746205974705",
          "action" => "refund_created",
          "message" => "Easy Shopee, TikTok &amp; Lazada refunded 1 item and shipping.",
          "createdAt" => "2025-07-16T15:49:54Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135746206040241",
          "action" => "closed",
          "message" => "Easy Shopee, TikTok &amp; Lazada archived this order.",
          "createdAt" => "2025-07-16T15:49:54Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}},
       {"node" =>
         {"__typename" => "BasicEvent",
          "id" => "gid://shopify/BasicEvent/135746206171313",
          "action" => "cancelled",
          "message" => "Easy Shopee, TikTok &amp; Lazada canceled this order.",
          "createdAt" => "2025-07-16T15:49:54Z",
          "appTitle" => "Easy Shopee, TikTok & Lazada",
          "attributeToApp" => true,
          "attributeToUser" => false,
          "criticalAlert" => false}}]}}}

with query

query ($orderId: ID!) {
  order(id: $orderId) {
    events(first: 250) {
      edges {
        node {
          id
          action
          message
          createdAt
          appTitle
          attributeToApp
          attributeToUser
          criticalAlert
        }
      }
    }
  }
} 

Thank you. Taking a look!

Hi there, are the any updates on this? Thanks!

Hey @sengming,

I was able to replicate what you’re seeing and dug into the other thread that Alan was investigating. What I see here matches with what was already reported by Alan.

What I found is that this is currently working as intended. When an order is marked as “PAID” but hasn’t actually processed payment (like orders created via REST API), cancelling with refund: true doesn’t trigger a refund because there’s no actual money to refund. This means the displayFinancialStatus remains “PAID” since technically no refund was processed.

While this status is accurate from a technical perspective, our team is considering ways to improve how this status is displayed to reduce merchant confusion in these scenarios.

thanks for looking into this! I’m glad that y’all are looking into it. One more thing, can you confirm that orders created with graphql, e.g. with orderCreate - GraphQL Admin will not be affected by this?

It’s not just for REST. I tested the orderCreate GraphQL mutation as well and it exhibits exactly the same behavior. The issue occurs when orders are created with financialStatus: "PAID" but don’t have associated payment transactions.

Here’s what I found across different creation methods:

Order Creation Method Has Transaction Input Status Refund Amount
REST API (no transaction) :cross_mark: No PAID :cross_mark: $0.00
GraphQL orderCreate (no transaction) :cross_mark: No PAID :cross_mark: $0.00
GraphQL orderCreate (with transaction) :white_check_mark: Yes REFUNDED :white_check_mark: $40.00
REST API (with transaction) :white_check_mark: Yes REFUNDED :white_check_mark: $45.00
Draft Order Complete :white_check_mark: Yes REFUNDED :white_check_mark: $35.00

It’s really about whether there’s associated transactions to refund rather than which API you’re using. When orders have associated transactions, the refund process works correctly and updates the financial status to “REFUNDED” when cancelled.

1 Like

Nice! Thanks for putting in the work. How do you create an order with transactions? It looks like I am probably doing that wrong.

Here’s an example of how to do it:

mutation createOrderWithTransaction {
  orderCreate(order: {
    lineItems: [
      {
        title: "Test Product",
        quantity: 1,
        priceSet: {
          shopMoney: {
            amount: "25.00",
            currencyCode: USD
          }
        }
      }
    ],
    transactions: [
      {
        kind: SALE,
        status: SUCCESS,
        amountSet: {
          shopMoney: {
            amount: "25.00",
            currencyCode: USD
          }
        },
        gateway: "manual"
      }
    ]
  }) {
    order {
      id
      name
      displayFinancialStatus
      totalPrice
      transactions {
        id
        kind
        status
        amount
        gateway
      }
    }
    userErrors {
      field
      message
    }
  }
}

thanks! I’m looking forward to trying it out.

1 Like

thanks @KyleG-Shopify for showing me an example of creating an order with transactions. Do you know if there is a way to create transactions without specifying the amount? I.e. I’d love that transaction to cover the full amount of the order.

When an order has line items with variant_ids, tax line items etc it can quite hard to figure out the full amount

Great question.

Typically for the orderCreate mutation it’s use to create records of orders, so those values would be assumed to be known.

If you’re looking for calculations to be calculated based on the order details, using draftorders may be better as that would create a transaction when completed.

Hey @sengming, I realized we overlooked a mutation that might actually simplify this whole process for you!

The markOrderAsPaid mutation works great for orders created with orderCreate. Here’s how it functions: you create your order with financialStatus: "PENDING" (no transactions needed), then use markOrderAsPaid which automatically calculates the full outstanding amount and creates proper SALE transactions. When you later cancel with refund: true, it correctly processes the refund and updates the financial status to “REFUNDED” instead of staying stuck on “PAID”.

From my testing, the main limitation is that markOrderAsPaid only works on orders with a positive outstanding balance where canMarkAsPaid returns true. But this approach completely eliminates the complexity of manually calculating amounts with taxes and line items, while ensuring proper refund behavior that you were originally seeking.

Lovely this is exactly what I was looking for. Thanks for all the help!

1 Like

I’m running into what could be a related issue. Are you saying that it is normal for an order to be marked as “paid” and not have any payments? So, if displayFinancialStatus is “PAID”, and fullyPaid is false and unpaid is true, should it be considered paid?

Hey @mattcoz, if fullyPaid=false and unpaid=true, no actual payment has been received regardless of what displayFinancialStatus shows.

The mismatch happens because these fields measure different things. displayFinancialStatus is presentational and can be set manually, while fullyPaid and unpaid are calculated from transaction records.

If you’re creating orders via API and need these fields to align, you can either include transactions when creating the order (see OrderCreateOrderTransactionInput) or create with financialStatus: "PENDING", then use markOrderAsPaid which creates proper transaction records