362 lines
19 KiB
PHP
Executable File
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]);
|
|
}
|