Skip to main content

Overview

Payment Customization Functions let you show, hide, or reorder payment methods based on cart contents, customer data, or other conditions.

Use Cases

Hide Payment Methods

Remove options based on conditions
  • Hide COD for high-value orders
  • Remove Buy Now Pay Later for B2B
  • Restrict payment by location

Reorder Methods

Change payment method order
  • Promote preferred methods
  • Push expensive methods down
  • Prioritize by customer segment

Rename Methods

Customize payment method names
  • Add processing fees
  • Show savings
  • Clarify payment terms

Conditional Display

Complex payment logic
  • VIP payment methods
  • Location-based options
  • Cart value restrictions

How It Works

1

Checkout Loads

Customer reaches payment method selection in checkout
2

Function Executes

Your function receives cart data and available payment methods
3

Methods Customized

Function returns which methods to show/hide and their order
4

Checkout Updates

Payment method list updates based on function output

Example: Hide COD for High-Value Orders

Generate a function that hides cash on delivery for expensive orders:
1

Describe Your Function

Create a payment customization function that hides the 
"Cash on Delivery" payment method when the cart total 
is over $200. For orders under $200, show all payment 
methods as normal.
2

Review Generated Code

Synapse creates:
  • Function logic (Rust or JavaScript)
  • Input query for cart and payment data
  • Customization rules
  • Configuration
3

Deploy & Test

  • Automatic validation
  • Deployed to dev store
  • Test with different cart totals
4

Activate Function

Payment functions are automatically active once deployed

Generated Function Structure

extensions/payment-customization/
├── src/
│   ├── run.rs                 # Function logic (Rust)
│   │   OR
│   ├── run.js                 # Function logic (JS)
│   └── run.graphql           # Input query
├── shopify.extension.toml     # Config
└── Cargo.toml / package.json

Function Code Examples

  • Hide COD (Rust)
  • Reorder Methods (JavaScript)
  • Rename Method
  • Input Query
use shopify_function::prelude::*;
use shopify_function::Result;

#[shopify_function_target(
    query_path = "src/run.graphql",
    schema_path = "schema.graphql"
)]
fn run(input: input::ResponseData) -> Result<output::FunctionRunResult> {
    // Calculate cart total
    let cart_total: f64 = input
        .cart
        .cost
        .subtotal_amount
        .amount
        .parse()
        .unwrap_or(0.0);
    
    // Find COD payment method
    let operations = input
        .payment_methods
        .iter()
        .filter_map(|method| {
            // Check if this is Cash on Delivery
            if method.name.to_lowercase().contains("cash") {
                // Hide if cart over $200
                if cart_total > 200.0 {
                    return Some(output::Operation::Hide(output::HideOperation {
                        payment_method_id: method.id.clone(),
                    }));
                }
            }
            None
        })
        .collect();
    
    Ok(output::FunctionRunResult {
        operations,
    })
}

Common Patterns

Show/hide payment methods based on order total:
let cart_total: f64 = input.cart.cost.subtotal_amount.amount
    .parse()
    .unwrap_or(0.0);

let operations = input.payment_methods
    .iter()
    .filter_map(|method| {
        match method.name.as_str() {
            "Cash on Delivery" if cart_total > 200.0 => {
                Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }))
            },
            "Buy Now Pay Later" if cart_total < 50.0 => {
                Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }))
            },
            _ => None,
        }
    })
    .collect();
Prompt Example:
"Hide Cash on Delivery for orders over $200. Hide Buy Now Pay 
Later for orders under $50. Show all other payment methods."
Different payment methods for different customers:
let is_wholesale = input.cart.buyer_identity
    .as_ref()
    .and_then(|identity| identity.customer.as_ref())
    .map(|customer| customer.has_tags(vec!["wholesale"]))
    .unwrap_or(false);

if is_wholesale {
    // Only show Net 30 for wholesale customers
    let operations = input.payment_methods
        .iter()
        .filter_map(|method| {
            if !method.name.contains("Net 30") {
                Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }))
            } else {
                None
            }
        })
        .collect();
}
Prompt Example:
"For customers tagged 'wholesale', only show the Net 30 payment 
option. Hide all other payment methods for wholesale customers."
Payment restrictions based on cart contents:
let has_restricted_product = input.cart.lines
    .iter()
    .any(|line| {
        line.merchandise.product.has_tags(vec!["restricted"])
    });

if has_restricted_product {
    // No COD for restricted products
    let operations = input.payment_methods
        .iter()
        .filter_map(|method| {
            if method.name.contains("Cash on Delivery") {
                Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }))
            } else {
                None
            }
        })
        .collect();
}
Prompt Example:
"If cart contains any products tagged 'high-value', hide Cash 
on Delivery and Buy Now Pay Later options. Only allow credit 
card payments."
Different payment options by shipping address:
let country = input.cart.delivery_address
    .as_ref()
    .map(|addr| addr.country_code.as_str())
    .unwrap_or("");

