Files
wevads-platform/scripts/php-offer-import.php
2026-02-26 04:53:11 +01:00

362 lines
19 KiB
PHP
Executable File

<?php
/**
* WEVADS OFFER IMPORT API v4.0 - PHP-based (bypasses Java)
* Supports: Everflow (Double M), CAKE (CX3 Ads), HitPath (prepared)
*/
error_reporting(E_ERROR);
ini_set('max_execution_time', 300);
header('Content-Type: application/json; charset=utf-8');
if (!isset($_SESSION)) session_start();
function oiDb() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('pgsql:host=localhost;dbname=adx_system', 'admin', 'admin123');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $pdo;
}
function oiHttp($url, $headers = [], $timeout = 30) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_USERAGENT => 'WEVADS-Importer/4.0'
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
return ['body' => $resp, 'code' => $code, 'error' => $err];
}
function oiNextId($table) {
return (int)oiDb()->query("SELECT COALESCE(MAX(id),0)+1 FROM $table")->fetchColumn();
}
function oiGetNetwork($id) {
$stmt = oiDb()->prepare("SELECT * FROM affiliate.affiliate_networks WHERE id = ? AND status = 'Activated'");
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
function oiUpsertOffer($network, $data) {
$today = date('Y-m-d');
$un = 'import@wevads';
$prodId = (string)$data['production_id'];
$stmt = oiDb()->prepare("SELECT id FROM affiliate.offers WHERE affiliate_network_id = ? AND production_id = ?");
$stmt->execute([$network['id'], $prodId]);
$existing = $stmt->fetch();
$payout = number_format(floatval(str_replace(['$','EUR'], '', $data['payout'] ?? '0')), 2, '.', '');
if ($existing) {
$sql = "UPDATE affiliate.offers SET name=?, campaign_id=?, payout=?, countries=?, description=?,
offer_url=?, unsub_url=?, default_suppression_link=?, status='Activated',
last_updated_date=?, last_updated_by=? WHERE id=?";
oiDb()->prepare($sql)->execute([
$data['name'], $data['campaign_id'] ?? $prodId, $payout,
$data['countries'] ?? '', base64_encode($data['description'] ?? ''),
$data['tracking_url'] ?? '', $data['unsub_url'] ?? '',
$data['suppression_link'] ?? '', $today, $un, $existing['id']
]);
return (int)$existing['id'];
} else {
$nid = oiNextId('affiliate.offers');
$sql = "INSERT INTO affiliate.offers (id, name, affiliate_network_id, affiliate_network_name,
production_id, campaign_id, payout, type, countries, description, rules,
offer_url, unsub_url, default_suppression_link, status, created_by, created_date,
last_updated_by, last_updated_date, available_days, verticals_ids, expiration_date)
VALUES (?,?,?,?,?,?,?,'CPA',?,?,?,?,?,?,'Activated',?,?,?,?,'mon,tue,wed,thu,fri,sat,sun','',?)";
oiDb()->prepare($sql)->execute([
$nid, $data['name'], $network['id'], $network['name'],
$prodId, $data['campaign_id'] ?? $prodId, $payout,
$data['countries'] ?? '', base64_encode($data['description'] ?? ''), base64_encode(''),
$data['tracking_url'] ?? '', $data['unsub_url'] ?? '',
$data['suppression_link'] ?? '', $un, $today, $un, $today,
date('Y-m-d', strtotime('+1 year'))
]);
return $nid;
}
}
function oiUpsertCreative($dbOfferId, $networkId, $index, $html) {
$today = date('Y-m-d');
$name = "offer_creative_" . $index;
$stmt = oiDb()->prepare("SELECT id FROM affiliate.creatives WHERE offer_id = ? AND name = ?");
$stmt->execute([$dbOfferId, $name]);
$ex = $stmt->fetch();
if ($ex) {
oiDb()->prepare("UPDATE affiliate.creatives SET value=?, last_updated_date=?, last_updated_by='import@wevads' WHERE id=?")
->execute([base64_encode($html), $today, $ex['id']]);
return (int)$ex['id'];
} else {
$nid = oiNextId('affiliate.creatives');
oiDb()->prepare("INSERT INTO affiliate.creatives (id, offer_id, affiliate_network_id, name, value, status, created_by, created_date, last_updated_by, last_updated_date) VALUES (?,?,?,?,?,'Activated','import@wevads',?,'import@wevads',?)")
->execute([$nid, $dbOfferId, $networkId, $name, base64_encode($html), $today, $today]);
return $nid;
}
}
function oiUpsertLink($dbOfferId, $networkId, $creativeId, $type, $value) {
$today = date('Y-m-d');
$stmt = oiDb()->prepare("SELECT id FROM affiliate.links WHERE offer_id = ? AND creative_id = ? AND type = ?");
$stmt->execute([$dbOfferId, $creativeId, $type]);
$ex = $stmt->fetch();
if ($ex) {
oiDb()->prepare("UPDATE affiliate.links SET value=?, last_updated_date=? WHERE id=?")->execute([$value, $today, $ex['id']]);
} else {
$nid = oiNextId('affiliate.links');
oiDb()->prepare("INSERT INTO affiliate.links (id, offer_id, affiliate_network_id, creative_id, type, value, status, created_by, created_date, last_updated_by, last_updated_date) VALUES (?,?,?,?,?,?,'Activated','import@wevads',?,'import@wevads',?)")
->execute([$nid, $dbOfferId, $networkId, $creativeId, $type, $value, $today, $today]);
}
}
function oiUpsertFromName($dbOfferId, $networkId, $index, $value) {
$today = date('Y-m-d');
$stmt = oiDb()->prepare("SELECT id FROM affiliate.from_names WHERE offer_id = ? AND value = ?");
$stmt->execute([$dbOfferId, $value]);
if (!$stmt->fetch()) {
$nid = oiNextId('affiliate.from_names');
oiDb()->prepare("INSERT INTO affiliate.from_names (id, offer_id, affiliate_network_id, name, value, status, created_by, created_date, last_updated_by, last_updated_date) VALUES (?,?,?,?,?,'Activated','import@wevads',?,'import@wevads',?)")
->execute([$nid, $dbOfferId, $networkId, "offer_from_name_$index", $value, $today, $today]);
}
}
function oiUpsertSubject($dbOfferId, $networkId, $index, $value) {
$today = date('Y-m-d');
$stmt = oiDb()->prepare("SELECT id FROM affiliate.subjects WHERE offer_id = ? AND value = ?");
$stmt->execute([$dbOfferId, $value]);
if (!$stmt->fetch()) {
$nid = oiNextId('affiliate.subjects');
oiDb()->prepare("INSERT INTO affiliate.subjects (id, offer_id, affiliate_network_id, name, value, status, created_by, created_date, last_updated_by, last_updated_date) VALUES (?,?,?,?,?,'Activated','import@wevads',?,'import@wevads',?)")
->execute([$nid, $dbOfferId, $networkId, "offer_subject_$index", $value, $today, $today]);
}
}
// ============== EVERFLOW ==============
function importEverflow($network, $offerIds, $maxCreatives, $getAll) {
$apiKey = $network['api_key'];
$apiUrl = rtrim($network['api_url'], '/');
$imported = 0; $updated = 0; $errors = [];
$resp = oiHttp($apiUrl . '/v1/affiliates/alloffers', ['X-Eflow-API-Key: ' . $apiKey]);
if ($resp['code'] != 200) return ['imported'=>0,'updated'=>0,'errors'=>['Everflow HTTP '.$resp['code']]];
$body = json_decode($resp['body'], true);
if (!is_array($body) || empty($body['offers'])) return ['imported'=>0,'updated'=>0,'errors'=>['No offers from Everflow']];
$offers = $body['offers'];
$total = count($offers);
if (!$getAll && !empty($offerIds)) {
$offers = array_values(array_filter($offers, function($o) use ($offerIds) {
return in_array((string)($o['network_offer_id']??''), $offerIds) || in_array((string)($o['network_id']??''), $offerIds);
}));
}
foreach ($offers as $offer) {
try {
$oid = $offer['network_offer_id'] ?? $offer['network_id'] ?? '';
$countries = '';
if (!empty($offer['countries'])) {
$cc = [];
foreach ((array)$offer['countries'] as $c) { $cc[] = is_array($c) ? ($c['country_code']??'') : (string)$c; }
$countries = implode('/', array_filter($cc));
}
$chk = oiDb()->prepare("SELECT id FROM affiliate.offers WHERE affiliate_network_id=? AND production_id=?");
$chk->execute([$network['id'], (string)$oid]);
$isUp = (bool)$chk->fetch();
$dbOid = oiUpsertOffer($network, [
'production_id'=>$oid, 'campaign_id'=>(string)$oid, 'name'=>$offer['name']??'',
'payout'=>$offer['payout']??0, 'tracking_url'=>$offer['tracking_url']??'',
'unsub_url'=>'', 'suppression_link'=>$offer['preview_url']??'',
'description'=>$offer['description']??'', 'countries'=>$countries
]);
if ($isUp) $updated++; else $imported++;
if ($maxCreatives != 0) {
$dr = oiHttp($apiUrl.'/v1/affiliates/offers/'.$oid, ['X-Eflow-API-Key: '.$apiKey], 15);
if ($dr['code']==200) {
$det = json_decode($dr['body'], true);
if (is_array($det)) {
$ci = 0;
if (!empty($det['creatives'])) {
foreach ($det['creatives'] as $cr) {
if ($maxCreatives>0 && $ci>=$maxCreatives) break;
$ci++;
$h = $cr['html_code'] ?? $cr['content'] ?? '';
if (empty($h) && !empty($offer['tracking_url']))
$h = '<a href="'.htmlspecialchars($offer['tracking_url']).'">'.htmlspecialchars($offer['name']??'Offer').'</a>';
if (!empty($h)) {
$cid = oiUpsertCreative($dbOid, $network['id'], $ci, $h);
if (!empty($offer['tracking_url'])) oiUpsertLink($dbOid, $network['id'], $cid, 'preview', $offer['tracking_url']);
}
}
}
if ($ci==0 && !empty($offer['tracking_url'])) {
$h = '<a href="'.htmlspecialchars($offer['tracking_url']).'">'.htmlspecialchars($offer['name']??'Offer').'</a>';
$cid = oiUpsertCreative($dbOid, $network['id'], 1, $h);
oiUpsertLink($dbOid, $network['id'], $cid, 'preview', $offer['tracking_url']);
}
if (!empty($det['email_instructions']['from_lines'])) {
$fi = 0;
foreach ($det['email_instructions']['from_lines'] as $fl) {
$fi++; $v = is_array($fl)?($fl['value']??$fl['name']??''):(string)$fl;
if (!empty(trim($v))) oiUpsertFromName($dbOid, $network['id'], $fi, trim($v));
}
}
if (!empty($det['email_instructions']['subject_lines'])) {
$si = 0;
foreach ($det['email_instructions']['subject_lines'] as $sl) {
$si++; $v = is_array($sl)?($sl['value']??$sl['name']??''):(string)$sl;
if (!empty(trim($v))) oiUpsertSubject($dbOid, $network['id'], $si, trim($v));
}
}
}
}
usleep(100000);
}
} catch (\Exception $e) { $errors[] = "Offer $oid: ".$e->getMessage(); }
}
return ['imported'=>$imported,'updated'=>$updated,'total_available'=>$total,'processed'=>count($offers),'errors'=>$errors];
}
// ============== CAKE (CX3) ==============
function importCake($network, $offerIds, $maxCreatives, $getAll) {
$apiKey = $network['api_key'];
$apiUrl = str_replace('http://', 'https://', rtrim($network['api_url'], '/'));
$affId = $network['affiliate_id'];
$imported = 0; $updated = 0; $errors = [];
$feedUrl = $apiUrl.'/Offers/Feed.asmx/OfferFeed?api_key='.urlencode($apiKey)
.'&affiliate_id='.$affId.'&offer_status_id=0&campaign_name=&media_type_category_id=0&vertical_category_id=0&vertical_id=0&offer_tag_id=0&start_at_row=1&row_limit=0';
$resp = oiHttp($feedUrl, [], 60);
if ($resp['code'] != 200 || strpos($resp['body'],'temporarily unavailable')!==false) {
return ['imported'=>0,'updated'=>0,'errors'=>['CX3/CAKE API unavailable (HTTP '.$resp['code'].'). Try again later.'],'total_available'=>0];
}
$bodyClean = preg_replace('/^\xEF\xBB\xBF/', '', $resp['body']);
$xml = @simplexml_load_string($bodyClean);
$offers = [];
if ($xml) {
$nodes = $xml->xpath('//offer') ?: $xml->xpath('//Offer') ?: $xml->xpath('//row') ?: [];
foreach ($nodes as $n) {
$o = [];
$o['offer_id'] = (string)($n->offer_id ?? $n->OfferId ?? '');
$o['campaign_id'] = (string)($n->campaign_id ?? $n->CampaignId ?? $o['offer_id']);
$o['name'] = (string)($n->offer_name ?? $n->OfferName ?? $n->name ?? '');
$o['payout'] = (string)($n->payout ?? $n->Payout ?? '0');
$o['offer_link'] = (string)($n->offer_link ?? $n->OfferLink ?? $n->unique_link ?? '');
$o['description'] = (string)($n->description ?? $n->Description ?? '');
$o['suppression_link'] = (string)($n->suppression_link ?? $n->SuppressionLink ?? '');
$o['countries'] = '';
$ccn = $n->xpath('.//allowed_countries/country') ?: $n->xpath('.//country') ?: [];
$cc = [];
foreach ($ccn as $cn) { $c=(string)($cn->country_code??$cn->CountryCode??$cn); if(!empty($c)&&strlen($c)<=3)$cc[]=$c; }
$o['countries'] = implode('/', $cc);
if (empty($o['offer_link']) && !empty($o['campaign_id']))
$o['offer_link'] = 'https://e36lbat.com/?offer_id='.$o['campaign_id'].'&aff_id='.$affId;
if (!empty($o['offer_id'])||!empty($o['campaign_id'])) $offers[] = $o;
}
}
$total = count($offers);
if ($total == 0) return ['imported'=>0,'updated'=>0,'errors'=>['No offers parsed (body: '.strlen($resp['body']).'b)'],'total_available'=>0];
if (!$getAll && !empty($offerIds)) {
$offers = array_values(array_filter($offers, function($o) use ($offerIds) {
return in_array($o['offer_id'], $offerIds) || in_array($o['campaign_id'], $offerIds);
}));
}
foreach ($offers as $offer) {
try {
$pid = $offer['offer_id'] ?: $offer['campaign_id'];
$cid = $offer['campaign_id'] ?: $offer['offer_id'];
$chk = oiDb()->prepare("SELECT id FROM affiliate.offers WHERE affiliate_network_id=? AND production_id=?");
$chk->execute([$network['id'], $pid]);
$isUp = (bool)$chk->fetch();
$dbOid = oiUpsertOffer($network, [
'production_id'=>$pid, 'campaign_id'=>$cid, 'name'=>$offer['name'],
'payout'=>$offer['payout'], 'tracking_url'=>$offer['offer_link'],
'unsub_url'=>$offer['suppression_link'], 'suppression_link'=>$offer['suppression_link'],
'description'=>$offer['description'], 'countries'=>$offer['countries']
]);
if ($isUp) $updated++; else $imported++;
if ($maxCreatives != 0 && !empty($cid)) {
$cUrl = $apiUrl.'/Offers/Creative.asmx/Creatives?api_key='.urlencode($apiKey).'&affiliate_id='.$affId.'&campaign_id='.$cid;
$cr = oiHttp($cUrl, [], 15);
if ($cr['code']==200) {
$cx = @simplexml_load_string(preg_replace('/^\xEF\xBB\xBF/','',$cr['body']));
if ($cx) {
$ci = 0;
foreach ($cx->xpath('//creative')?:$cx->xpath('//Creative')?:[] as $cn) {
if ($maxCreatives>0 && $ci>=$maxCreatives) break; $ci++;
$h = (string)($cn->html_code??$cn->HtmlCode??$cn->content??'');
$cl = (string)($cn->unique_link??$cn->UniqueLink??'');
if (empty($h)&&!empty($cl)) $h='<a href="'.htmlspecialchars($cl).'">'.htmlspecialchars($offer['name']).'</a>';
if (empty($h)&&!empty($offer['offer_link'])) $h='<a href="'.htmlspecialchars($offer['offer_link']).'">'.htmlspecialchars($offer['name']).'</a>';
if (!empty($h)) {
$creaId = oiUpsertCreative($dbOid, $network['id'], $ci, $h);
$lv = !empty($cl)?$cl:$offer['offer_link'];
if (!empty($lv)) oiUpsertLink($dbOid, $network['id'], $creaId, 'preview', $lv);
}
}
}
}
$gUrl = $apiUrl.'/Offers/Feed.asmx/GetCampaign?api_key='.urlencode($apiKey).'&affiliate_id='.$affId.'&campaign_id='.$cid;
$gr = oiHttp($gUrl, [], 15);
if ($gr['code']==200) {
$gx = @simplexml_load_string(preg_replace('/^\xEF\xBB\xBF/','',$gr['body']));
if ($gx) {
$fi=0; foreach ($gx->xpath('//from_line')?:$gx->xpath('//FromLine')?:[] as $fl) {
$fi++; $v=trim((string)$fl); if(!empty($v)) oiUpsertFromName($dbOid,$network['id'],$fi,$v);
}
$si=0; foreach ($gx->xpath('//subject_line')?:$gx->xpath('//SubjectLine')?:[] as $sl) {
$si++; $v=trim((string)$sl); if(!empty($v)) oiUpsertSubject($dbOid,$network['id'],$si,$v);
}
}
}
usleep(100000);
}
} catch (\Exception $e) { $errors[] = "Offer ".($offer['offer_id']??'?').": ".$e->getMessage(); }
}
return ['imported'=>$imported,'updated'=>$updated,'total_available'=>$total,'processed'=>count($offers),'errors'=>$errors];
}
// ============== ROUTER ==============
$networkId = intval($_POST['affiliate-network-id'] ?? $_GET['network_id'] ?? 0);
$getAll = ($_POST['get-all'] ?? '') === 'on';
$maxCreatives = intval($_POST['max-creatives'] ?? 1);
$offerIdsRaw = $_POST['production-ids'] ?? '';
$offerIds = array_filter(array_map('trim', explode("\n", $offerIdsRaw)));
if ($networkId <= 0) die(json_encode(['status'=>400,'message'=>'Missing affiliate-network-id','httpStatus'=>400]));
$network = oiGetNetwork($networkId);
if (!$network) die(json_encode(['status'=>404,'message'=>"Network $networkId not found",'httpStatus'=>404]));
try {
switch ($network['api_type']) {
case 'everflow': $r = importEverflow($network, $offerIds, $maxCreatives, $getAll); break;
case 'cake': $r = importCake($network, $offerIds, $maxCreatives, $getAll); break;
case 'hitpath': die(json_encode(['status'=>501,'message'=>'HitPath: coming soon','httpStatus'=>501]));
default: die(json_encode(['status'=>400,'message'=>'Unknown type: '.$network['api_type'],'httpStatus'=>400]));
}
$msg = $network['name'].': '.$r['imported'].' new, '.($r['updated']??0).' updated';
if (!empty($r['total_available'])) $msg .= ' (from '.$r['total_available'].' available)';
if (!empty($r['errors'])) $msg .= ' | '.count($r['errors']).' errors';
echo json_encode(['status'=>200,'message'=>$msg,'httpStatus'=>200,'data'=>$r]);
} catch (\Exception $e) {
echo json_encode(['status'=>500,'message'=>'Error: '.$e->getMessage(),'httpStatus'=>500]);
}