Developer Filters
Documented Split Pay WordPress extension points for integrations, routing, product settings, metadata, vendor automation, and refund-debt recovery.
Overview#
Split Pay exposes a set of WordPress filters that allow developers to programmatically modify transfer settings at runtime. These filters are useful for dynamic transfer logic, custom product types, multivendor scenarios, and any case where the admin UI settings are not sufficient.
All filters follow standard WordPress conventions. Add them in your theme's functions.php, a custom plugin, or a code snippets plugin.
Core integration, global-setting, and applicable runtime hooks are available in both Free and PRO. Hooks attached to PRO-only product, variation, FluentCart product-editor, or Vendor Automation features require that feature to be active.
Integration architecture hooks 3.7.0+#
The 3.7.0 modular adapter architecture introduced hooks for registering additional platform/gateway integrations, plus several FluentCart transfer-time hooks. Use the registry hooks when building an adapter; use the transfer-time hooks only for the integrations identified below.
spp_register_integrations (action) 3.7.0+#
Fires once at plugins_loaded with the shared IntegrationRegistry. Use it from third-party code to register additional integrations alongside the bundled WooCommerce and FluentCart adapters.
add_action( 'spp_register_integrations', function( $registry ) {
if ( class_exists( 'My_Plugin\\Integration\\MyPlatformAdapter' ) ) {
$registry->register( new \My_Plugin\Integration\MyPlatformAdapter() );
}
} );
spp_integrations (filter) 3.7.0+#
Filter the populated IntegrationRegistry after the action above runs. Lets you inspect or substitute the full set of integrations Split Pay will use.
add_filter( 'spp_integrations', function( $registry ) {
return $registry;
} );
spp_product_data_tab_classes (filter) 3.7.0+#
Filter the WooCommerce product-tab visibility classes for the Split Pay product data tab. By default the tab appears for show_if_simple and show_if_variable products. Add additional classes to opt other product types in. The second argument is the Split Pay object that owns the product-tab UI, or null when the recovery panel checks the same visibility rules.
add_filter( 'spp_product_data_tab_classes', function( $classes, $context ) {
$classes[] = 'show_if_subscription'; // Add for WC Subscriptions products
$classes[] = 'show_if_variable-subscription';
return $classes;
}, 10, 2 );
spp_before_process_transfers (action) 3.7.0+#
FluentCart only. Fires immediately before FluentCart starts processing a transfer set. Receives the FluentCart order adapter, the 'fluentcart' integration slug, and the Stripe charge ID. This action is useful for logging or auditing; it does not provide a return value that can cancel processing.
add_action( 'spp_before_process_transfers', function( $orderAdapter, $integration, $chargeId ) {
error_log( "[SPP] About to process transfers for {$integration} order {$orderAdapter->getOrderId()} (charge {$chargeId})" );
}, 10, 3 );
spp_after_process_transfers (action) 3.7.0+#
FluentCart only. Fires when FluentCart reaches the normal end of a transfer attempt. It is not guaranteed to fire after an early return or exception. It receives the same parameters as the “before” action and can be used for post-attempt auditing or analytics.
add_action( 'spp_after_process_transfers', function( $orderAdapter, $integration, $chargeId ) {
do_action( 'my_analytics/track', 'split_pay_transfer_completed', [
'integration' => $integration,
'order_id' => $orderAdapter->getOrderId(),
'charge_id' => $chargeId,
] );
}, 10, 3 );
spp_fc_order_transferable_items (filter) 3.7.0+#
FluentCart-only. Filter the array of TransferableItem objects returned by FCOrderAdapter::getTransferableItems(). For example, this excludes one FluentCart product ID:
add_filter( 'spp_fc_order_transferable_items', function( $items, $order ) {
return array_values( array_filter( $items, function( $item ) {
return (int) $item->product_id !== 123;
} ) );
}, 10, 2 );
spp_transfer_metadata (filter) 3.7.0+#
FluentCart only. Filters metadata for every prepared FluentCart transfer leg. It receives the metadata array, null for the order argument, and the 'fluentcart' integration slug.
add_filter( 'spp_transfer_metadata', function( $metadata, $order, $integration ) {
$metadata['source_integration'] = $integration;
return $metadata;
}, 10, 3 );
WooCommerce runtime routing filters 3.8.0+#
These filters run while Split Pay builds a WooCommerce transfer plan, before any Stripe transfer is requested. Preserve the complete object or row you receive and test the resulting order plan in Test mode; malformed routing can stop the order before transfer.
spp_order_items (filter) 3.8.0+#
WooCommerce only. Filters the full array of TransferableItem objects after ordinary order lines and configured positive order-fee items are prepared. A marketplace adapter can append a new item or add a recipient to an existing item without changing saved product metadata. Keep each item’s connected_accounts, transfer_types, transfer_percentages, and transfer_amounts arrays index-aligned. Returning a non-array stops transfer preparation.
add_filter( 'spp_order_items', function( $items, $order ) {
// Return the complete TransferableItem array.
return $items;
}, 10, 2 );
spp_product_recipient_transfer (filter) 3.8.0+#
WooCommerce only. Filters one calculated product-recipient row immediately before it joins the transfer plan. It receives the complete row, its TransferableItem, the recipient index, and the global settings including stripe_mode. Return the complete row. A result missing connected_acc_id or transfer_amount is ignored in favor of the original; omitting other fields can remove calculation, logging, or refund context.
add_filter( 'spp_product_recipient_transfer', function( $row, $item, $index, $settings ) {
// Change only values supported by your verified routing rule.
return $row;
}, 10, 4 );
spp_wcfm_autolink_default_percent (filter) PRO 3.8.4+#
WCFM auto-linking only. Filters the vendor percentage assigned when Split Pay links a vendor product. It receives the resolved percentage, WCFM vendor user ID, and commission source (manual or wcfm_membership). Return a number above 0 and no more than 100; another value leaves the resolved percentage unchanged.
add_filter( 'spp_wcfm_autolink_default_percent', function( $percentage, $vendor_id, $source ) {
return 85;
}, 10, 3 );
spp_debt_recovery_max_fraction (filter) PRO#
Caps the fraction of one otherwise payable transfer that optional refund-debt recovery may withhold. It receives the default fraction 1.0, connected-account ID, lowercase currency, and current transfer amount in that currency’s minor units. Split Pay clamps the returned value to 0.0–1.0, then rounds the resulting minor-unit cap down. This does not change the debt balance itself.
add_filter( 'spp_debt_recovery_max_fraction', function( $fraction, $account_id, $currency, $transfer_minor ) {
return 0.5; // Withhold at most 50% of this transfer.
}, 10, 4 );
Vendor onboarding & automation hooks 3.8.0#
Added in 3.8.0 for the Vendor Automation feature set. These actions and filters fire around vendor Stripe onboarding and the auto-created vendor products, QR codes, transaction emails, and order-received message. They are all opt-in alongside the features that use them.
spp_vendor_onboarding_completed (action) 3.8.0#
Fires when a vendor completes Stripe Connect onboarding — from either the front-end [split_pay_vendor_connect] flow (verified against the Stripe API on return) or the admin-side vendor dashboard flow. This is the hook Automatic Vendor Products listens to; use it for your own post-onboarding automation (welcome emails, role changes, CRM sync).
do_action( 'spp_vendor_onboarding_completed', int $user_id, string $account_id, string $mode );
Parameters: $user_id (the vendor’s WordPress user id), $account_id (the connected Stripe account id, acct_...), $mode ('test' or 'live').
spp_vendor_disconnected (action) 3.8.0#
Fires when a vendor disconnects their Stripe account from the vendor dashboard. The mode is 'test' or 'live'.
do_action( 'spp_vendor_disconnected', int $user_id, string $account_id, string $mode );
spp_vendor_product_ready (action) 3.8.0#
Fires after a vendor’s auto product has been created or updated (re-onboard/reconnect) and assigned to their connected account.
do_action( 'spp_vendor_product_ready', int $product_id, int $user_id, string $account_id, string $mode );
spp_vendor_product_args (filter) 3.8.0#
Customize the auto-created vendor product before it is saved. Available keys: title, status, catalog_visibility, virtual, sold_individually, price.
add_filter( 'spp_vendor_product_args', function ( array $args, int $user_id, string $mode ) {
$args['title'] = 'Tip ' . get_userdata( $user_id )->display_name;
$args['catalog_visibility'] = 'visible'; // show in the shop catalog
return $args;
}, 10, 3 );
spp_apply_nyp_meta (filter) 3.8.0#
Control the name-your-price meta stamped on auto vendor products. Return an empty array to skip NYP entirely, or the exact meta_key => value pairs your NYP plugin expects. $detected is the auto-detected plugin slug ('wpc-name-your-price', 'wc-name-your-price', or '' when none was found).
add_filter( 'spp_apply_nyp_meta', function ( array $meta, int $product_id, string $detected ) {
// Example: a custom NYP plugin
return [ '_my_nyp_enabled' => 'yes', '_my_nyp_min' => '1' ];
}, 10, 3 );
spp_success_page_message (filter) 3.8.0#
Adjust the final custom order-received message after placeholders are replaced.
add_filter( 'spp_success_page_message', function ( string $message, WC_Order $order, string $vendor_account ) {
return $message . ' <a href="/receipt?order=' . $order->get_id() . '">View receipt</a>';
}, 10, 3 );
spp_transfer_succeeded (action) 3.7.5+#
Normally fires once after a Stripe transfer succeeds. It also fires when vendor-debt recovery fully withholds a leg, in which case no Stripe transfer is created and the payload contains amount 0 and an empty transfer_id. The vendor and customer transaction emails listen to this hook. Every payload contains order_id, destination, amount (in Stripe minor units), and transfer_id.
add_action( 'spp_transfer_succeeded', function( $data ) {
// $data['order_id'], $data['destination'], $data['amount'], $data['transfer_id']
error_log( "[SPP] transfer {$data['transfer_id']} to {$data['destination']}" );
}, 10, 1 );
spp_account_create_args (filter) 3.7.5+#
Customize the Stripe Accounts v1 create arguments used during vendor onboarding. The second parameter is 'shortcode', 'connect_link', 'connect_link_generic', or 'admin'. Supported Accounts v1 type or controller arguments can replace the Standard/Express default; this filter does not call the Accounts v2 /v2/core/accounts endpoint.
add_filter( 'spp_account_create_args', function( $args, $context ) {
return $args;
}, 10, 2 );
Global filters#
These filters modify the global transfer settings that apply to all products without product-level overrides.
spp_get_stored_accounts#
Filter the raw locally stored connected-account rows available to admin selectors. Each row uses the keys bsd_sat_id, bsd_account_id, bsd_account_name, bsd_account_email, and bsd_account_mode. The second argument identifies the selector context. Filter existing synchronized rows; adding an arbitrary account here does not connect or verify it with Stripe.
add_filter( 'spp_get_stored_accounts', function( $accounts, $level ) {
// Hide one already-synchronized account from admin selectors.
$accounts = array_filter( $accounts, function( $account ) {
return ( $account['bsd_account_id'] ?? '' ) !== 'acct_ACCOUNT_TO_HIDE';
} );
return array_values( $accounts );
}, 10, 2 );
spp_global_connect_id_settings#
Filter the array of global recipient rows immediately before it is saved. Each row uses account_id, bsd_spscwt_type ('percentage' or 'amount'), percentage_or_amount, bsd_global_shipping_type, and bsd_global_shipping_percentage_amount; an existing row can also contain its database id. Modify complete rows and return the full row array.
add_filter( 'spp_global_connect_id_settings', function( $rows ) {
foreach ( $rows as &$row ) {
if ( ( $row['account_id'] ?? '' ) === 'acct_VENDOR_ABC' ) {
$row['bsd_spscwt_type'] = 'percentage';
$row['percentage_or_amount'] = 15;
}
}
unset( $row );
return $rows;
} );
Product-level filters#
These filters modify transfer settings for individual products. Each filter receives a row-aligned array and the product ID ($post_id). The same numeric key identifies one recipient across the account, type, value, and shipping arrays, so callbacks must preserve that alignment and return an array.
Platform-agnostic since 3.7.0. Each spp_before_save_product_* filter listed below now also fires from the FluentCart product save path (FCProductAdapter::saveProductSplitPaySettings()) in addition to the WooCommerce save path. The same callback runs for both platforms, so you can write platform-agnostic logic.
spp_before_save_product_accounts#
Filter the connected account ID(s) assigned to a product.
add_filter( 'spp_before_save_product_accounts', function( $accounts, $post_id ) {
// Route all products in category "electronics" to a specific vendor
if ( has_term( 'electronics', 'product_cat', $post_id ) && isset( $accounts[0] ) ) {
$accounts[0] = 'acct_ELECTRONICS_VENDOR';
}
return $accounts;
}, 10, 2 );
spp_before_save_product_types#
Filter the transfer-type array for a product. Each row value is 'percentage' or 'amount'.
add_filter( 'spp_before_save_product_types', function( $types, $post_id ) {
// WooCommerce-only example: force virtual products to use fixed transfers.
if ( ! function_exists( 'wc_get_product' ) ) {
return $types;
}
$product = wc_get_product( $post_id );
if ( $product && $product->is_virtual() ) {
foreach ( array_keys( $types ) as $row ) {
$types[ $row ] = 'amount';
}
}
return $types;
}, 10, 2 );
spp_before_save_product_transfer_percentage#
Filter the row-aligned transfer-percentage array for a product.
add_filter( 'spp_before_save_product_transfer_percentage', function( $percentages, $post_id ) {
// Give premium vendors a higher split
$vendor_tier = get_post_meta( $post_id, '_vendor_tier', true );
if ( $vendor_tier === 'premium' ) {
foreach ( array_keys( $percentages ) as $row ) {
$percentages[ $row ] = 25;
}
}
return $percentages;
}, 10, 2 );
spp_before_save_product_transfer_amount#
Filter the row-aligned fixed-transfer-amount array for a product.
add_filter( 'spp_before_save_product_transfer_amount', function( $amounts, $post_id ) {
// Set a minimum fixed amount of $5 on each configured row.
foreach ( $amounts as $row => $amount ) {
if ( (float) $amount < 5 ) {
$amounts[ $row ] = 5;
}
}
return $amounts;
}, 10, 2 );
spp_before_save_product_shipping_transfer_type#
Filter the row-aligned shipping-type array for a product. Each configured value is 'percentage' or 'amount'.
add_filter( 'spp_before_save_product_shipping_transfer_type', function( $types, $post_id ) {
foreach ( array_keys( $types ) as $row ) {
$types[ $row ] = 'percentage';
}
return $types;
}, 10, 2 );
spp_before_save_product_shipping_transfer_percentage#
Filter the row-aligned shipping-percentage array for a product.
add_filter( 'spp_before_save_product_shipping_transfer_percentage', function( $percentages, $post_id ) {
foreach ( array_keys( $percentages ) as $row ) {
$percentages[ $row ] = 50;
}
return $percentages;
}, 10, 2 );
spp_before_save_product_shipping_transfer_amount#
Filter the row-aligned fixed-shipping-amount array for a product.
add_filter( 'spp_before_save_product_shipping_transfer_amount', function( $amounts, $post_id ) {
foreach ( array_keys( $amounts ) as $row ) {
$amounts[ $row ] = 3.00;
}
return $amounts;
}, 10, 2 );
Variable product filters#
These filters follow the same row-aligned array contract as the product-level filters but apply to individual product variations. They use the variable_product prefix in the filter name and receive the variation's post ID.
Available variable product filters#
| Filter Name | Description |
|---|---|
spp_before_save_variable_product_accounts |
Connected account(s) for a variation |
spp_before_save_variable_product_types |
Transfer type for a variation |
spp_before_save_variable_product_transfer_percentage |
Transfer percentage for a variation |
spp_before_save_variable_product_transfer_amount |
Fixed transfer amount for a variation |
spp_before_save_variable_product_shipping_transfer_type |
Shipping transfer type for a variation |
spp_before_save_variable_product_shipping_transfer_percentage |
Shipping transfer percentage for a variation |
spp_before_save_variable_product_shipping_transfer_amount |
Fixed shipping transfer amount for a variation |
Usage follows the same pattern as product-level filters:
add_filter( 'spp_before_save_variable_product_transfer_percentage', function( $percentages, $variation_id ) {
// Variation-specific logic
$variation = wc_get_product( $variation_id );
$size = $variation->get_attribute( 'size' );
if ( $size === 'XL' || $size === 'XXL' ) {
foreach ( array_keys( $percentages ) as $row ) {
$percentages[ $row ] = 20; // Higher split for larger sizes
}
}
return $percentages;
}, 10, 2 );
Transfer-time metadata filters#
These filters run at the moment a Stripe Transfer is created, allowing you to modify the metadata array attached to each transfer. This is useful for adding custom tracking data, order details, or vendor-specific information to Stripe's transfer metadata.
Transfer metadata filters run while Split Pay prepares a transfer for Stripe. The initiating payment-complete path depends on the active gateway and is not limited to a specific webhook event. These filters do not run during admin settings saves.
spp_global_account_wise_meta_data#
Filter the metadata attached to global transfers. On WooCommerce this covers global merchandise transfers. FluentCart currently calls the same filter for both global merchandise and global shipping legs. The filter receives the metadata array, the originating store order ID, and the connected account ID.
add_filter( 'spp_global_account_wise_meta_data', function( $meta_data, $order_id, $account_id ) {
// Add custom tracking data to global transfers.
// $order_id is a store order ID (WC or FC) - look it up via the
// appropriate API for your platform if you need the order object.
$meta_data['vendor_region'] = get_user_meta( get_current_user_id(), 'region', true );
$meta_data['internal_ref'] = 'GLOBAL-' . $order_id;
return $meta_data;
}, 10, 3 );
spp_product_wise_meta_data#
Filter the metadata attached to product-level transfers. On WooCommerce this covers product merchandise transfers. FluentCart currently calls the same filter for both product merchandise and product shipping legs. The filter receives the metadata array, the originating store order ID, and the connected account ID.
add_filter( 'spp_product_wise_meta_data', function( $meta_data, $order_id, $account_id ) {
// Include the product name in the transfer metadata.
// wc_get_product() is WC-specific; on FluentCart use the
// platform's product accessor.
// WooCommerce supplies product_id when transfer metadata is enabled.
if ( isset( $meta_data['product_id'] ) && function_exists( 'wc_get_product' ) ) {
$product = wc_get_product( $meta_data['product_id'] );
if ( $product ) {
$meta_data['Product Name'] = $product->get_name();
}
}
return $meta_data;
}, 10, 3 );
Shipping transfer metadata filters#
These WooCommerce-only filters apply to shipping fee transfers. FluentCart shipping legs use spp_global_account_wise_meta_data or spp_product_wise_meta_data instead:
| Filter Name | Description |
|---|---|
spp_shipping_transfer_meta_data |
Metadata for global shipping transfers (account-level) |
spp_global_shipping_transfer_meta_data |
Metadata for global shipping transfers (percentage/fixed) |
spp_product_wise_shipping_transfer_meta_data |
Metadata for product-level shipping transfers |
All shipping metadata filters receive the same three parameters: $meta_data, $order_id, and $account_id.
add_filter( 'spp_shipping_transfer_meta_data', function( $meta_data, $order_id, $account_id ) {
$meta_data['shipping_carrier'] = 'USPS';
return $meta_data;
}, 10, 3 );
Documented extension-point reference#
This table lists the extension points with public behavior documented on this page. Split Pay also uses internal WordPress hooks that are not documented here as public integration contracts.
| Filter Name | When it Fires | Parameters |
|---|---|---|
spp_register_integrations (action) | plugins_loaded — integration registry boot (3.7.0+) | $registry |
spp_integrations | plugins_loaded — after registry is populated (3.7.0+) | $registry |
spp_product_data_tab_classes | WooCommerce product edit screen — tab visibility (3.7.0+) | $classes, $context |
spp_before_process_transfers (action) | FluentCart transfer time — before processing begins (3.7.0+) | $orderAdapter, $integration, $chargeId |
spp_after_process_transfers (action) | FluentCart transfer time — normal end of an attempt (3.7.0+) | $orderAdapter, $integration, $chargeId |
spp_fc_order_transferable_items | FluentCart — reading transferable line items (3.7.0+) | $items, $order |
spp_get_stored_accounts | Admin UI — account dropdowns | $accounts, $level |
spp_global_connect_id_settings | Admin — saving global settings | $rows |
spp_before_save_product_accounts | Admin — saving product | $accounts, $post_id |
spp_before_save_product_types | Admin — saving product | $types, $post_id |
spp_before_save_product_transfer_percentage | Admin — saving product | $percentages, $post_id |
spp_before_save_product_transfer_amount | Admin — saving product | $amounts, $post_id |
spp_before_save_product_shipping_transfer_type | Admin — saving product | $types, $post_id |
spp_before_save_product_shipping_transfer_percentage | Admin — saving product | $percentages, $post_id |
spp_before_save_product_shipping_transfer_amount | Admin — saving product | $amounts, $post_id |
spp_before_save_variable_product_accounts | Admin — saving variation | $accounts, $variation_id |
spp_before_save_variable_product_types | Admin — saving variation | $types, $variation_id |
spp_before_save_variable_product_transfer_percentage | Admin — saving variation | $percentages, $variation_id |
spp_before_save_variable_product_transfer_amount | Admin — saving variation | $amounts, $variation_id |
spp_before_save_variable_product_shipping_transfer_type | Admin — saving variation | $types, $variation_id |
spp_before_save_variable_product_shipping_transfer_percentage | Admin — saving variation | $percentages, $variation_id |
spp_before_save_variable_product_shipping_transfer_amount | Admin — saving variation | $amounts, $variation_id |
spp_global_account_wise_meta_data | Transfer time — global transfers | $meta_data, $order_id, $account_id |
spp_product_wise_meta_data | Transfer time — product transfers | $meta_data, $order_id, $account_id |
spp_shipping_transfer_meta_data | WooCommerce transfer time — percentage global shipping | $meta_data, $order_id, $account_id |
spp_global_shipping_transfer_meta_data | WooCommerce transfer time — fixed global shipping | $meta_data, $order_id, $account_id |
spp_product_wise_shipping_transfer_meta_data | WooCommerce transfer time — product shipping | $meta_data, $order_id, $account_id |
spp_transfer_metadata | FluentCart transfer time — every prepared transfer leg | $metadata, $order, $integration |
spp_order_items | WooCommerce — after transferable items are prepared (3.8.0+) | $items, $order |
spp_product_recipient_transfer | WooCommerce — after one product-recipient row is calculated (3.8.0+) | $row, $item, $index, $settings |
spp_wcfm_autolink_default_percent | PRO WCFM auto-link — resolving the percentage for a newly linked product (3.8.4+) | $percentage, $vendor_id, $source |
spp_debt_recovery_max_fraction | PRO refund-debt recovery — before reserving a deduction | $fraction, $account_id, $currency, $transfer_minor |
spp_vendor_onboarding_completed (action) | Vendor completes Stripe onboarding (3.8.0) | $user_id, $account_id, $mode |
spp_vendor_disconnected (action) | Vendor disconnects Stripe account (3.8.0) | $user_id, $account_id, $mode |
spp_vendor_product_ready (action) | Auto vendor product created/updated (3.8.0) | $product_id, $user_id, $account_id, $mode |
spp_vendor_product_args | Building the auto vendor product (3.8.0) | $args, $user_id, $mode |
spp_apply_nyp_meta | Stamping name-your-price meta on auto products (3.8.0) | $meta, $product_id, $detected |
spp_success_page_message | Order-received page — split-pay orders (3.8.0) | $message, $order, $vendor_account |
spp_transfer_succeeded (action) | After a successful transfer or a fully debt-withheld leg (3.7.5+) | $data |
spp_account_create_args | Vendor onboarding — Stripe Accounts v1 creation (3.7.5+) | $args, $context |
Best practices#
- Always return a value — Every filter callback must return the filtered value, even if unchanged.
- Test in test mode — Use Stripe test mode to verify your filters produce the correct transfer amounts before going live.
- Use a custom plugin — Placing filter code in a separate plugin (rather than
functions.php) ensures it persists across theme changes. - Log for debugging — Use
error_log()within your filter callbacks to trace values during development. - Check priority — If multiple filters modify the same value, use the priority parameter (default: 10) to control execution order.
Filters that set transfer amounts too high can result in transfer errors. Keep the total of all transfers within the captured source charge amount. Stripe processing fees remain the platform’s responsibility and are not subtracted from that transfer ceiling.