Tworzenie stron · Usługi programistyczne

Konfigurowalny formularz ACF wysyłający dane do systemu marketing automation

Duży klient B2B · B2B / marketing automation

Form field configuration in the Custom Form ACF block in WordPress editor - marketing adds fields, picks type and maps to marketing automation system

Wyzwanie

Duży klient B2B miał na stronie wiele różnych formularzy – jedne do kontaktu, inne pod webinary, jeszcze inne pod eventy VIP. Za każdym razem gdy marketing chciał dorzucić nowe pole albo zmienić etykietę, dzwonili do developera. Za każdym razem znaczyło to godzinę pracy – napisanie kodu, testy, deploy. Za każdym razem koszt.

Do tego wszystkie te formularze musiały trafiać do jednego systemu marketing automation, który obsługuje cały pipeline sprzedażowy klienta. System ten trzyma się mocno swoich standardów: konkretny endpoint, konkretne nazwy pól, konkretny format.

Zadanie było proste do sformułowania, trudne do zrobienia dobrze: zbudować w WordPressie jeden uniwersalny blok formularza, w którym marketing sam sobie ustawia pola przez interfejs administracyjny, bez ruszania kodu. A dane i tak zawsze prawidłowo trafiają do systemu marketing automation.

Co zrobiłam

Podeszłam do tego jako bloku ACF w Gutenbergu. Blok ma konfigurowalny repeater „Pola formularza” – marketing dodaje sobie pole, wybiera typ (tekst, email, telefon, select, textarea), ustala etykietę, decyduje czy jest wymagane. Dla pól typu select wpisuje opcje jedna pod drugą. Do tego osobne pola konfiguracji: zgody użytkownika (repeater z tekstem RODO), URL endpoint’a systemu marketing automation, opcjonalne pola ukryte przekazywane do systemu (np. identyfikator kampanii), komunikaty (sukces, błąd, walidacja).

Formularz na froncie generuje się dynamicznie z tej konfiguracji. Marketing dodał pole – pole pojawia się na stronie. Zmienił etykietę – etykieta się zmienia. Zero developera.

Bypass CORS przez ukryty iframe

Największy problem techniczny był z wysyłaniem danych do systemu marketing automation. Endpoint tego systemu jest na innej domenie niż strona klienta, a CORS blokuje bezpośredni POST z JavaScript. Można to obejść przez proxy po stronie WordPressa, ale wtedy tracimy kontekst przeglądarki (ciasteczka trackingowe systemu marketing) – a to psuje przypisywanie leadów do kampanii.

Rozwiązanie: ukryty iframe. Skrypt tworzy w tle niewidoczny formularz HTML z akcją prowadzącą do endpoint’a marketing automation i celem submitu na ukryty iframe. Wysyłamy submit, przeglądarka wykonuje POST natywnie (jakby to był normalny formularz), CORS nie ma tu nic do gadania bo to nie fetch tylko form submit. Odpowiedź ląduje w iframe i my jej nie odczytujemy – ale nie musimy, bo system marketing automation ma po swojej stronie logikę i tak.

function submitToMarketingAutomation(form, endpointUrl, onDone) {
    var iframeName = 'cf-iframe-' + Date.now();
    var iframe = document.createElement('iframe');
    iframe.name = iframeName;
    iframe.style.display = 'none';
    document.body.appendChild(iframe);

    var tempForm = document.createElement('form');
    tempForm.method = 'POST';
    tempForm.action = endpointUrl;
    tempForm.target = iframeName;
    tempForm.style.display = 'none';

    // ... zebranie pól z oryginalnego formularza i dodanie do tempForm

    document.body.appendChild(tempForm);
    tempForm.submit();

    setTimeout(function () {
        onDone();
        if (tempForm.parentNode) tempForm.parentNode.removeChild(tempForm);
        if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
    }, 2000);
}

Agregacja pól o tej samej nazwie

System marketing automation klienta ma ograniczenie: jest jedno pole „Comments” na wszystkie dodatkowe informacje. Marketing chciał w formularzu mieć trzy osobne pola widoczne dla użytkownika – dietę, NIP, dodatkowego uczestnika – ale w systemie muszą trafić jako jeden ciąg tekstu.

