Pular para o conteúdo principal

Funções do Shopify 2026: descontos, entrega, personalização de pagamentos

Crie funções do Shopify para descontos, personalização da taxa de entrega, filtragem de métodos de pagamento e validação de carrinho. Exemplos de ferrugem + JavaScript.

E
ECOSIRE Research and Development Team
|4 de maio de 20268 min de leitura1.5k Palavras|

Este artigo está atualmente disponível apenas em inglês. Tradução em breve.

Shopify Functions run customer-defined code inside Shopify's checkout pipeline as WebAssembly modules. They replaced Shopify Scripts (Ruby, Plus-only) and now power custom discount logic, delivery rate manipulation, payment method filtering, cart validation, and order routing on every Shopify plan.

Shopify Functions run customer-defined code inside Shopify's checkout pipeline as WebAssembly modules. They replaced Shopify Scripts (Ruby, Plus-only) and now power custom discount logic, delivery rate manipulation, payment method filtering, cart validation, and order routing on every Shopify plan. This article focuses on the three function types most apps ship: discounts, delivery customization, and payment customization. We'll cover the input/output schemas, real Rust and JavaScript implementations, deployment, and the pitfalls we've hit running Functions in production.

Key Takeaways

  • Functions execute in a WebAssembly sandbox with a 5 ms wall-clock budget and 200 KB memory cap
  • Inputs come from a GraphQL query you define; outputs are typed JSON the platform applies
  • Rust gives the most headroom for complex logic; JavaScript (via function-runner) is fine for simple rules
  • Functions deploy as part of an app extension — one app can hold multiple functions of different types
  • Discount Functions can stack with automatic discounts but follow Shopify's combination rules
  • Functions are cached aggressively — the same input produces the same output, so don't rely on Date.now() for time-sensitive logic

What's different about Shopify Functions

Functions are not webhooks. They run synchronously inside Shopify's request path — discounts on every cart update, delivery rates at the rate-shopping step, payment customization at checkout render. The merchant doesn't host them, you do, and you push them as part of an app.

Hard limits per execution:

ResourceLimit
Wall clock5 ms
Memory200 KB
Input size64 KB
Output size64 KB
Network accessNone
FilesystemNone

If your function exceeds the budget, Shopify cancels it and applies the default behavior (no discount, full payment list, etc.). It does not error out the checkout.

Function types

FunctionTriggerMutation analog
Product discountCart updateDiscounts collection
Order discountCart updateDiscounts collection
Shipping discountCart updateDiscounts collection
Delivery customizationRate shoppingDelivery customizations
Payment customizationCheckout renderPayment customizations
Cart validationCart updateCart Transform
Cart TransformCart updateBundles
Order routingFulfillmentRouting

We'll cover the first three families.

Discount Functions: a tiered VIP discount

Goal: Customers tagged vip get 15% off any cart over $200; tagged vip-plus get 20%.

// src/lib.rs
use serde::Deserialize;
use serde_json::json;

#[derive(Deserialize)]
struct Input {
    cart: Cart,
}

#[derive(Deserialize)]
struct Cart {
    cost: Cost,
    buyer_identity: Option<BuyerIdentity>,
    lines: Vec<Line>,
}

#[derive(Deserialize)]
struct Cost { subtotal_amount: Money }

#[derive(Deserialize)]
struct Money { amount: String, currency_code: String }

#[derive(Deserialize)]
struct BuyerIdentity { customer: Option<Customer> }

#[derive(Deserialize)]
struct Customer { has_any_tag: bool, tags: Vec<String> }

#[derive(Deserialize)]
struct Line { id: String }

#[no_mangle]
pub extern "C" fn run() {
    let input: Input = serde_json::from_reader(std::io::stdin()).unwrap();
    let subtotal: f64 = input.cart.cost.subtotal_amount.amount.parse().unwrap();

    let tags = input.cart.buyer_identity
        .and_then(|b| b.customer)
        .map(|c| c.tags)
        .unwrap_or_default();

    let percentage = if tags.contains(&"vip-plus".to_string()) && subtotal >= 200.0 {
        20.0
    } else if tags.contains(&"vip".to_string()) && subtotal >= 200.0 {
        15.0
    } else {
        0.0
    };

    let output = if percentage > 0.0 {
        json!({
            "discounts": [{
                "targets": input.cart.lines.iter().map(|l| json!({"cartLine": {"id": l.id}})).collect::<Vec<_>>(),
                "value": { "percentage": { "value": format!("{:.1}", percentage) } }
            }],
            "discountApplicationStrategy": "MAXIMUM"
        })
    } else {
        json!({ "discounts": [] })
    };

    println!("{}", output);
}

The matching GraphQL input query (in src/run.graphql):

query Input {
  cart {
    cost { subtotalAmount { amount currencyCode } }
    buyerIdentity { customer { tags } }
    lines { id }
  }
}

Deploy with shopify app deploy from the CLI. The merchant then turns on the function in the discounts admin.

Discount combination rules

When discountApplicationStrategy is MAXIMUM, only the largest discount applies per line. FIRST applies in the order returned. Shopify's combinations setting on the discount itself (configured by the merchant, not the function) determines whether your function-discount can stack with manual or automatic discounts.

