Een koppeling maken met mijn platform. Ik doe nu het volgende:
1. Tabel met bol accounts ( 10 stuks )
2. Via een cronjob door de bol accounts loopen en API calls maken om de orders op te halen.
Er zitten rate limits aan de API calls bijv 5 calls per minuut mag je maken.
Ik vind de code die ik nu heb niet stabiel genoeg en hoor graag hoe dit beter kan.
Zo zijn er klanten met echt veel orders waardoor die soms vastloopt lijkt het, is een cronjob met een while loop wel de beste manier, lijkt wel alsof die oneindig doorgaat maar niet van alle klanten de orders ophaalt.
Hoor het graag. Zit even niet op de pc dus heb geen voorbeeld code, update ik morgen even.
Na een dag testen ben ik nu achter een vervelender probleem gekomen:
Het lijkt erop dat de Bol API mijn server een x aantal aanvragen geeft per aanvraag aangezien ik heel erg vaak een curl error 7 ontvang, (geen connectie), ik krijg dan niets terug en als ik een paar keer refresh reageert de api soms wel.
Via postman krijg ik elke aanvraag antwoord.
Via een andere server van mij ook.
Moet dus bij mijn server liggen, kan het zijn dat bol een limit kan zetten per ipadres buiten de standaard rate limits, zo ja, kan ik nooit betrouwbaar de api gebruiken...
Iemand een idee wat te doen buiten bol contacteren (hopeloze zaak)?
Het leek inderdaad erop dat ik een IP ban had, na contact is dit zoals ik nu kan zien opgelost, ik krijg weer elke aanvraag antwoord. Ik kan dus weer verder met de oorsprong van het topic:
Ik heb nu de oplossing van Ivo gebouwd als het goed is?
Verder heb ik extra checks ingebouwd om te zorgen dat ik binnen de rate limits blijf van de API alleen weet iemand hoe je vanuit een header array een waarde kan selecteren, zie regel " $iRateLimitRemaining = ''; // Hier moet de waarde komen vanuit de array met key: x-ratelimit-remaining", ben daar wat slecht in.
Dan het belangrijkste, hoe schaalbaar is deze gehele oplossing, zo lijkt het mij bij deze oplossing dat ik bij 1000 klanten alsnog 1000 minuten moet wachten alvorens iedere klant zijn laatste bestellingen binnen heeft.
<?php
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
error_reporting(E_ALL);
$database = new database();
// rate limit 25 per minuut.
// Als klant net nieuw is moet die de eerste zijn waarvan we de orders ophalen.
// Pak 1 klant waarbij bol gekoppeld is EN ER GEEN LOCK OP ZIT, order bij nieuwste klant en eerst gerunde cron.
$q = 'SELECT * FROM bol_accounts WHERE bol_token != "" AND status = 1 AND cron_orders_lock = 0 ORDER BY cron_orders_start ASC, ID DESC LIMIT 1 ';
$result = $database->query($q);
while ($rows = mysqli_fetch_assoc($result))
{
// lock deze bol account zodat die niet weer gestart gaat worden.
$q = 'UPDATE bol_accounts SET cron_orders_lock = 1 WHERE id = "'.$rows['id'].'"';
$database->query($q);
// log de start tijd.
$q = 'UPDATE bol_accounts SET cron_orders_start = NOW() WHERE id = "'.$rows['id'].'"';
$database->query($q);
// check of sessie nog actief is, zo niet vraag nieuwe token op.
getCheckAccountNewBolToken($rows['klant_id'], $rows['id']);
$counter = 0;
$x = 1;
// pak tot 50 pagina's mee = 50x50. 2500 orders.
for ($x; $x <= 50; $x++)
{
$lastStatusCode = '';
$iRateLimitRemaining = ''; // 24
$iRateLimitReset = ''; //in seconden
if ($lastStatusCode != "200")
{
die("Geen goede response meer vanuit de bol api".$lastStatusCode);
}
if ($iRateLimitRemaining < 2)
{
sleep($iRateLimitReset); // Sleep tot de rate limit reset is.
}
$orderLink = "https://api.bol.com/retailer/shipments?fulfilment-method=FBR&page=".$x.""; //get all orders tot response leeg is wat betekent dat er geen orders meer zijn
$headers = ['Accept' => 'application/json'];
$ch = curl_init($orderLink);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // toegevoegd om headers te krijgen
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET' );
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/vnd.retailer.v10+json',
'Authorization: Bearer ' . getAccountBolToken($rows['id'], $rows['klant_id']),
'Accept: application/vnd.retailer.v10+json',
));
// execute!
$response = curl_exec($ch);
$err = curl_error($ch);
$sResponseCode = curl_getinfo( $ch , CURLINFO_RESPONSE_CODE );
$lastStatusCode = $sResponseCode;
// Return headers seperatly from the Response Body
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $header_size);
$body = substr($response, $header_size);
$headers = explode("\r\n", $headers); // The seperator used in the Response Header is CRLF (Aka. \r\n)
$headers = array_filter($headers);
foreach ($headers as $value)
{
//echo $value."<br/>";
$iRateLimitRemaining = ''; // Hier moet de waarde komen vanuit de array met key: x-ratelimit-remaining
$iRateLimitReset = ''; // Hier moet de waarde komen vanuit de array met key: x-ratelimit-reset
}
// close the connection, release resources used
curl_close($ch);
$json = json_decode($body,true);
// teveel aanvragen melding check
if ($json['status'] == "429")
{
// limiet bereikt.
echo "limiet bereikt 429";
die("limiet bereikt van 429"); // Betekent dat we iets niet goed hebben gedaan bij de rate limits controle.
}
if ($response == "{}" || $response == "{ }" || $response == "")
{
echo 'Bol account ID:'.$rows['id']." heeft geen orders meer vanaf pagina:".$x;
$paginageenOrders = $x;
$x = 51;
//die(); moet ik hier die() doen of stopt die vanzelf omdat de waarde 51 is?
// geen orders meer.
}
else
{
foreach($json as $key=>$val)
{
$OpgehaaldeOrders += count($json['shipments']);
$OpgehaaldeOrdersCronTotal += count($json['shipments']);
if(is_array($val))
{
foreach($val as $key=>$val)
{
if(is_array($val))
{
$sShipmentId = $val['shipmentId'];
$shipmentOrderId = $val['order']['orderId'];
$aantalOpgehaaldeorders++;
// check of order al is geimporteerd, zo niet importeer.
$qt = 'SELECT shipment_id FROM bol_orders WHERE shipment_id = "'.$sShipmentId.'" AND klant_id = "'.$rows['klant_id'].'" AND bol_account_id = "'.$rows['id'].'" ';
$resultt = $database->query($qt);
if (mysqli_num_rows($resultt) == 0)
{
// voer gegevens in
$q = 'INSERT INTO bol_orders (klant_id, bol_account_id,shipment_id,shipment_order_id, created_at) VALUES ("'.$rows['klant_id'].'", "'.$rows['id'].'", "'.$sShipmentId.'", "'.$shipmentOrderId.'", NOW())';
echo $q.'<br/>';
$database->query($q);
}
$counter++;
}
}
}
else
{
}
}
}
echo $rows['id']." gelooped"."<br/>";
}
// log de eind tijd.
$q = 'UPDATE bol_accounts SET cron_orders_eind = NOW() WHERE id = "'.$rows['id'].'"';
$database->query($q);
// release de lock zodat die weer op nieuw kan.
$q = 'UPDATE bol_accounts SET cron_orders_lock = 0 WHERE id = "'.$rows['id'].'"';
$database->query($q);
}
?>
nb: het kan ook goed zijn dan de new line alleen maar \n is!
Regel 92 doet mi. niet heel veel nuttigs.
Maar dan is wel de plek om eens te kijken wat er in je array zit:
<?php var_dump($headerArray); ?>
Daaropvolgend probeer je iets met een foreach lus door het array te gaan.
Dat is niet de handigste methode:
Zie wat er uit de var_dump() komt en het je dan
$iRateLimitRemaining = $headerArray[....]
kunt gebruiken.
Aangezien deze header mogelijk ontbreekt zou je ook kunnen doen
(en wat op .... moet komen, mag je zelf even uit var_dump zoeken.
[size=xsmall]Toevoeging op 13/12/2023 10:52:31:[/size]
enne:
429 is niet de grootte van de limit, maar een status-code die aangeeft "er is een request limiet bereikt".
Net als 404 staat voor Not Found of 401 voor "geen toegang"
en 418 voor "ik ben een theepot"
Ik heb het nu volledig werkend en het is een stuk meer bulletproof met de eerste versie maar zoals gezegd wil ik het graag gelijk future proof hebben daar ik 30% van me klanten ben verloren door onzorgvuldige code met teveel bugs
Daarom mijn zorgen of de oplossing met een lock op de bol_accounts tabel future proof is.
1. Cronjob runt deze code elke minuut en pakt dus steeds een bol account waar geen lock op zit en welke het laatst is gerunt.
2. Naar mijn mening duurt deze manier alsnog 1000minuten bij 1000 klanten.
3. Wat gebeurd er in een cronjob welke elke minuut runt als er een klant is welke door de rate limit 2/3x x aantal seconden moet “sleepen” wachten? Maakt de cron de huidige job af of start die gewoon opnieuw de code na 1minuut waardoor klanten dus niet volledige orders te zien krijgen toch?
4. Dit is 1 van de vele api points, zo heb ik ook nog moelijkere zoals “offers” producten welke je in 3 stappen moet doen bij de bol API namelijk:
4.1 vraag een excell export aan.
4.2 je krijgt dan een status ID welke je zelf moet checken als een soort webhook of deze de status “success” heeft.
4.3 als deze succes heeft krijg je een excell bestand met de producten van de retailer.
Ik wilde dezelfde oplossing toepassen maar misschien weet er iemand iets beters? Het doel is dat een klant zsm zijn bestaande en daarna nieuw toegevoegde producten te zien krijgt.
Sorry voor de vragen maar er zitten hier programmeurs met jaren meer ervaring als ik dus advies is altijd welkom.
Is het nodig om je al zorgen te maken over 1.000 klanten?
Ik denk dat je dan wel een speciaal contract met Bol.com hebt die alles naar jou zou pushen zodat jij je geen druk hoeft te maken om limitrates. ;-)
Voor processen waarbij het eventjes duurt voordat de data compleet is (als er van alles bij verzameld moet worden), zou ik de data nog even buiten beeld houden totdat alles compleet is. Je kan een flag plaatsen met 'completed'.