add_action('app_process_vendor_transfer', function(int $order_id) {
$order = wc_get_order($order_id);
if (!$order || $order->get_meta('_stripe_transfers_completed')) {
return;
}
$stripe = new \Stripe\StripeClient(get_option('stripe_secret_key'));
$charge_id = $order->get_transaction_id();
// Grupăm sumele datorate fiecărui cont conectat
$vendor_totals = [];
foreach ($order->get_items() as $item) {
$account_id = $item->get_meta('_vendor_stripe_account_id');
$net_amount = (float) $item->get_meta('_vendor_net_amount');
if ($account_id && $net_amount > 0) {
$vendor_totals[$account_id] = ($vendor_totals[$account_id] ?? 0) + $net_amount;
}
}
foreach ($vendor_totals as $account_id => $amount) {
$stripe->transfers->create([
'amount' => (int) round($amount * 100),
'currency' => strtolower($order->get_currency()),
'destination' => $account_id,
'source_transaction' => $charge_id,
'metadata' => ['order_id' => $order_id]
]);
}
$order->update_meta_data('_stripe_transfers_completed', true);
$order->save();
});Când ai un WooCommerce clasic și clientul cere brusc marketplace cu split automat de fonduri, primul impuls e să instalezi Dokan sau WCFM. Am făcut greșeala asta acum vreo patru ani pe un magazin B2B cu vreo 40 de vendori și în jur de 1.200 de comenzi lunare. După două luni de erori tăcute de webhook și reconcilieri financiare manuale în Excel, am rescris logica de plată de la zero direct pe API-ul Stripe Connect.
Problema principală la un marketplace în WooCommerce nu e formularul de checkout, ci arhitectura banilor. În Stripe Connect ai două direcții mari: Destination Charges și Separate Charges & Transfers. Dacă alegi greșit de la început, refactorizarea te va costa săptămâni întregi de muncă.
Destination Charges pică instant la coșuri multi-vendor
Destination charges sunt extrem de elegante dacă un client cumpără de la un singur vendor per tranzacție. Banii pleacă de pe cardul clientului, Stripe își oprește comisionul, platforma ta reține application_fee_amount, iar restul ajunge direct în contul conectat al vendorului. Responsabilitatea pentru chargeback-uri poate fi pasată către vendor, iar tu stai liniștit.
Dar ce faci când un client pune în coș un tricou de la Vendorul A și o cană de la Vendorul B?
Aici crapă schema. Stripe nu permite rutarea unei singure autorizări către două conturi conectate diferite prin Destination Charges. Dacă lași clientul să cumpere produse mixte într-un singur checkout WooCommerce, ești obligat să folosești Separate Charges & Transfers. Adică banii intră mai întâi 100% în contul platformei tale, iar tu ești forțat să devii Merchant of Record.
Trade-off-ul dureros: responsabilitate legală și comisioane pierdute
Când platforma ta încasează toți banii și face transferuri ulterioare (Stripe\Transfer), ești răspunzător legal pentru tot: fraude, carduri furate și dispute. Dacă un vendor vinde marfă proastă și fuge cu banii, banca îți va lua ție banii din cont, nu lui.
Al doilea blocaj apare la retururi parțiale. Să presupunem că un client vrea banii înapoi doar pe produsul Vendorului A. Trebuie să faci un TransferReversal prin API ca să tragi banii înapoi din contul Stripe conectat, dar comisionul inițial de procesare al cardului rămâne pierdut în buzunarul procesatorului. La magazinul menționat mai devreme, pierdeam lejer între 350 și 400 de euro pe lună doar din comisioane Stripe nerecuperate la retururi până când am introdus o clauză contractuală și un buffer de payout de 14 zile.
Cum legi transferurile curate fără monoliți de marketplace
În loc să cari în spate un plugin mamut de 50 MB cu mii de linii de cod pe care nu le folosești, cel mai stabil e să folosești WooCommerce doar pentru catalog și coș, iar logica de split să o muți asincron pe un hook.
Nu rula niciodată transferurile Stripe pe woocommerce_checkout_order_processed în timpul request-ului HTTP al clientului. Dacă pică un apel de API sau dă timeout, clientul rămâne cu ecran alb și comanda pe jumătate procesată. Folosește Action Scheduler (care vine deja inclus în WooCommerce) și declanșează transferurile după ce plata e confirmată complet via webhook (payment_intent.succeeded).
Cum gestionați voi coșurile mixte pe marketplace-uri românești: forțați split de comenzi per vendor la checkout sau preferați să fiți voi procesatorul principal și să gestionați transferurile asincron?