Delivery customization: hide rates over a weight threshold

Goal: For carts over 50 kg, hide the standard ground rate (it's unsafe; force a freight quote).

// src/run.js (JavaScript flavor)
export function run(input) {
  const totalWeight = input.cart.deliveryGroups
    .flatMap(g => g.cartLines)
    .reduce((sum, line) => sum + line.merchandise.weight.value * line.quantity, 0);

  if (totalWeight < 50) return { operations: [] };

  const operations = [];
  for (const group of input.cart.deliveryGroups) {
    for (const opt of group.deliveryOptions) {
      if (/standard|ground/i.test(opt.title)) {
        operations.push({ hide: { deliveryOptionHandle: opt.handle } });
      }
    }
  }
  return { operations };
}

Operations the platform supports for delivery customization:

  • hide — remove a rate
  • rename — change the title shown to the buyer
  • move — re-order rates

There is no add operation — Functions cannot create rates that don't already exist as Shopify shipping zones or third-party carrier responses. To create rates dynamically, you still need a CarrierService API endpoint.

Payment customization: hide COD on B2B orders

Goal: B2B orders (cart attribute buyer_type=b2b) shouldn't show Cash on Delivery.

export function run(input) {
  const isB2B = input.cart.attribute?.value === 'b2b';
  if (!isB2B) return { operations: [] };

  const codMethods = input.paymentMethods.filter(pm =>
    /cash on delivery|cod/i.test(pm.name)
  );
  return {
    operations: codMethods.map(pm => ({ hide: { paymentMethodId: pm.id } })),
  };
}

Same operation set as delivery: hide, rename, move. Payment customization runs at checkout render, so changes are immediate when buyers update their cart.

Local testing with function-runner

shopify app function run --input fixtures/vip-cart.json

fixtures/vip-cart.json matches the shape of your GraphQL input query. Drop in a few cases (no customer, customer without tag, VIP under threshold, VIP over, VIP-plus) and assert the output. Shopify's CLI runs the WASM through a local sandbox and prints the output JSON.

Deployment and versioning

shopify app deploy --message "VIP discount tiers v2"

Each deploy creates a new app version. Merchants stay on their current version until they explicitly update — or, in the case of automatic updates (set on the version), Shopify rolls them forward. Roll back via shopify app versions list and selecting an older version.

Limits, costs, and gotchas

  • No randomness, no clocks: The runner is deterministic. Math.random() is allowed but seeded; Date.now() returns a fixed value within a request. For time-based logic, use cart attributes set by your app's webhooks.
  • No outbound calls: Functions cannot call your backend. If you need external data (e.g., loyalty points balance), push it via metafields or cart attributes ahead of time.
  • Cart Transform vs Discounts: Don't try to do bundles via discounts — use Cart Transform Functions, which can merge or expand line items.
  • Per-shop function limit: 50 active functions per shop. App Store apps shouldn't burn the budget.

Comparing to Shopify Scripts (Plus-only, deprecated)

AspectShopify ScriptsShopify Functions
PlanPlus onlyAll plans
LanguageRubyRust, JS, AssemblyScript, any WASM-compatible
ExecutionRuby VMWebAssembly sandbox
HostingShopify hosts the scriptShopify hosts the WASM
Limits1 sec, 8 MB5 ms, 200 KB
VersioningInline editApp deploy
DiscoverabilityPlus adminDiscounts admin

Plus merchants have until end of 2026 to migrate Scripts to Functions. After that, Scripts stop running.

Frequently Asked Questions

Can I write Functions in Python or Go?

Not directly. Python doesn't compile to small-enough WASM, and Go's WASM output exceeds the 200 KB cap. Practical options are Rust, JavaScript (via Shopify's javy runtime), AssemblyScript, and TinyGo for very simple cases.

How do I A/B test a Function?

Deploy two app versions and route a portion of your merchant base to each. There's no built-in A/B test for Functions inside a single shop. For per-customer A/B, use cart attributes set by your client-side script.

Why is my Function not running?

Check three things: (1) the merchant has activated the function in their admin, (2) the function targets the correct discount class (product vs order vs shipping), (3) your input query returns the fields you reference. The Functions log in the merchant admin shows runtime errors.

How do Functions interact with the Cart API?

Cart Functions read the cart and can apply discounts, hide payment/delivery options, or transform line items. They don't mutate the cart object directly — Shopify applies the operations declaratively. For external mutations, use the Storefront Cart API.

What happens at scale?

Functions scale with the platform. We've run discount functions on Plus shops doing 30,000 cart updates/minute during BFCM with no degradation. The 5 ms budget is plenty for sane logic.


ECOSIRE has shipped 25+ Shopify Functions across discounts, delivery customization, and Cart Transform for Plus and standard merchants. Our Shopify customization team handles end-to-end Function development, App Store submission, and ongoing tuning.

E

Escrito por

ECOSIRE Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

Amplie sua loja Shopify

Serviços personalizados de desenvolvimento, otimização e migração para comércio eletrônico de alto crescimento.