Orchestrate
Dynamic Funding

Dynamic Funding

Learn how to split payment funds across multiple processing accounts for marketplace and platform payment flows


Dynamic funding extends automated funding by allowing you to split funds from a single payment transaction across multiple processing accounts. This enables complex payment routing scenarios such as commission splitting, multi-vendor payments, and platform fee collection, all within a single payment operation.

Prerequisites

Before implementing dynamic funding, it's helpful to learn about the following topics.


When to Use Dynamic Funding


Use dynamic funding when you need to split payment funds from a single transaction across multiple processing accounts, rather than manually creating separate transfers after the fact. This is essential for platforms and marketplaces that need to automatically route funds to multiple parties at the time of payment collection.

Common use cases

  • Multi-Vendor Orders: Distribute a single customer payment across multiple vendors based on cart contents, with each vendor receiving their portion of the order total.
  • Revenue Sharing: Implement revenue sharing models where multiple parties receive a percentage of each transaction, such as referral partners, service providers, and platform operators.
  • Platform Fees: Automatically deduct platform fees, processing fees, or service charges from payments before routing the remaining funds to the primary recipient.
  • Legal Trust Accounting: Split legal payments between an IOLTA trust account for reserve funds and an operating account for earned fees, ensuring compliance with attorney trust account regulations.
  • Franchise Payments: Automatically split payments between the franchisee's operating account and the franchisor's account based on royalty agreements, with each party receiving their designated percentage.

How Dynamic Funding Works


Dynamic funding uses the transfers array on a payment transaction to specify how funds should be split across multiple processing accounts. Each transfer in the array defines an amount and a destination account.

Transfer Amount Notation

Transfer amounts use a specific notation to indicate how funds flow:

  • Negative amounts (-90) indicate funds being deducted from the payment and sent to the specified account
  • The sum of all negative transfer amounts must not exceed the total payment amount
  • Transfer amounts represent the actual dollar values being moved, not percentages

Basic Flow Example

Here's how a $100 payment with dynamic funding works:

Customer Initiates Payment

Customer initiates a $100 payment using their card. The payment is processed and authorized.

Define Transfer Split

The transfers array specifies how to split the funds:

  • Transfer 1: -$90 to Merchant Processing Account
  • Transfer 2: -$10 to Platform Processing Account

Create Funding Transactions

Payload creates corresponding funding transactions:

  • Deposit of $90 on the merchant's processing account
  • Deposit of $10 on the platform's processing account

Distribute Funds

Each processing account receives funds according to its funding configuration.

Creating Payments with Dynamic Funding


To split payment funds across multiple accounts, include the transfers array in your payment transaction request. Each transfer specifies an amount and a receiver account.

Split payment between merchant and platform

Collect a customer payment and automatically split funds between the merchant and platform commission.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Create a payment with dynamic funding split
transaction = pl.Transaction.create(
    type='payment',
    amount=100.00,
    sender={
        'method': {
            'type': 'card',
            'card': {
                'card_number': '4111111111111111',
                'expiry': '12/35',
                'card_code': '123'
            },
            'billing_address': {
                'postal_code': '11111'
            }
        }
    },
    receiver={
        'account_id': 'acct_processing123'
    },
    transfers=[
        {
            'amount': -90.00,
            'receiver': {'account_id': 'acct_processing123'}
        },
        {
            'amount': -10.00,
            'receiver': {'account_id': 'acct_platform456'}
        }
    ]
)
 
print(f"Payment created: {transaction.id}")
print(f"Total amount: ${transaction.amount}")
print(f"Transfers: {len(transaction.transfers)}")
print(f"Merchant receives: $90")
print(f"Platform receives: $10")

This example creates a $100 payment with dynamic funding:

  • type='payment' initiates the payment collection
  • sender.method provides inline card details (or use sender.method_id for saved cards)
  • receiver.account_id specifies the primary processing account
  • transfers array splits the funds:
    • -$90 to the merchant processing account
    • -$10 to the platform processing account as commission

The system automatically creates two deposit funding transactions, one for $90 to the merchant and one for $10 to the platform.

Split payment across multiple vendors

