WS Forms Bexio Verknüpfung

Anmeldedaten von Kunde für Bexio & Developer Bexio Portal

Bexio API Token: https://developer.bexio.com/
Personal Access Token setzen
– Website Formular
– Company Eingeben und Token rauskopieren.
Wichtig! Expires notieren!

Datei in wp-content speichern. Es geht nicht via Plugin, oder nur sehr mühsam.

/wp-content/bexio-webhook.php

PHP
<?php
// Verhindert direkten Zugriff ohne POST-Daten
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    die('Method Not Allowed');
}

/**
 * Bexio Contact Handler - Mit Smart Update
 * Leere Felder überschreiben vorhandene Werte NICHT
 */


// === KONFIGURATION ===
define('BEXIO_TOKEN', 'BEXIO_API_TOKEN');
define('BEXIO_API_BASE', 'https://api.bexio.com/2.0');
define('BEXIO_USER_ID', 2);
define('BEXIO_OWNER_ID', 2);
define('BEXIO_COUNTRY_ID', 1);

// === BEXIO API REQUEST ===
function bexioRequest($url, $method = 'POST', $data = null) {
    $ch = curl_init($url);
    
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . BEXIO_TOKEN,
            'Accept: application/json',
            'Content-Type: application/json'
        ]
    ]);
    
    if ($data !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    }
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    curl_close($ch);
    
    if ($error) {
        throw new Exception("cURL Error: $error");
    }
    
    return [
        'code' => $httpCode,
        'body' => json_decode($response, true)
    ];
}

// === HELPER FUNCTIONS ===
function cleanString($value) {
    if ($value === null) {
        return '';
    }
    return trim((string)$value);
}

function hasValue($value) {
    $cleaned = cleanString($value);
    return !empty($cleaned);
}

// === SMART MERGE ===
function smartMerge($newData, $existingData) {
    $merged = $newData;
    
    $smartFields = [
        'phone_mobile',
        'phone_fixed',
        'street_name',
        'house_number',
        'address_addition',
        'postcode',
        'city',
        'mail_second'
    ];
    
    foreach ($smartFields as $field) {
        if (!hasValue($merged[$field] ?? '') && hasValue($existingData[$field] ?? '')) {
            $merged[$field] = $existingData[$field];
        }
    }
    
    return $merged;
}

// === PRÜFE OB ZUSATZADRESSE SCHON EXISTIERT ===
function isAdditionalAddressDuplicate($existingAddresses, $name, $street, $houseNumber, $postcode, $city) {
    if (empty($existingAddresses)) {
        return false;
    }
    
    foreach ($existingAddresses as $addr) {
        $nameMatch = strtolower(trim($addr['name'] ?? '')) === strtolower(trim($name));
        $streetMatch = strtolower(trim($addr['street_name'] ?? '')) === strtolower(trim($street));
        $houseMatch = trim($addr['house_number'] ?? '') === trim($houseNumber);
        $postcodeMatch = trim($addr['postcode'] ?? '') === trim($postcode);
        $cityMatch = strtolower(trim($addr['city'] ?? '')) === strtolower(trim($city));
        
        if ($nameMatch && $streetMatch && $houseMatch && $postcodeMatch && $cityMatch) {
            return true;
        }
    }
    
    return false;
}

