Mechanic is a development and ecommerce automation platform for Shopify. :)
This task auto-cancels incoming orders when it finds all of a certain set of risk assessment facts. Risk assessment facts must match exactly, so double-check your configuration! Optionally, this task can also auto-tag the order, email the customer, restock the inventory, and/or refund payment.
Runs Occurs whenever an order is updated. Configuration includes required risk assessment facts, cancellation reason to set, ignore unpaid orders, refund payment for cancelled orders, restock inventory for cancelled orders, email customer when cancelling, staff note for timeline, and add this order tag when cancelling.
This task auto-cancels incoming orders when it finds all of a certain set of risk assessment facts. Risk assessment facts must match exactly, so double-check your configuration! Optionally, this task can also auto-tag the order, email the customer, restock the inventory, and/or refund payment.
Valid cancellation reasons to set:
NOTE: This task will not cancel orders that have been partially or fully fulfilled
Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.
That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.
(By the way, have you seen our documentation? Have you joined the Slack community?)
shopify/orders/updated
{% assign required_risk_assessment_facts = options.required_risk_assessment_facts__array_required %} {% assign cancellation_reason = options.cancellation_reason_to_set | default: "other" %} {% assign ignore_unpaid_orders = options.ignore_unpaid_orders__boolean %} {% assign refund_payment = options.refund_payment_for_cancelled_orders__boolean %} {% assign restock_inventory = options.restock_inventory_for_cancelled_orders__boolean %} {% assign notify_customer = options.email_customer_when_cancelling__boolean %} {% assign staff_note = options.staff_note_for_timeline %} {% assign order_tag_to_apply = options.add_this_order_tag_when_cancelling %} {% comment %} -- check that a valid cancellation reason has been configured; it will default to 'other' if left blank {% endcomment %} {% assign valid_cancellation_reasons = "customer,declined,fraud,inventory,other,staff" | split: "," %} {% unless valid_cancellation_reasons contains cancellation_reason %} {% error %} {{ "Cancellation reason " | append: cancellation_reason | append: " - must be one of 'customer', 'declined', 'fraud', 'inventory', 'other', or 'staff'." | json }} {% enderror %} {% endunless %} {% comment %} -- get the order statuses and risk assessments {% endcomment %} {% capture query %} query { order(id: {{ order.admin_graphql_api_id | json }}) { id name cancelledAt displayFinancialStatus displayFulfillmentStatus risk { assessments { facts { description } } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "order": { "id": "gid://shopify/Order/1234567890", "displayFinancialStatus": "PAID", "displayFulfillmentStatus": "UNFULFILLED", "risk": { "assessments": [ { "facts": [ {% for required_risk_assessment_fact in required_risk_assessment_facts %} { "description": {{ required_risk_assessment_fact | json }} }{% unless forloop.last %},{% endunless %} {% endfor %} ] } ] } } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign order = result.data.order %} {% log order: order %} {% if order.cancelledAt %} {% log "This order has already been cancelled." %} {% break %} {% endif %} {% if ignore_unpaid_orders and order.displayFinancialStatus != "PAID" %} {% log "This order has not been paid and the ignore unpaid orders option is enabled." %} {% break %} {% endif %} {% comment %} -- get the description from each fact in each risk assessment, and look for matches of all configured risk assessment facts {% endcomment %} {% assign order_risk_assessment_facts = order.risk.assessments | map: "facts" | map: "description" %} {% assign matched_risk_assessment_facts = array %} {% for order_risk_assessment_fact in order_risk_assessment_facts %} {% if required_risk_assessment_facts contains order_risk_assessment_fact %} {% assign matched_risk_assessment_facts = matched_risk_assessment_facts | push: order_risk_assessment_fact | uniq %} {% endif %} {% endfor %} {% if matched_risk_assessment_facts.size != required_risk_assessment_facts.size %} {% log message: "Did not match all the risk assessment facts that are required.", matched_risk_assessment_facts: matched_risk_assessment_facts, order_risk_assessment_facts: order_risk_assessment_facts, required_risk_assessment_facts: required_risk_assessment_facts %} {% break %} {% endif %} {% comment %} -- all of the configured risk assessment facts have been matched; check to make sure order is unfulfilled to avoid cancellation error {% endcomment %} {% if order.displayFinancialStatus == "FULFILLED" or order.displayFulfillmentStatus == "PARTIALLY_FULFILLED" %} {% log "This order has already been fulfilled or partially fulfilled and cannot be cancelled." %} {% break %} {% endif %} {% comment %} -- cancel the order with configured options {% endcomment %} {% action "shopify" %} mutation { orderCancel( orderId: {{ order.id | json }} notifyCustomer: {{ notify_customer | json }} reason: {{ cancellation_reason | upcase }} refund: {{ refund_payment | json }} restock: {{ restock_inventory | json }} staffNote: {{ staff_note | json }} ) { job { id } orderCancelUserErrors { code field message } } } {% endaction %} {% comment %} -- tag the order if there is a tag configured to aplly on cancellation {% endcomment %} {% if order_tag_to_apply != blank %} {% action "shopify" %} mutation { tagsAdd( id: {{ order.id | json }} tags: {{ order_tag_to_apply | json }} ) { userErrors { field message } } } {% endaction %} {% endif %}