eduardweb.
Payments (Stripe, PayPal, Netopia)Avansat#woocommerce#stripe#php#marketplace#payments

Split payments în WooCommerce cu Stripe Connect: lecții dintr-un marketplace cu 140 de vendori

De Liliana Ghiță, 4 oct. 2026 · 8 vizualizări · 3 like-uri

Postat 4 oct. 2026
php
add_action('wc_stripe_payment_intent_succeeded', function($order_id, $intent) {
    $order = wc_get_order($order_id);
    if (!$order || $order->get_meta('_stripe_transfers_done')) {
        return;
    }

    $vendor_payouts = my_marketplace_calculate_vendor_splits($order);

    foreach ($vendor_payouts as $vendor_id => $amount_in_cents) {
        $stripe_account_id = get_user_meta($vendor_id, '_stripe_connect_id', true);
        if (!$stripe_account_id) continue;

        \Stripe\Transfer::create([
            'amount' => $amount_in_cents,
            'currency' => 'ron',
            'destination' => $stripe_account_id,
            'source_transaction' => $intent->latest_charge,
            'description' => sprintf('Plată comandă #%d', $order_id),
        ], [
            'idempotency_key' => sprintf('transfer_%d_%d', $order_id, $vendor_id)
        ]);
    }

    $order->update_meta_data('_stripe_transfers_done', true);
    $order->save();
}, 10, 2);

Când un client îți cere marketplace pe WooCommerce cu checkout unificat și vendori independenți, primul impuls sănătos e să-l trimiți spre o platformă dedicată. Al doilea e să desfaci Stripe Connect și să vezi cum împarți banii programatic fără să ajungi la procese fiscale.

Anul trecut am migrat un magazin de nișă cu vreo 140 de producători locali și o medie de 1.800 de comenzi lunare de la clasicul payout manual la split payments automatizat. A durat trei săptămâni doar să curățăm cazurile de margine de care nu scrie în documentația oficială.

Destination Charges vs. Separate Charges & Transfers

Aici e prima greșeală pe care o văd la majoritatea implementărilor. Mulți folosesc Destination Charges pentru că e cel mai simplu de legat: clientul plătește, Stripe reține comisionul platformei (de exemplu 10%), iar restul pleacă direct în contul vendorului conectat.

Problema? Destination Charges funcționează exclusiv dacă fiecare comandă conține produse de la un singur vendor. În momentul în care un client a pus în coș o sticlă de vin de la crama X și un calup de brânză de la ferma Y, logica se rupe. Stripe nu știe să împartă o singură tranzacție către două destinații simultan prin parametrul transfer_data.

Soluția obligatorie pentru coșuri mixte este arhitectura de Separate Charges and Transfers:

  1. Încasezi suma întreagă pe contul platformei (platform charge).
  2. La webhook-ul de plată confirmată, generezi transferuri separate către conturile conectate ale fiecărui vendor.

Trade-off-ul e dur: platforma devine procesatorul legal în fața clientului. Dacă un vendor livrează marfă stricată sau dă faliment înainte de livrare, chargeback-ul cade pe capul magazinului tău, nu pe vendor.

Capcana refund-urilor și a disputelor

Toată lumea configurează Transfer::create și se bucură că merg plățile, dar nimeni nu testează retururile parțiale până când nu urlă contabilul. Dacă un client cere retur doar pentru produsul de 80 de lei de la vendorul A, comanda ta inițială de 300 de lei trebuie ajustată proporțional.

Când dai refund din WooCommerce fără logică custom, platforma restituie banii din balanța proprie, în timp ce vendorul rămâne cu banii transferați inițial. Trebuie neapărat să pasezi reverse_transfer => true pe apelul de refund sau să creezi un debit manual din contul conectat. Dacă balanța vendorului pe Stripe e zero (a dat payout instant în contul lui bancar), transfer reversal o să eșueze, iar WooCommerce va arunca eroare fatală dacă nu prinzi excepția Stripe Exception InvalidRequestException.

Webhook-uri, nu hooks de WordPress

Niciodată să nu declanșezi transferurile de bani pe hook-ul woocommerce_thankyou. Dacă userul închide tab-ul la redirecționarea 3D Secure sau are o cădere de net fix la redirect, hook-ul nu se execută niciodată, deși banii au fost retrași de pe card.

Am mapat execuția transferurilor exclusiv pe webhook-ul payment_intent.succeeded. Verificăm idempotency key-ul bazat pe ID-ul comenzii și ID-ul vendorului ca să nu trimitem banii de două ori dacă Stripe trimite webhook-ul de trei ori (ceea ce se întâmplă frecvent la latențe de rețea).

Voi cum gestionați riscul de chargeback când lucrați cu conturi Stripe Connect Express? Îi obligați pe vendori să țină o rezervă minimă în balanță sau asumați pierderile din comisionul platformei?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.