Distribute a single payment across three different vendor accounts based on order contents.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Create a payment split across three vendors
transaction = pl.Transaction.create(
    type='payment',
    amount=150.00,
    sender={
        'method': {
            'type': 'card',
            'card': {
                'card_number': '4111111111111111',
                'expiry': '12/35',
                'card_code': '123'
            },
            'billing_address': {
                'postal_code': '11111'
            }
        }
    },
    receiver={
        'account_id': 'acct_marketplace'
    },
    transfers=[
        {
            'amount': -50.00,
            'receiver': {'account_id': 'acct_vendor1'}
        },
        {
            'amount': -60.00,
            'receiver': {'account_id': 'acct_vendor2'}
        },
        {
            'amount': -40.00,
            'receiver': {'account_id': 'acct_vendor3'}
        }
    ]
)
 
print(f"Multi-vendor payment created: {transaction.id}")
print(f"Total amount: ${transaction.amount}")
print(f"Vendor 1 receives: $50")
print(f"Vendor 2 receives: $60")
print(f"Vendor 3 receives: $40")

This example splits a $150 payment across three vendors:

  • transfers array contains three entries
  • Each vendor receives their portion: $50, $60, and $40
  • Total transfers ($150) equals the payment amount
  • Each vendor's processing account receives a separate deposit funding transaction

This pattern is common for marketplace carts where a customer purchases from multiple vendors in a single checkout.

Deduct platform fee and route remaining funds

Collect a payment, deduct a platform fee, and send the remainder to the merchant.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Create a payment with platform fee deduction
transaction = pl.Transaction.create(
    type='payment',
    amount=200.00,
    sender={
        'method': {
            'type': 'card',
            'card': {
                'card_number': '4111111111111111',
                'expiry': '12/35',
                'card_code': '123'
            },
            'billing_address': {
                'postal_code': '11111'
            }
        }
    },
    receiver={
        'account_id': 'acct_processing123'
    },
    transfers=[
        {
            'amount': -185.00,
            'receiver': {'account_id': 'acct_merchant'}
        },
        {
            'amount': -15.00,
            'receiver': {'account_id': 'acct_platform'}
        }
    ]
)
 
print(f"Payment with platform fee created: {transaction.id}")
print(f"Total payment: ${transaction.amount}")
print(f"Merchant receives: $185.00")
print(f"Platform fee: $15.00")

This example processes a $200 payment with platform fee:

  • Customer pays $200
  • Platform deducts $15 processing fee
  • Merchant receives $185
  • Both amounts are specified as negative transfers
  • Each account's funding happens according to its funding configuration

This approach ensures transparent fee collection while maintaining clean accounting for both parties.

Calculate and split based on percentages

Split payment dynamically based on percentage allocations calculated in your application.

import payload
pl = payload.Session('secret_key_3bW9...', api_version='v2')
 
# Calculate percentage-based split
total_amount = 500.00
primary_percentage = 0.85  # 85%
secondary_percentage = 0.15  # 15%
 
# Calculate amounts
primary_amount = total_amount * primary_percentage
secondary_amount = total_amount * secondary_percentage
 
# Create payment with calculated split
transaction = pl.Transaction.create(
    type='payment',
    amount=total_amount,
    sender={
        'method': {
            'type': 'card',
            'card': {
                'card_number': '4111111111111111',
                'expiry': '12/35',
                'card_code': '123'
            },
            'billing_address': {
                'postal_code': '11111'
            }
        }
    },
    receiver={
        'account_id': 'acct_processing123'
    },
    transfers=[
        {
            'amount': -primary_amount,
            'receiver': {'account_id': 'acct_primary'}
        },
        {
            'amount': -secondary_amount,
            'receiver': {'account_id': 'acct_secondary'}
        }
    ]
)
 
print(f"Percentage-based split payment created: {transaction.id}")
print(f"Total amount: ${total_amount}")
print(f"Primary (85%): ${primary_amount}")
print(f"Secondary (15%): ${secondary_amount}")

This example demonstrates percentage-based splitting:

  • Total payment is $500
  • Calculate split amounts in your application logic
  • Primary party receives 85% ($425)
  • Secondary party receives 15% ($75)
  • Pass calculated amounts as negative transfers

