Mechanic is a development and ecommerce automation platform for Shopify. :)
Running when an order is updated, this task captures payment in proportion to the order value that has been fulfilled. For example, for an order with a subtotal of $10 and a total of $15 with shipping/taxes/discounts, this task will capture $7.50 when $5 of the order's value has been fulfilled.
Runs Occurs whenever an order is updated. Configuration includes payment gateway names.
Running when an order is updated, this task captures payment in proportion to the order value that has been fulfilled. For example, for an order with a subtotal of $10 and a total of $15 with shipping/taxes/discounts, this task will capture $7.50 when $5 of the order's value has been fulfilled.
This task only works with payment gateways that support multiple captures against an authorization, which includes Shopify Payments. However, this task will not process orders that use multiple payment gateways on the same order.
Note: To find the payment gateway names, you will need to check the payment_gateway_names field on any order that uses the gateway(s) you wish to configure in this task. One method of doing this is to add .json to the end of the order admin page address, and searching for payment_gateway_names (e.g. "https://admin.shopify.com/store/my-shop/orders/1234567890.json").
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 payment_gateway_names = options.payment_gateway_names__array_required %} {% if event.preview %} {% assign order = hash %} {% assign order["admin_graphql_api_id"] = "gid://shopify/Order/1234567890" %} {% assign order["financial_status"] = "partially_paid" %} {% endif %} {% if order.financial_status != "authorized" and order.financial_status != "partially_paid" %} {% log "Order is not either authorized or partially_paid." %} {% else %} {% capture query %} query { order(id: {{ order.admin_graphql_api_id | json }}) { id paymentGatewayNames transactions(capturable: true) { id amountSet { presentmentMoney { amount currencyCode } } } totalPriceSet { presentmentMoney { amount } } subtotalPriceSet { presentmentMoney { amount } } totalCapturableSet { presentmentMoney { amount } } fulfillments { name fulfillmentLineItems(first: 250) { pageInfo { hasNextPage } nodes { discountedTotalSet { presentmentMoney { amount } } } } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "order": { "paymentGatewayNames": [ {{ payment_gateway_names.first | json }} ], "transactions": [ { "id": "gid://shopify/OrderTransaction/1234567890", "amountSet": { "presentmentMoney": { "amount": "16.99", "currencyCode": "USD" } } } ], "totalPriceSet": { "presentmentMoney": { "amount": "16.99" } }, "subtotalPriceSet": { "presentmentMoney": { "amount": "10.0" } }, "totalCapturableSet": { "presentmentMoney": { "amount": "16.54" } }, "fulfillments": [ { "name": "#1234-F1", "fulfillmentLineItems": { "nodes": [ { "discountedTotalSet": { "presentmentMoney": { "amount": "5.0" } } } ] } } ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign order = result.data.order %} {% unless order.paymentGatewayNames.size == 1 and payment_gateway_names contains order.paymentGatewayNames.first %} {% log message: "This order did not use one of the configured gateways or there are multiple gateways on the order; skipping.", configured_payment_gateway_names: payment_gateway_names, order: order %} {% break %} {% endunless %} {% assign total = order.totalPriceSet.presentmentMoney.amount | times: 1.0 %} {% assign total_captured = total | minus: order.totalCapturableSet.presentmentMoney.amount %} {% assign subtotal = order.subtotalPriceSet.presentmentMoney.amount | times: 1.0 %} {% assign subtotal_fulfilled = 0.0 %} {% for fulfillment in order.fulfillments %} {% if fulfillment.fulfillmentLineItems.pageInfo.hasNextPage %} {% error "This order has a fulfillment with more than 250 line items in it. This is not supported by this task." %} {% break %} {% endif %} {% for fulfillment_line_item in fulfillment.fulfillmentLineItems.nodes %} {% assign subtotal_fulfilled = subtotal_fulfilled | plus: fulfillment_line_item.discountedTotalSet.presentmentMoney.amount %} {% endfor %} {% endfor %} {% assign desired_total_captured = subtotal_fulfilled | divided_by: subtotal | times: total %} {% assign amount_to_capture = desired_total_captured | minus: total_captured | times: 100 | round | divided_by: 100.0 %} {% assign parent_transaction = order.transactions.first %} {% log total: total, total_captured: total_captured, subtotal: subtotal, subtotal_fulfilled: subtotal_fulfilled, desired_total_captured: desired_total_captured, amount_to_capture: amount_to_capture, parent_transaction: parent_transaction %} {% if order.capturable == false %} {% log "This order is not capturable." %} {% elsif amount_to_capture == 0 %} {% log "Nothing to capture at this time." %} {% else %} {% action "shopify" %} mutation { orderCapture( input: { id: {{ order.id | json }} parentTransactionId: {{ parent_transaction.id | json }} amount: {{ amount_to_capture | append: "" | json }} currency: {{ parent_transaction.amountSet.presentmentMoney.currencyCode }} } ) { transaction { id status } userErrors { field message } } } {% endaction %} {% endif %} {% endif %}