Skip to content

Split de Pagamento

Sistema de divisão automática de pagamentos entre conta principal e sub-contas.

O que é Split?

Split é a divisão automática do valor de uma transação entre diferentes contas. Ideal para:

  • Marketplaces: Dividir entre plataforma e vendedores
  • Agregadores: Distribuir comissões automaticamente
  • Plataformas SaaS: Repassar valores para clientes

Como Funciona

mermaid
graph LR
    A[Cliente Paga R$ 100] --> B[DiviPay]
    B --> C[Conta Principal R$ 10]
    B --> D[Sub-conta R$ 90]

Tipos de Split

1. Split por Taxa Fixa

Defina um valor fixo que fica na conta principal:

javascript
{
  "customerId": "sub_abc123",
  "fee": "10.00",
  "amount": 100.00
}

Resultado:

  • R$ 10,00 → Conta principal
  • R$ 90,00 → Sub-conta

2. Split Inverso

Use valor negativo para inverter a lógica:

javascript
{
  "customerId": "sub_abc123",
  "fee": "-10.00",
  "amount": 100.00
}

Resultado:

  • R$ 90,00 → Conta principal
  • R$ 10,00 → Sub-conta

3. Split por Percentual (Item)

Configure percentual de comissão por item:

javascript
{
  "customerId": "sub_abc123",
  "itens": [
    {
      "name": "Produto A",
      "quantity": 1,
      "unitPrice": 100.00,
      "feePercent": 10.0  // 10%
    }
  ]
}

Resultado:

  • R$ 10,00 → Conta principal (10% de R$ 100)
  • R$ 90,00 → Sub-conta

Exemplos Práticos

Marketplace de Produtos

javascript
async function createMarketplaceSale(sale) {
  const marketplaceFee = 0.10; // 10% de comissão
  const feeAmount = sale.total * marketplaceFee;

  const charge = await auth.request('/api/charge/pix', 'POST', {
    customerId: sale.seller.diviPayId,
    fee: feeAmount.toFixed(2),
    amount: sale.total,
    description: `Pedido #${sale.id}`,
    referenceId: sale.id,
    callbackUrl: `${BASE_URL}/webhook/sale`,
    expirationSeconds: 3600,
    client: {
      name: sale.customer.name,
      document: sale.customer.cpf,
      email: sale.customer.email,
      phone: sale.customer.phone,
      ip: sale.customer.ip
    },
    itens: sale.items.map(item => ({
      name: item.name,
      quantity: item.quantity,
      unitPrice: item.price,
      feePercent: marketplaceFee * 100
    }))
  });

  return charge;
}

// Uso
const sale = {
  id: 'sale-123',
  total: 250.00,
  seller: {
    id: 'seller-456',
    diviPayId: 'sub_abc123'
  },
  customer: {
    name: 'João Silva',
    cpf: '12345678901',
    email: 'joao@email.com',
    phone: '11999999999',
    ip: '192.168.1.1'
  },
  items: [
    {
      name: 'Produto A',
      quantity: 2,
      price: 125.00
    }
  ]
};

const charge = await createMarketplaceSale(sale);
// R$ 25,00 para marketplace
// R$ 225,00 para vendedor

Plataforma de Serviços

javascript
async function createServicePayment(booking) {
  // Taxa fixa de R$ 15,00 + 5% do valor
  const fixedFee = 15.00;
  const percentFee = booking.amount * 0.05;
  const totalFee = fixedFee + percentFee;

  const charge = await auth.request('/api/charge/pix', 'POST', {
    customerId: booking.provider.diviPayId,
    fee: totalFee.toFixed(2),
    amount: booking.amount,
    description: `Serviço #${booking.id}`,
    referenceId: booking.id,
    callbackUrl: `${BASE_URL}/webhook/booking`,
    expirationSeconds: 1800,
    client: {
      name: booking.client.name,
      document: booking.client.cpf,
      email: booking.client.email,
      phone: booking.client.phone,
      ip: booking.client.ip
    },
    itens: [
      {
        name: booking.service.name,
        quantity: 1,
        unitPrice: booking.amount,
        feePercent: 5.0
      }
    ]
  });

  return charge;
}

Agregador de Pagamentos

javascript
async function createAggregatorPayment(payment) {
  // Comissão variável por tipo de cliente
  const feeRates = {
    'premium': 0.02,   // 2%
    'standard': 0.05,  // 5%
    'basic': 0.10      // 10%
  };

  const feeRate = feeRates[payment.client.tier] || 0.05;
  const feeAmount = payment.amount * feeRate;

  const charge = await auth.request('/api/charge/pix', 'POST', {
    customerId: payment.client.diviPayId,
    fee: feeAmount.toFixed(2),
    amount: payment.amount,
    description: payment.description,
    referenceId: payment.id,
    callbackUrl: `${BASE_URL}/webhook/payment`,
    expirationSeconds: 3600,
    client: {
      name: payment.payer.name,
      document: payment.payer.document,
      email: payment.payer.email,
      phone: payment.payer.phone,
      ip: payment.payer.ip
    },
    itens: [
      {
        name: payment.description,
        quantity: 1,
        unitPrice: payment.amount,
        feePercent: feeRate * 100
      }
    ]
  });

  return charge;
}

Split com Múltiplos Itens