Transfer Objects and Relationships


Each transfer in the transfers array creates a corresponding Transfer object that tracks the fund movement between accounts. These Transfer objects are associated with the associated transaction and provide detailed tracking of how funds were split.

Transfer Object Properties

Key properties of Transfer objects created from dynamic funding:

  • amount - The transfer amount (negative values represent debits/outgoing funds)
  • sender.account_id - The account sending funds (typically the payment source)
  • receiver.account_id - The account receiving the funds
  • transaction_id - Reference to the parent payment transaction
  • assoc_transaction_id - Reference to the associated funding transaction created for this transfer
  • type - Automatically set based on the transfer context (typically "payment" or "deposit")

Querying Transfers

You can query transfers to understand how funds were split for a specific payment:

import payload
 
pl = payload.Session("secret_key_3bW9...", api_version="v2")
 
# Query transfers for a specific payment transaction
transfers = pl.Transfer.filter_by(
    transaction_id="txn_payment123",
).all()
 
print(f"Found {len(transfers)} transfers for payment:")
for transfer in transfers:
    print(f"  Transfer {transfer.id}:")
    print(f"    Amount: ${abs(transfer.amount)}")
    print(f"    Receiver: {transfer.receiver.account_id}")
    print(f"    Type: {transfer.type}")
    print(f"    Processed: {transfer.processed_date}")
 
# Calculate total
total = sum(abs(t.amount) for t in transfers)
print(f"\nTotal distributed: ${total}")

This retrieves all transfers associated with a specific payment transaction, showing exactly how funds were distributed across processing accounts.

Monitoring Dynamic Funding Events


Dynamic funding transactions trigger the same webhook events as standard automated funding, with additional transfer-specific events for tracking fund splits.

Webhook Events for Dynamic Funding

Monitor these webhook events to track dynamic funding operations:

  • payment - Triggered when the payment transaction is created
  • processed - Triggered when the payment completes successfully
  • deposit - Triggered for each deposit funding transaction created (one per transfer)
  • decline - Triggered if the payment or any funding transaction fails

Example webhook handler for dynamic funding:

from flask import Flask, request
 
app = Flask(__name__)
 
@app.route("/webhooks/payload", methods=["POST"])
def handle_webhook():
    webhook = request.json
 
    # Handle successful payment with dynamic funding
    if webhook["trigger"] == "processed" and webhook["triggered_on"]["type"] == "payment":
        transaction_id = webhook["triggered_on"]["id"]
 
        transaction = pl.Transaction.get(transaction_id)
 
        print("Payment processed:", transaction.id)
        print("Total amount:", transaction.amount)
        print("Number of transfers:", len(transaction.transfers) if transaction.transfers else 0)
 
    if webhook["trigger"] == "deposit":
        transaction_id = webhook["triggered_on"]["id"]
 
        transaction = pl.Transaction.get(transaction_id)
 
        print("Deposit funding created:", transaction.id)
        print("Amount deposited:", transaction.amount)
        print("Receiver account:", transaction.receiver["account_id"])
 
    return "OK", 200

Reconciling Split Payments

To reconcile a payment with dynamic funding:

  1. Query the payment transaction to get the total amount and transfers array
  2. Query associated transfers to get detailed transfer records
  3. Query deposit funding transactions for each processing account
  4. Verify that the sum of all deposit amounts equals the payment amount
import payload
 
pl = payload.Session("secret_key_3bW9...", api_version="v2")
 
# Step 1: Get the payment transaction
payment = pl.Transaction.get("txn_payment123")
 
print(f"Payment ID: {payment.id}")
print(f"Total amount: ${payment.amount}")
print(f"Number of transfers: {len(payment.transfers) if payment.transfers else 0}")
 
# Step 2: Query all transfers for this payment
transfers = pl.Transfer.filter_by(transaction_id=payment.id).all()
 
print("\nTransfer breakdown:")
total_transferred = 0
for transfer in transfers:
    amount = abs(transfer.amount)
    total_transferred += amount
    print(f"  ${amount} -> {transfer.receiver.account_id}")
 
