V1 Working
This commit is contained in:
parent
cd8806bbf6
commit
1e7037428a
8
.env-sample
Normal file
8
.env-sample
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# WeFact Secrets
|
||||||
|
WEFACT_API_KEY=
|
||||||
|
WEFACT_API_URL=https://api.mijnwefact.nl/v2
|
||||||
|
|
||||||
|
# Monday Secrets
|
||||||
|
MONDAY_API_TOKEN=
|
||||||
|
BEDRIJVEN_BOARD_ID=
|
||||||
|
VAKKRACHTEN_BOARD_ID=
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@
|
|||||||
# Built Visual Studio Code Extensions
|
# Built Visual Studio Code Extensions
|
||||||
*.vsix
|
*.vsix
|
||||||
|
|
||||||
|
.env
|
||||||
67
lib/wefact_api.php
Normal file
67
lib/wefact_api.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class WeFactAPI
|
||||||
|
{
|
||||||
|
private $url;
|
||||||
|
private $apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor die de API sleutel en URL ontvangt.
|
||||||
|
* @param string $apiKey Jouw WeFact API sleutel.
|
||||||
|
* @param string $apiUrl De basis-URL voor de WeFact API.
|
||||||
|
*/
|
||||||
|
public function __construct(string $apiKey, string $apiUrl = 'https://api.mijnwefact.nl/v2/')
|
||||||
|
{
|
||||||
|
$this->url = $apiUrl;
|
||||||
|
$this->apiKey = $apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verstuurt een verzoek naar de WeFact API.
|
||||||
|
* @param string $controller De naam van de controller (bv. 'debtor').
|
||||||
|
* @param string $action De actie die uitgevoerd moet worden (bv. 'add').
|
||||||
|
* @param array $params Een array met parameters voor de call.
|
||||||
|
* @return array Het antwoord van de API.
|
||||||
|
*/
|
||||||
|
public function sendRequest(string $controller, string $action, array $params): array
|
||||||
|
{
|
||||||
|
// Voeg de verplichte parameters toe
|
||||||
|
$params['api_key'] = $this->apiKey;
|
||||||
|
$params['controller'] = $controller;
|
||||||
|
$params['action'] = $action;
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, '10');
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
|
||||||
|
|
||||||
|
// Let op: SSL-verificatie nooit uitschakelen in een productieomgeving.
|
||||||
|
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||||
|
|
||||||
|
$curlResp = curl_exec($ch);
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($curlError != '') {
|
||||||
|
$result = [
|
||||||
|
'controller' => 'invalid', 'action' => 'invalid',
|
||||||
|
'status' => 'error', 'date' => date('c'),
|
||||||
|
'errors' => [$curlError]
|
||||||
|
];
|
||||||
|
} elseif ($httpCode >= 400) { // Vang alle client/server fouten af
|
||||||
|
$result = [
|
||||||
|
'controller' => 'invalid', 'action' => 'invalid',
|
||||||
|
'status' => 'error', 'date' => date('c'),
|
||||||
|
'errors' => ["HTTP status code: " . $httpCode, $curlResp]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$result = json_decode($curlResp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
208
webhook.php
Normal file
208
webhook.php
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
<?php
|
||||||
|
ini_set('error_log', __DIR__ . '/debug.log');
|
||||||
|
|
||||||
|
// Laad de class en de .env variabelen
|
||||||
|
require_once 'lib/wefact_api.php';
|
||||||
|
|
||||||
|
// Helper functie om .env bestand te laden ZONDER Composer
|
||||||
|
function loadEnv($path) {
|
||||||
|
if (!file_exists($path)) return;
|
||||||
|
$lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||||
|
foreach ($lines as $line) {
|
||||||
|
if (strpos(trim($line), '#') === 0) continue;
|
||||||
|
list($name, $value) = explode('=', $line, 2);
|
||||||
|
$_ENV[trim($name)] = trim($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadEnv(__DIR__ . '/.env');
|
||||||
|
|
||||||
|
// --- Configuratie ---
|
||||||
|
$wefactApiKey = $_ENV['WEFACT_API_KEY'];
|
||||||
|
$mondayApiToken = $_ENV['MONDAY_API_TOKEN'];
|
||||||
|
$bedrijvenBoardId = (int)$_ENV['BEDRIJVEN_BOARD_ID'];
|
||||||
|
$vakkrachtenBoardId = (int)$_ENV['VAKKRACHTEN_BOARD_ID'];
|
||||||
|
|
||||||
|
// --- Data Mapping & Helper Functies (ongewijzigd) ---
|
||||||
|
$countryCodeMap = [
|
||||||
|
'nederland' => 'NL', 'belgië' => 'BE', 'belgium' => 'BE',
|
||||||
|
'duitsland' => 'DE', 'germany' => 'DE',
|
||||||
|
];
|
||||||
|
|
||||||
|
function getColumnValue(array $columnValues, string $columnId): ?string {
|
||||||
|
foreach ($columnValues as $column) {
|
||||||
|
if ($column['id'] === $columnId) return $column['text'];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMondayColumn(int $boardId, int $itemId, string $columnId, string $newValue, string $mondayToken) {
|
||||||
|
$query = 'mutation ($itemId: ID!, $boardId: ID!, $columnId: String!, $newValue: String!) {
|
||||||
|
change_simple_column_value(item_id: $itemId, board_id: $boardId, column_id: $columnId, value: $newValue) { id }
|
||||||
|
}';
|
||||||
|
$variables = ['boardId' => $boardId, 'itemId' => $itemId, 'columnId' => $columnId, 'newValue' => $newValue];
|
||||||
|
$payload = json_encode(['query' => $query, 'variables' => $variables]);
|
||||||
|
$ch = curl_init('https://api.monday.com/v2');
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Authorization: ' . $mondayToken]);
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
if ($httpCode >= 400) {
|
||||||
|
error_log("Fout bij updaten Monday kolom {$columnId}: " . $response);
|
||||||
|
} else {
|
||||||
|
error_log("Monday kolom {$columnId} succesvol bijgewerkt voor item {$itemId}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMondayItemData(int $itemId, string $mondayApiToken): ?array {
|
||||||
|
$graphQLQuery = 'query($itemId: [ID!]) { items (ids: $itemId) { name column_values { id text } } }';
|
||||||
|
$mondayPayload = json_encode(['query' => $graphQLQuery, 'variables' => ['itemId' => $itemId]]);
|
||||||
|
$ch_monday = curl_init('https://api.monday.com/v2');
|
||||||
|
curl_setopt($ch_monday, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch_monday, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch_monday, CURLOPT_POSTFIELDS, $mondayPayload);
|
||||||
|
curl_setopt($ch_monday, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Authorization: ' . $mondayApiToken]);
|
||||||
|
$mondayResponse = curl_exec($ch_monday);
|
||||||
|
curl_close($ch_monday);
|
||||||
|
$itemData = json_decode($mondayResponse, true)['data']['items'][0] ?? null;
|
||||||
|
if (!$itemData) {
|
||||||
|
error_log("Item met ID {$itemId} niet gevonden in Monday.com response.");
|
||||||
|
}
|
||||||
|
return $itemData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Functies voor Bedrijven en Vakkrachten (ongewijzigd) ---
|
||||||
|
function handleBedrijven(array $itemData, int $itemId, WeFactAPI $wefactApi, string $mondayApiToken) {
|
||||||
|
global $countryCodeMap, $bedrijvenBoardId;
|
||||||
|
$columnValues = $itemData['column_values'];
|
||||||
|
$wefactDebtorIdColumn = 'text_mksq79kz';
|
||||||
|
$wefactDebtorId = getColumnValue($columnValues, $wefactDebtorIdColumn);
|
||||||
|
$debiteurData = array_filter([
|
||||||
|
'CompanyName' => $itemData['name'],
|
||||||
|
'CompanyNumber' => getColumnValue($columnValues, 'kvk_mkmphj97'),
|
||||||
|
'TaxNumber' => getColumnValue($columnValues, 'text_mkpw2a77'),
|
||||||
|
'PhoneNumber' => getColumnValue($columnValues, 'telefoon_mkktmz4t'),
|
||||||
|
'EmailAddress' => getColumnValue($columnValues, 'e_mail_mkktxyr6'),
|
||||||
|
'Address' => getColumnValue($columnValues, 'text_mkpwgxkm'),
|
||||||
|
'ZipCode' => getColumnValue($columnValues, 'text_mkpwp7ps'),
|
||||||
|
'City' => getColumnValue($columnValues, 'text_mkpw5ek1'),
|
||||||
|
'Country' => getColumnValue($columnValues, 'text_mkpwb8q1') ? ($countryCodeMap[strtolower(getColumnValue($columnValues, 'text_mkpwb8q1'))] ?? getColumnValue($columnValues, 'text_mkpwb8q1')) : null,
|
||||||
|
'InvoiceEmail' => getColumnValue($columnValues, 'text_mkpw1nxd'),
|
||||||
|
], fn($value) => $value !== null && $value !== '');
|
||||||
|
if ($wefactDebtorId) {
|
||||||
|
error_log("WeFact Debiteur ID {$wefactDebtorId} gevonden. Debiteur wordt bijgewerkt...");
|
||||||
|
$debiteurData['DebtorCode'] = $wefactDebtorId;
|
||||||
|
$response = $wefactApi->sendRequest('debtor', 'edit', $debiteurData);
|
||||||
|
} else {
|
||||||
|
error_log("Geen WeFact Debiteur ID gevonden. Nieuwe debiteur wordt aangemaakt...");
|
||||||
|
$response = $wefactApi->sendRequest('debtor', 'add', $debiteurData);
|
||||||
|
if ($response['status'] === 'success' && isset($response['debtor']['DebtorCode'])) {
|
||||||
|
$newDebtorId = $response['debtor']['DebtorCode'];
|
||||||
|
error_log("Debiteur succesvol aangemaakt met ID: {$newDebtorId}. Monday wordt bijgewerkt.");
|
||||||
|
updateMondayColumn($bedrijvenBoardId, $itemId, $wefactDebtorIdColumn, $newDebtorId, $mondayApiToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($response['status'] === 'error') {
|
||||||
|
error_log("FOUT van WeFact (Debiteur): " . json_encode($response['errors']));
|
||||||
|
} else {
|
||||||
|
error_log("SUCCES! Reactie van WeFact (Debiteur): " . json_encode($response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleVakkrachten(array $itemData, int $itemId, WeFactAPI $wefactApi, string $mondayApiToken) {
|
||||||
|
global $vakkrachtenBoardId;
|
||||||
|
$columnValues = $itemData['column_values'];
|
||||||
|
$wefactProductIdColumn = 'text_mksqgn4v';
|
||||||
|
$wefactProductId = getColumnValue($columnValues, $wefactProductIdColumn);
|
||||||
|
$productData = array_filter([
|
||||||
|
'ProductName' => $itemData['name'],
|
||||||
|
'ProductKeyPhrase' => getColumnValue($columnValues, 'text_mkrhy2ks'),
|
||||||
|
], fn($value) => $value !== null && $value !== '');
|
||||||
|
if (empty($productData['ProductName'])) {
|
||||||
|
error_log("Geen productnaam gevonden voor item {$itemId}. Actie wordt gestopt.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ($wefactProductId) {
|
||||||
|
error_log("WeFact Product ID {$wefactProductId} gevonden. Product wordt bijgewerkt...");
|
||||||
|
$productData['ProductCode'] = $wefactProductId;
|
||||||
|
$response = $wefactApi->sendRequest('product', 'edit', $productData);
|
||||||
|
} else {
|
||||||
|
error_log("Geen WeFact Product ID gevonden. Nieuw product wordt aangemaakt...");
|
||||||
|
$response = $wefactApi->sendRequest('product', 'add', $productData);
|
||||||
|
if ($response['status'] === 'success' && isset($response['product']['ProductCode'])) {
|
||||||
|
$newProductId = $response['product']['ProductCode'];
|
||||||
|
error_log("Product succesvol aangemaakt met ID: {$newProductId}. Monday wordt bijgewerkt.");
|
||||||
|
updateMondayColumn($vakkrachtenBoardId, $itemId, $wefactProductIdColumn, $newProductId, $mondayApiToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($response['status'] === 'error') {
|
||||||
|
error_log("FOUT van WeFact (Product): " . json_encode($response['errors']));
|
||||||
|
} else {
|
||||||
|
error_log("SUCCES! Reactie van WeFact (Product): " . json_encode($response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Webserver & Routing Logica ---
|
||||||
|
$requestBody = file_get_contents('php://input');
|
||||||
|
$data = json_decode($requestBody, true);
|
||||||
|
|
||||||
|
if (isset($data['challenge'])) {
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode(['challenge' => $data['challenge']]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data['event'])) {
|
||||||
|
$event = $data['event'];
|
||||||
|
$boardId = (int)$event['boardId'];
|
||||||
|
$itemId = (int)$event['pulseId'];
|
||||||
|
|
||||||
|
// --- NIEUW: LOCK MECHANISME OM RACE CONDITIONS TE VOORKOMEN ---
|
||||||
|
$lockFileDir = __DIR__ . '/locks';
|
||||||
|
if (!is_dir($lockFileDir)) {
|
||||||
|
mkdir($lockFileDir, 0775, true);
|
||||||
|
}
|
||||||
|
$lockFilePath = $lockFileDir . '/' . $itemId . '.lock';
|
||||||
|
$lockFileHandle = fopen($lockFilePath, 'c');
|
||||||
|
|
||||||
|
if ($lockFileHandle === false) {
|
||||||
|
error_log("Kon geen lock file handle aanmaken voor item {$itemId}.");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probeer een exclusieve, niet-blokkerende lock te krijgen.
|
||||||
|
if (!flock($lockFileHandle, LOCK_EX | LOCK_NB)) {
|
||||||
|
// Als het niet lukt, is een ander proces al bezig met dit item.
|
||||||
|
error_log("Process voor item {$itemId} is al bezig. Deze instantie wordt gestopt om duplicaten te voorkomen.");
|
||||||
|
fclose($lockFileHandle);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zorg ervoor dat de lock altijd wordt vrijgegeven, zelfs bij een fout.
|
||||||
|
register_shutdown_function(function() use ($lockFileHandle, $lockFilePath) {
|
||||||
|
flock($lockFileHandle, LOCK_UN); // Geef lock vrij
|
||||||
|
fclose($lockFileHandle);
|
||||||
|
unlink($lockFilePath); // Verwijder het lock-bestand
|
||||||
|
});
|
||||||
|
// --- EINDE LOCK MECHANISME ---
|
||||||
|
|
||||||
|
$itemData = getMondayItemData($itemId, $mondayApiToken);
|
||||||
|
|
||||||
|
if ($itemData) {
|
||||||
|
$wefactApi = new WeFactAPI($wefactApiKey);
|
||||||
|
|
||||||
|
if ($boardId === $bedrijvenBoardId) {
|
||||||
|
handleBedrijven($itemData, $itemId, $wefactApi, $mondayApiToken);
|
||||||
|
} elseif ($boardId === $vakkrachtenBoardId) {
|
||||||
|
handleVakkrachten($itemData, $itemId, $wefactApi, $mondayApiToken);
|
||||||
|
} else {
|
||||||
|
error_log("Webhook ontvangen van een niet-geconfigureerd bord: ID {$boardId}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_code(200);
|
||||||
|
echo json_encode(['message' => 'Event succesvol ontvangen.']);
|
||||||
|
?>
|
||||||
67
wefact_api.php
Normal file
67
wefact_api.php
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class WeFactAPI
|
||||||
|
{
|
||||||
|
private $url;
|
||||||
|
private $apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor die de API sleutel en URL ontvangt.
|
||||||
|
* @param string $apiKey Jouw WeFact API sleutel.
|
||||||
|
* @param string $apiUrl De basis-URL voor de WeFact API.
|
||||||
|
*/
|
||||||
|
public function __construct(string $apiKey, string $apiUrl = 'https://api.mijnwefact.nl/v2/')
|
||||||
|
{
|
||||||
|
$this->url = $apiUrl;
|
||||||
|
$this->apiKey = $apiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verstuurt een verzoek naar de WeFact API.
|
||||||
|
* @param string $controller De naam van de controller (bv. 'debtor').
|
||||||
|
* @param string $action De actie die uitgevoerd moet worden (bv. 'add').
|
||||||
|
* @param array $params Een array met parameters voor de call.
|
||||||
|
* @return array Het antwoord van de API.
|
||||||
|
*/
|
||||||
|
public function sendRequest(string $controller, string $action, array $params): array
|
||||||
|
{
|
||||||
|
// Voeg de verplichte parameters toe
|
||||||
|
$params['api_key'] = $this->apiKey;
|
||||||
|
$params['controller'] = $controller;
|
||||||
|
$params['action'] = $action;
|
||||||
|
|
||||||
|
$ch = curl_init();
|
||||||
|
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, '10');
|
||||||
|
curl_setopt($ch, CURLOPT_POST, 1);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
|
||||||
|
|
||||||
|
// Let op: SSL-verificatie nooit uitschakelen in een productieomgeving.
|
||||||
|
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||||
|
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||||
|
|
||||||
|
$curlResp = curl_exec($ch);
|
||||||
|
$curlError = curl_error($ch);
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($curlError != '') {
|
||||||
|
$result = [
|
||||||
|
'controller' => 'invalid', 'action' => 'invalid',
|
||||||
|
'status' => 'error', 'date' => date('c'),
|
||||||
|
'errors' => [$curlError]
|
||||||
|
];
|
||||||
|
} elseif ($httpCode >= 400) { // Vang alle client/server fouten af
|
||||||
|
$result = [
|
||||||
|
'controller' => 'invalid', 'action' => 'invalid',
|
||||||
|
'status' => 'error', 'date' => date('c'),
|
||||||
|
'errors' => ["HTTP status code: " . $httpCode, $curlResp]
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
$result = json_decode($curlResp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user