Zrobiłam to przez konwencję: w konfiguracji ACF marketing wpisuje wszystkim trzem polom tę samą nazwę marketing automation („Comments”), a JavaScript przed submitem zbiera je w mapę, łączy w string z etykietami rozdzielony pionową kreską:

var fieldMap = {};
var fields = form.querySelectorAll('input, select, textarea');

fields.forEach(function (field) {
    if (!field.name || !field.value) return;

    var name = field.name;
    var label = getLabelForField(field);

    if (!fieldMap[name]) fieldMap[name] = [];
    fieldMap[name].push({ label: label, value: field.value });
});

// Zbuduj hidden inputs
Object.keys(fieldMap).forEach(function (name) {
    var entries = fieldMap[name];
    var input = document.createElement('input');
    input.type = 'hidden';
    input.name = name;

    if (entries.length === 1) {
        input.value = entries[0].value;
    } else {
        // Wiele pól o tej samej nazwie - zlep w jeden string
        // Format: "Dieta: Wegetarianska | NIP: 1234 | Dodatkowy: Anna"
        var parts = entries.map(function (e) {
            return e.label + ': ' + e.value;
        });
        input.value = parts.join(' | ');
    }
    tempForm.appendChild(input);
});

Dlaczego setTimeout, a nie iframe.onload

Pokusa użycia zdarzenia iframe.onload do wykrycia momentu odebrania odpowiedzi przez system marketing automation jest duża, ale w praktyce nie zadziała. Ponieważ endpoint działa na innej domenie niż WordPress, wchodzi w grę polityka Same-Origin Policy. Przeglądarka odpala zdarzenie onload na ramce iframe od razu przy jej utworzeniu (dla pustego dokumentu about:blank), albo blokuje zdarzenie całkowicie po wysłaniu cross-origin POST. Nie ma stabilnego sposobu żeby „podsłuchać” moment, w którym zewnętrzny serwer skończył przetwarzać submit.

Kontrolowane opóźnienie czasowe (setTimeout) jest w tym scenariuszu najbardziej niezawodnym kompromisem produkcyjnym. Alternatywa – proxy po backendzie WordPressa – działałaby, ale kosztem dodatkowej warstwy infrastruktury i utraty kontekstu ciasteczek trackingowych systemu marketing automation.

Świadome ograniczenia

Nie wszystko warto było automatyzować. Świadomie zdecydowałam, że nie robię własnej warstwy walidacji odpowiedzi z systemu marketing automation. Iframe cross-origin i tak nie pozwala odczytać odpowiedzi (CORS blokuje), więc alternatywą byłoby proxy po backend’zie – a to komplikacja której nie potrzebowaliśmy. Zamiast tego formularz zawsze pokazuje „Dziękujemy” po 2 sekundach od submitu (analogicznie do innych formularzy klienta, które od dawna działają w ten sam sposób).

Konsekwencja: jeśli system marketing automation odrzuci submit (np. zły format email), użytkownik i tak zobaczy „Dziękujemy”. W praktyce się to nie zdarza – front-end waliduje wymagane pola przed submitem, a system marketing dostaje tylko poprawne dane. Ryzyko akceptowalne w zamian za brak dodatkowej warstwy infrastruktury.

Efekt

Marketing dodaje sobie nowe formularze bez developera. Wystarczy wrzucić na stronę blok „Custom Form”, skonfigurować pola, wpisać endpoint systemu marketing automation – gotowe. Zmiana etykiety w polu „Nazwa firmy”? 30 sekund w wp-admin, bez commita, bez deploya.

Dane trafiają do jednego systemu marketing automation w jednolitym formacie. Zgody użytkownika są konfigurowalne (RODO wymaga elastyczności w formułowaniu zgód pod różne kanały). Formularz pod webinar, formularz kontaktowy, formularz eventu – wszystko przez ten sam blok, wszystko trafia gdzie ma trafić.

Od strony developera – jeden blok do utrzymywania, jeden JS do debugowania. Zamiast pięciu różnych implementacji dla pięciu różnych formularzy – jeden reusable komponent.

Wróć do realizacji

Masz projekt do omówienia?

Napisz, o co chodzi - nie musi być technicznie, od tego jestem ja. Odezwę się i powiem wprost, czy i jak mogę pomóc.