# Step 3: Verify totals match
print(f"\nReconciliation:")
print(f"  Payment amount: ${payment.amount}")
print(f"  Total transferred: ${total_transferred}")
print(f"  Match: {'✓ Yes' if total_transferred == payment.amount else '✗ No'}")
 
# Step 4: Query deposit funding transactions for each receiver
account_ids = [t.receiver.account_id for t in transfers]
deposits = pl.Transaction.filter_by(
    type="deposit",
    receiver={"account_id": account_ids},
).all()
 
print(f"\nDeposit funding status:")
for deposit in deposits:
    print(
        f"  {deposit.id}: ${deposit.amount} -> {deposit.receiver.account_id} ({deposit.status})"
    )

This reconciliation approach ensures all funds were distributed correctly and helps with financial reporting and auditing.


Schema Reference

The following fields are particularly relevant for implementing dynamic funding:

Transaction Schema - Transfers Array

The transfers array on payment transactions controls how funds are split:

transfersTransfer
array
No description

Transfer Schema

Transfer objects created from the transfers array:

amount
number (double)Immutable
The monetary amount of this transfer. Positive amounts represent credits (funds received), while negative amounts represent debits (funds sent). This amount contributes to account balance calculations.
sender
object
Information about the sender party in this transfer. This includes the account from which funds are being sent or debited.
accountAccount
object
The expanded sender account object. When included, this provides the full details of the customer or processing account that is sending funds in this transfer.
Visibility: explicit
account_id ID of Account
stringImmutable
ID of the associated account object. (ID prefix: acct)
Pattern: ^acct_[A-Za-z0-9]+$
receiver
object
Information about the receiver party in this transfer. This includes the account to which funds are being sent or credited.
accountAccount
object
The expanded receiver account object. When included, this provides the full details of the customer or processing account that is receiving funds in this transfer.
Visibility: explicit
account_id ID of Account
stringImmutable
ID of the associated account object. (ID prefix: acct)
Pattern: ^acct_[A-Za-z0-9]+$
transaction_id ID of Transaction
string
ID of the transaction object (ID prefix: txn)
Pattern: ^txn_[A-Za-z0-9]+$
assoc_transaction_id ID of Transaction
string
ID of the associated transaction object. (ID prefix: txn)
Pattern: ^txn_[A-Za-z0-9]+$
Required if:
receiver=null
sender=null

Best Practices

Follow these best practices when implementing dynamic funding:

Validate Transfer Amounts

Always validate that the sum of all negative transfer amounts does not exceed the total payment amount before submitting the transaction. Implement client-side validation to catch errors early.

def validate_transfers(payment_amount, transfers):
    total_transfers = sum(abs(t["amount"]) for t in transfers)
 
    if total_transfers > payment_amount:
        raise ValueError(
            f"Transfer total (${total_transfers}) exceeds payment amount (${payment_amount})"
        )
 
    return True

Handle Rounding Errors

When calculating percentage-based splits, be careful with rounding. Ensure the sum of calculated amounts exactly matches the payment total.

import math
 
def split_percentage(amount, percentages):
    # Calculate each split, rounding down
    splits = [math.floor(amount * pct * 100) / 100 for pct in percentages]
 
    # Add any remainder to the first split to ensure exact total
    total = sum(splits)
    remainder = amount - total
    splits[0] += remainder
 
    return splits

Monitor Failed Transfers

Implement monitoring for failed dynamic funding transactions. If any transfer in the array fails, the entire payment may be declined. Alert on these failures and implement retry logic where appropriate.

Next Steps

Build comprehensive marketplace payment flows, manage complex revenue sharing, and create detailed financial reporting


Understand the Foundation of Dynamic Funding

Learn how Automated Funding works for processing accounts, configure funding payment methods with Default Payment Methods, and understand Processing Accounts capabilities.

Create Transactions with Dynamic Funding

Process payments with revenue splitting using Creating Payment Intents, understand transfer operations with Creating Transfers, and monitor the complete Payment Processing lifecycle and status.

Monitor and Reconcile Dynamic Funding

Track split funding and settlement with Reporting Overview, create custom revenue reports using Building Report Queries, and monitor fund distribution through Transfer Reports.


Related articles