let operations = input.payment_methods
    .iter()
    .filter_map(|method| {
        match (country, method.name.as_str()) {
            ("IN", name) if name.contains("Cash on Delivery") => None,
            (_, "Cash on Delivery") => {
                Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }))
            },
            _ => None,
        }
    })
    .collect();
Prompt Example:
"Only show Cash on Delivery for customers in India. Hide it 
for all other countries."

Operation Types

Payment customization functions support three operations:
  • Hide
  • Move
  • Rename
Remove a payment method from the list:
Operation::Hide(HideOperation {
    payment_method_id: method.id.clone(),
})
Use when:
  • Payment method not allowed
  • Condition not met
  • Customer segment restriction

Testing Payment Functions

1

Test in Checkout

  1. Add items to cart
  2. Go to checkout
  3. Proceed to payment methods
  4. Verify correct methods appear
2

Test Different Scenarios

  • Different cart values
  • Various customer types
  • Multiple product combinations
  • Different shipping addresses
3

Check Function Logs

shopify app function logs
View function input/output
4

Verify Performance

  • Functions should execute in under 5ms
  • Test with many payment methods
  • Check with large carts

Best Practices

Always Show Some Method

Never hide all payment methods
// Ensure at least one method remains
let visible_count = input.payment_methods.len() 
    - hide_operations.len();

if visible_count == 0 {
    // Don't hide everything
    return Ok(FunctionRunResult {
        operations: vec![],
    });
}

Clear Naming

Make renamed methods descriptive
// Good: Clear fee structure
name: "Credit Card (3% fee)"

// Bad: Vague
name: "Credit Card (extra charge)"

Fallback Logic

Handle missing data gracefully
let cart_total = input.cart.cost.subtotal_amount.amount
    .parse::<f64>()
    .unwrap_or(0.0); // Default to 0

Performance

Keep functions fast
  • Minimize iterations
  • Use Rust for speed
  • Cache calculations
  • Limit query surface

Advanced Examples

let operations = input.payment_methods
    .iter()
    .map(|method| {
        let fee_percentage = match method.name.as_str() {
            name if name.contains("Credit Card") => 2.9,
            name if name.contains("PayPal") => 3.5,
            _ => 0.0,
        };
        
        if fee_percentage > 0.0 {
            let fee_amount = cart_total * (fee_percentage / 100.0);
            Operation::Rename(RenameOperation {
                payment_method_id: method.id.clone(),
                name: format!(
                    "{} (+${:.2} processing fee)",
                    method.name,
                    fee_amount
                ),
            })
        } else {
            Operation::Rename(RenameOperation {
                payment_method_id: method.id.clone(),
                name: method.name.clone(),
            })
        }
    })
    .collect();
let customer_tier = input.cart.buyer_identity
    .as_ref()
    .and_then(|id| id.customer.as_ref())
    .and_then(|c| {
        if c.has_tags(vec!["tier-gold"]) { Some(3) }
        else if c.has_tags(vec!["tier-silver"]) { Some(2) }
        else if c.has_tags(vec!["tier-bronze"]) { Some(1) }
        else { Some(0) }
    })
    .unwrap_or(0);

let mut operations = vec![];

// Move preferred methods to top based on tier
if customer_tier >= 2 {
    if let Some(express) = input.payment_methods.iter()
        .find(|m| m.name.contains("Express")) {
        operations.push(Operation::Move(MoveOperation {
            payment_method_id: express.id.clone(),
            index: 0,
        }));
    }
}
let cart_total = parse_amount(&input.cart.cost.subtotal_amount.amount);
let is_international = input.cart.delivery_address
    .as_ref()
    .map(|addr| addr.country_code != "US")
    .unwrap_or(false);
let has_subscription = input.cart.lines.iter()
    .any(|line| line.merchandise.product.has_tags(vec!["subscription"]));

let operations = input.payment_methods
    .iter()
    .filter_map(|method| {
        // Complex rules
        if method.name.contains("Cash on Delivery") {
            if is_international || cart_total > 200.0 || has_subscription {
                return Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }));
            }
        }
        
        if method.name.contains("Buy Now Pay Later") {
            if cart_total < 50.0 || has_subscription {
                return Some(Operation::Hide(HideOperation {
                    payment_method_id: method.id.clone(),
                }));
            }
        }
        
        None
    })
    .collect();

Example Prompts

Create a payment customization function that hides Cash on 
Delivery for any orders shipping outside the United States. 
Check the shipping address country code.
For customers tagged 'vip', move the Express Checkout payment 
method to the top of the list. Keep all other methods in their 
default order.
Rename payment methods to show processing fees: Credit Card 
shows +2.9%, PayPal shows +3.5%, and Bank Transfer shows +'Free'. 
Display the fee percentage after the method name.
If cart contains any products tagged 'subscription', only allow 
Credit Card and Bank Transfer payment methods. Hide all other 
payment options including COD and Buy Now Pay Later.

Troubleshooting

Cause: Function hiding all payment methodsSolution: Add safeguard to ensure at least one method remains visible
Cause: Name matching logic incorrectSolution: Check exact payment method name, use contains() or regex
Cause: Function not activatedSolution: Payment functions activate automatically on deployment, check logs

Next Steps