javascript
async function createMultiItemSale(cart) {
  const charge = await auth.request('/api/charge/pix', 'POST', {
    customerId: cart.seller.diviPayId,
    amount: cart.total,
    description: `Pedido #${cart.id}`,
    referenceId: cart.id,
    callbackUrl: `${BASE_URL}/webhook/cart`,
    expirationSeconds: 3600,
    client: {
      name: cart.customer.name,
      document: cart.customer.cpf,
      email: cart.customer.email,
      phone: cart.customer.phone,
      ip: cart.customer.ip
    },
    itens: cart.items.map(item => ({
      name: item.name,
      quantity: item.quantity,
      unitPrice: item.price,
      // Comissão diferente por categoria
      feePercent: item.category === 'electronics' ? 5.0 : 10.0
    }))
  });

  return charge;
}

// Exemplo de carrinho
const cart = {
  id: 'cart-789',
  total: 350.00,
  seller: {
    diviPayId: 'sub_abc123'
  },
  customer: {
    name: 'Maria Santos',
    cpf: '98765432100',
    email: 'maria@email.com',
    phone: '11988888888',
    ip: '192.168.1.2'
  },
  items: [
    {
      name: 'Notebook',
      quantity: 1,
      price: 250.00,
      category: 'electronics'  // 5% comissão
    },
    {
      name: 'Mouse',
      quantity: 2,
      price: 50.00,
      category: 'accessories'  // 10% comissão
    }
  ]
};

// Split resultante:
// Notebook: R$ 12,50 (5% de R$ 250)
// Mouse: R$ 10,00 (10% de R$ 100)
// Total marketplace: R$ 22,50
// Total vendedor: R$ 327,50

Calculando Split

Função Helper

javascript
function calculateSplit(amount, feePercent) {
  const fee = amount * (feePercent / 100);
  const sellerAmount = amount - fee;
  
  return {
    total: amount,
    platformFee: parseFloat(fee.toFixed(2)),
    sellerAmount: parseFloat(sellerAmount.toFixed(2)),
    feePercent
  };
}

// Uso
const split = calculateSplit(100.00, 10);
console.log(split);
// {
//   total: 100.00,
//   platformFee: 10.00,
//   sellerAmount: 90.00,
//   feePercent: 10
// }

Validação de Split

javascript
function validateSplit(amount, fee) {
  if (fee < 0) {
    throw new Error('Fee cannot be negative');
  }
  
  if (fee >= amount) {
    throw new Error('Fee cannot be greater than or equal to amount');
  }
  
  const sellerAmount = amount - fee;
  if (sellerAmount < 1.00) {
    throw new Error('Seller amount must be at least R$ 1.00');
  }
  
  return true;
}

Webhooks com Split

Quando um pagamento com split é confirmado, ambas as contas recebem webhook:

Webhook Conta Principal

json
{
  "event": "charge.paid",
  "chargeId": "cob_abc123",
  "referenceId": "sale-123",
  "amount": 100.00,
  "platformFee": 10.00,
  "sellerAmount": 90.00,
  "customerId": "sub_abc123",
  "paidAt": "2024-11-04T15:30:00.000Z"
}

Webhook Sub-conta

json
{
  "event": "charge.paid",
  "chargeId": "cob_abc123",
  "referenceId": "sale-123",
  "amount": 90.00,
  "receivedAmount": 90.00,
  "paidAt": "2024-11-04T15:30:00.000Z"
}

Relatórios de Split

javascript
async function getSplitReport(startDate, endDate) {
  const charges = await db.charges.findMany({
    where: {
      paidAt: {
        gte: startDate,
        lte: endDate
      },
      customerId: { not: null }
    }
  });

  const report = {
    totalTransactions: charges.length,
    totalAmount: 0,
    totalPlatformFee: 0,
    totalSellerAmount: 0,
    bySeller: {}
  };

  charges.forEach(charge => {
    report.totalAmount += charge.amount;
    report.totalPlatformFee += charge.fee;
    report.totalSellerAmount += (charge.amount - charge.fee);

    if (!report.bySeller[charge.customerId]) {
      report.bySeller[charge.customerId] = {
        transactions: 0,
        amount: 0,
        fee: 0,
        received: 0
      };
    }

    const seller = report.bySeller[charge.customerId];
    seller.transactions++;
    seller.amount += charge.amount;
    seller.fee += charge.fee;
    seller.received += (charge.amount - charge.fee);
  });

  return report;
}

Boas Práticas

1. Transparência

Mostre claramente a divisão para vendedores:

javascript
function displaySplitInfo(amount, feePercent) {
  const split = calculateSplit(amount, feePercent);
  
  return `
    Valor total: R$ ${split.total.toFixed(2)}
    Taxa da plataforma (${feePercent}%): R$ ${split.platformFee.toFixed(2)}
    Você receberá: R$ ${split.sellerAmount.toFixed(2)}
  `;
}

2. Validação

Valide valores antes de criar cobrança:

javascript
function validateChargeWithSplit(data) {
  if (data.fee && data.fee >= data.amount) {
    throw new Error('Fee must be less than amount');
  }
  
  const sellerAmount = data.amount - (data.fee || 0);
  if (sellerAmount < 1.00) {
    throw new Error('Seller must receive at least R$ 1.00');
  }
}

3. Auditoria

Registre todas as operações de split:

javascript
await db.splitLog.create({
  data: {
    chargeId: charge.id,
    amount: charge.amount,
    fee: charge.fee,
    customerId: charge.customerId,
    createdAt: new Date()
  }
});

Próximos Passos

Documentação da API DiviPay