Skip to main content
Payment splits let you distribute a single payment across multiple wallets. This is essential for marketplaces, platforms with revenue sharing, and any application where funds need to go to more than one party.

How Splits Work

When a payment with splits is confirmed, ZendFi automatically distributes the funds to each recipient’s wallet according to the split configuration.

Create a Payment with Splits

Include a split_recipients array when creating a payment:
const payment = await zendfi.createPayment({
  amount: 100,
  currency: 'USD',
  description: 'Marketplace order #1234',
  split_recipients: [
    {
      recipient_wallet: 'SellerWalletAddress111111111111111111111',
      recipient_name: 'Seller payout',
      percentage: 70,
    },
    {
      recipient_wallet: 'PlatformWalletAddress11111111111111111',
      recipient_name: 'Platform fee',
      percentage: 20,
    },
    {
      recipient_wallet: 'AffiliateWalletAddress1111111111111111',
      recipient_name: 'Affiliate commission',
      percentage: 10,
    },
  ],
});

Split Types

Each recipient gets a percentage of the total payment. All percentages must sum to 100.
split_recipients: [
  { recipient_wallet: '...', recipient_name: 'Seller', percentage: 80 },
  { recipient_wallet: '...', recipient_name: 'Platform', percentage: 20 },
]

Split Recipient Configuration

Each recipient in the split_recipients array accepts:
FieldTypeDescription
recipient_walletstringSolana wallet address to receive funds
recipient_namestring(Optional) Human-readable label for this recipient
percentagenumber(Optional) Percentage of the payment (0-100)
fixed_amount_usdnumber(Optional) Fixed amount in USD
split_ordernumber(Optional) Order in which splits are executed

Retrieve Splits

Get all splits for a payment

const response = await fetch(
  'https://api.zendfi.tech/api/v1/payments/pay_test_abc123/splits',
  { headers: { Authorization: `Bearer ${apiKey}` } }
);

const splits = await response.json();
Response:
{
  "data": [
    {
      "id": "split_abc123",
      "payment_id": "pay_test_abc123",
      "wallet_address": "SellerWallet...",
      "type": "percentage",
      "value": 70,
      "amount": 70.00,
      "label": "Seller payout",
      "status": "completed",
      "transaction_signature": "5K7x..."
    },
    {
      "id": "split_def456",
      "payment_id": "pay_test_abc123",
      "wallet_address": "PlatformWallet...",
      "type": "percentage",
      "value": 20,
      "amount": 20.00,
      "label": "Platform fee",
      "status": "completed",
      "transaction_signature": "8M2n..."
    }
  ]
}

Split Statuses

StatusDescription
pendingSplit is queued, waiting for payment confirmation
processingFunds are being transferred to the recipient
completedFunds have been delivered to the recipient’s wallet
failedSplit transfer failed (will be retried)

Marketplace Example

A complete marketplace flow where sellers list products and the platform takes a commission:
// Server-side: create payment when customer checks out
app.post('/api/checkout', async (req, res) => {
  const { productId, sellerId } = req.body;

  // Look up product and seller
  const product = await db.products.findUnique({ where: { id: productId } });
  const seller = await db.sellers.findUnique({ where: { id: sellerId } });

  const PLATFORM_FEE_PERCENT = 15;

  const payment = await zendfi.createPayment({
    amount: product.price,
    currency: 'USD',
    description: `Order: ${product.name}`,
    customer_email: req.user.email,
    split_recipients: [
      {
        recipient_wallet: seller.walletAddress,
        recipient_name: `Seller: ${seller.name}`,
        percentage: 100 - PLATFORM_FEE_PERCENT,
      },
      {
        recipient_wallet: process.env.PLATFORM_WALLET!,
        recipient_name: 'Platform commission',
        percentage: PLATFORM_FEE_PERCENT,
      },
    ],
    metadata: {
      product_id: productId,
      seller_id: sellerId,
      order_type: 'marketplace',
    },
  });

  res.json({ checkout_url: payment.checkout_url });
});

Webhook Events for Splits

When splits are processed, you receive webhook events. Use the handlers map:
handlers: {
  'split.completed': async (data) => {
    console.log(`Split completed for payment ${data.payment_id}`);
    console.log(`Amount: $${data.amount} to ${data.wallet_address}`);

    // Notify the seller
    await notifySeller(data.wallet_address, {
      amount: data.amount,
      paymentId: data.payment_id,
    });
  },
}

Validation Rules

  • Percentage splits must sum to exactly 100%.
  • Fixed amount splits must not exceed the payment amount.
  • You can mix percentage and fixed amount splits, but the fixed amounts are deducted first, and the percentage splits are applied to the remainder.
  • Each payment can have up to 10 split recipients.
  • All wallet addresses must be valid Solana addresses.
  • Splits cannot be modified after payment creation.

Multi-Vendor Cart

For shopping carts with items from multiple sellers:
function buildSplits(cartItems: CartItem[], platformFee: number) {
  // Group items by seller
  const sellerTotals = new Map<string, { wallet: string; total: number; name: string }>();

  for (const item of cartItems) {
    const existing = sellerTotals.get(item.sellerId);
    if (existing) {
      existing.total += item.price * item.quantity;
    } else {
      sellerTotals.set(item.sellerId, {
        wallet: item.sellerWallet,
        total: item.price * item.quantity,
        name: item.sellerName,
      });
    }
  }

  const cartTotal = cartItems.reduce((sum, i) => sum + i.price * i.quantity, 0);
  const splits = [];

  // Platform fee as fixed amount
  splits.push({
    recipient_wallet: process.env.PLATFORM_WALLET!,
    recipient_name: 'Platform fee',
    fixed_amount_usd: cartTotal * (platformFee / 100),
  });

  // Each seller gets their portion as fixed amount
  for (const [, seller] of sellerTotals) {
    splits.push({
      recipient_wallet: seller.wallet,
      recipient_name: `Payout: ${seller.name}`,
      fixed_amount_usd: seller.total * (1 - platformFee / 100),
    });
  }

  return splits;
}

Testing Splits

Use the CLI to verify split behavior:
# Create a test payment (splits are configured via the API, not the CLI)
zendfi payment create --amount 100

# After payment confirmation, check the payment details
zendfi payment status pay_test_abc123
# The output includes split_ids when splits are configured
In test mode, splits go to Devnet wallets. Use a Solana explorer like Solscan (set to Devnet) to verify each split transaction.