// === HAUPTLOGIK ===
try {
    header('Content-Type: application/json');
    
    $rawInput = file_get_contents('php://input');
    
    if (empty($rawInput)) {
        throw new Exception('Keine Daten empfangen');
    }
    
    $formData = json_decode($rawInput, true);
    
    if (!$formData) {
        throw new Exception('JSON-Decode Fehler: ' . json_last_error_msg());
    }
    
    // === VALIDIERUNG ===
    if (!isset($formData['rechnung_typ'])) {
        throw new Exception('Feld "rechnung_typ" fehlt');
    }
    
    if (!hasValue($formData['mail'] ?? '')) {
        throw new Exception('E-Mail-Adresse ist erforderlich');
    }
    
    $rechnungsTyp = cleanString($formData['rechnung_typ']);
    $email = cleanString($formData['mail']);
    $emailSecond = cleanString($formData['mail_second'] ?? '');
    
    // === KONTAKTDATEN VORBEREITEN (RECHNUNGSADRESSE) ===
    if ($rechnungsTyp === 'Firma') {
        $firmenname = cleanString($formData['firma_name'] ?? '');
        $firmennameZusatz = cleanString($formData['firma_name_zusatz'] ?? '');
        
        if (!hasValue($firmenname)) {
            throw new Exception('Firmenname ist erforderlich bei Firmen');
        }
        
        $contactData = [
            'contact_type_id' => 1,
            'name_1' => $firmenname,
            'name_2' => hasValue($firmennameZusatz) ? $firmennameZusatz : null,
            'street_name' => cleanString($formData['street_name'] ?? ''),
            'house_number' => cleanString($formData['house_number'] ?? ''),
            'address_addition' => hasValue($formData['address_addition'] ?? '') ? cleanString($formData['address_addition']) : null,
            'postcode' => cleanString($formData['postcode'] ?? ''),
            'city' => cleanString($formData['city'] ?? ''),
            'mail' => $email,
            'mail_second' => hasValue($emailSecond) ? $emailSecond : '',
            'phone_fixed' => cleanString($formData['phone_mobile'] ?? ''),
            'phone_mobile' => '',
            'country_id' => BEXIO_COUNTRY_ID,
            'user_id' => BEXIO_USER_ID,
            'owner_id' => BEXIO_OWNER_ID
        ];
        
    } else {
        $vorname = cleanString($formData['name_2'] ?? '');
        $nachname = cleanString($formData['name_1'] ?? '');
        
        if (!hasValue($vorname) || !hasValue($nachname)) {
            throw new Exception('Vor- und Nachname sind erforderlich bei Privatpersonen');
        }
        
        $contactData = [
            'contact_type_id' => 2,
            'name_1' => $nachname,
            'name_2' => $vorname,
            'street_name' => cleanString($formData['street_name'] ?? ''),
            'house_number' => cleanString($formData['house_number'] ?? ''),
            'address_addition' => hasValue($formData['address_addition'] ?? '') ? cleanString($formData['address_addition']) : null,
            'postcode' => cleanString($formData['postcode'] ?? ''),
            'city' => cleanString($formData['city'] ?? ''),
            'mail' => $email,
            'mail_second' => hasValue($emailSecond) ? $emailSecond : '',
            'phone_mobile' => cleanString($formData['phone_mobile'] ?? ''),
            'phone_fixed' => '',
            'country_id' => BEXIO_COUNTRY_ID,
            'user_id' => BEXIO_USER_ID,
            'owner_id' => BEXIO_OWNER_ID
        ];
    }
    
    // === DUPLIKAT-CHECK FÜR RECHNUNGSKONTAKT ===
    $searchResult = bexioRequest(
        BEXIO_API_BASE . '/contact/search',
        'POST',
        [[
            'field' => 'mail',
            'value' => $email,
            'criteria' => '='
        ]]
    );
    
    $contactId = null;
    
    // === KONTAKT ERSTELLEN ODER AKTUALISIEREN ===
    if (!empty($searchResult['body']) && is_array($searchResult['body'])) {
        $contactId = $searchResult['body'][0]['id'];
        
        $existingContact = bexioRequest(
            BEXIO_API_BASE . "/contact/$contactId",
            'GET'
        );
        
        $contactData = smartMerge($contactData, $existingContact['body']);
        
        $result = bexioRequest(
            BEXIO_API_BASE . "/contact/$contactId",
            'POST',
            $contactData
        );
        
    } else {
        $result = bexioRequest(
            BEXIO_API_BASE . '/contact',
            'POST',
            $contactData
        );
        
        $contactId = $result['body']['id'] ?? null;
    }
    
    // === STANDORTADRESSE ALS ADDITIONAL ADDRESS HINZUFÜGEN ===
    if ($contactId) {
        $standortVorname = cleanString($formData['vorname_standort'] ?? '');
        $standortNachname = cleanString($formData['nachname_standort'] ?? '');
        $standortName = trim($standortVorname . ' ' . $standortNachname);
        $standortStrasse = cleanString($formData['strasse_standort'] ?? '');
        $standortHausnummer = cleanString($formData['hausnummer_standort'] ?? '');
        $standortAdresszusatz = cleanString($formData['adresszusatz_standort'] ?? '');
        $standortPLZ = cleanString($formData['plz_standort'] ?? '');
        $standortOrt = cleanString($formData['ort_standort'] ?? '');
        $standortTelefon = cleanString($formData['telefon_standort'] ?? '');
        $standortEmail = cleanString($formData['mail_standort'] ?? '');
        
        if (hasValue($standortName) && hasValue($standortStrasse) && hasValue($standortPLZ)) {
            
            $existingAddresses = bexioRequest(
                BEXIO_API_BASE . "/contact/$contactId/additional_address",
                'GET'
            );
            
            $isDuplicate = isAdditionalAddressDuplicate(
                $existingAddresses['body'] ?? [],
                $standortName,
                $standortStrasse,
                $standortHausnummer,
                $standortPLZ,
                $standortOrt
            );
            
            $addressCount = count($existingAddresses['body'] ?? []);
            
            if (!$isDuplicate && $addressCount < 17) {
                // Nur E-Mail in Beschreibung (oder leer lassen wenn keine E-Mail)
                $description = hasValue($standortEmail) ? $standortEmail : '';
                
                $additionalAddressData = [
                    'name' => $standortName,
                    'name_addition' => hasValue($standortTelefon) ? $standortTelefon : null,
                    'street_name' => $standortStrasse,
                    'house_number' => $standortHausnummer,
                    'address_addition' => hasValue($standortAdresszusatz) ? $standortAdresszusatz : null,
                    'postcode' => $standortPLZ,
                    'city' => $standortOrt,
                    'country_id' => BEXIO_COUNTRY_ID,
                    'subject' => $standortName,
                    'description' => $description
                ];
                
                bexioRequest(
                    BEXIO_API_BASE . "/contact/$contactId/additional_address",
                    'POST',
                    $additionalAddressData
                );
            }
        }
    }
    
    // === ERFOLG ===
    http_response_code($result['code']);
    echo json_encode($result['body']);
    
} catch (Exception $e) {
    http_response_code(500);
    echo json_encode([
        'error' => true,
        'message' => $e->getMessage()
    ]);
}
?>