Construir um ShareASale Proxy com Slim PHP Framework

Recentemente, lancei um novo site (e extremamente burro), FootballCropTop.com , que usa a API ShareASale Product Search . Se você já trabalhou com o ShareASale antes, provavelmente está familiarizado com alguns dos obstáculos gerados pela aplicação de suas políticas de segurança. Quero compartilhar uma abordagem que usei para resolver esses obstáculos como parte de um esforço maior para explicar como construí e mantenho um site afiliado automatizado com uma experiência de comércio eletrônico por centavos por mês .

Para acessar a API de pesquisa de produtos oferecida pelo ShareASale, você precisará configurar alguns controles básicos de segurança e acesso em sua conta. Um desses controles é um recurso de lista de permissões de IP. Isso requer que você defina explicitamente todos (até cinco) os endereços IP que terão permissão para acessar a API usando suas credenciais de acesso. Isso significa que você precisará ter um endereço IP estático em algum lugar do mundo.

Para meus projetos pessoais, faço hospedagem leve no Digital Ocean e cada um dos meus Droplets recebe um IP estático, algo pelo qual já estou pagando (~ $ 5 / mês). Para este projeto, decidi construir um pequeno aplicativo proxy em PHP usando o Slim Framework e implantá-lo em um dos meus Droploets existentes.

Meu aplicativo proxy é uma instalação “hello world” do Slim, seguindo o guia de instalação . Depende de apenas uma biblioteca adicional, liga / csv da Liga dos Pacotes Extraordinários .

Além do requisito de lista de permissões de IP estático, o ShareASale também exige que cada solicitação seja assinada com um token criptografado, o que deve ser feito em cada solicitação. Gostaria de usar outras ferramentas (como Postman localmente ou AWS Lambdas na nuvem) para acessar os dados do ShareASale por meio do endereço IP estático associado ao Droplet que hospeda este pequeno aplicativo proxy.

Eu criei um design básico que me permitiria definir minhas informações de credenciais secretas por meio de cabeçalhos e encaminhar uma solicitação por meio do proxy. Por sua vez, o proxy assinará e autenticará a solicitação, buscará os resultados, limpará e formatará uma boa resposta (o ShareASale não é o melhor nesta área) e, finalmente, a enviará de volta ao cliente solicitante. Aqui está o código do meu projeto Slim que faz este trabalho:

<?php

use GuzzleHttpClient;
use LeagueCsvReader;
use SlimHttpRequest;
use SlimHttpResponse;

// Routes

$app
->group('/shareasale', function () {
$this
->get('/{action}', function ($request, $response, $args) {

// Extract our ShareASale query params from inbound request
try {
// 'keyword' => 'crop top',
// 'merchantId' => 1234,
parse_str
($request->getUri()->getQuery(), $query);
} catch (Exception $e) {
$query
= [];
}

// Extract our ShareASale action from the route
$action
= $args['action'];
$serviceUrl
= 'https://api.shareasale.com/x.cfm';

// Extract our ShareASale auth parts from inbound headers
$versionHeaders
= $request->getHeader('x-shareasale-version');
$version
= array_shift($versionHeaders);

$affiliateIdHeaders
= $request->getHeader('x-shareasale-affiliateid');
$affiliateId
= array_shift($affiliateIdHeaders);

$tokenHeaders
= $request->getHeader('x-shareasale-token');
$token
= array_shift($tokenHeaders);

$secretKeyHeaders
= $request->getHeader('x-shareasale-secretkey');
$secretKey
= array_shift($secretKeyHeaders);

// Create our ShareASale authentication from auth parts
$timestamp
= gmdate(DATE_RFC1123);
$signature
= $token.':'.$timestamp.':'.$action.':'.$secretKey;
$signatureHash
= hash("sha256", $signature);

// Prepare our ShareASale request
$headers
= [
'x-ShareASale-Date' => $timestamp,
'x-ShareASale-Authentication' => $signatureHash,
];

$options
= array_merge($query, [
'action' => $action,
'affiliateId' => $affiliateId,
'version' => $version,
'token' => $token,
'format' => 'csv'
]);

// Get our ShareASale response from our request
$client
= new Client();
$res
= $client->request('GET', 'https://api.shareasale.com/x.cfm', [
'headers' => $headers,
'query' => $options
]);

$status
= $res->getStatusCode();
$json
= [];

// Handle and format our ShareASale response
try {
$csv
= Reader::createFromString($res->getBody())->jsonSerialize();

$csvHeaders
= array_shift($csv);

$csvHeaders
= array_map(function ($property) {
$property
= preg_replace('/([a-z])([A-Z])/',"$1_$2", $property);
$property
= strtolower($property);
$property
= preg_replace('/ /', '_', $property);

return $property;
}, $csvHeaders);

array_walk
($csv, function ($csvRow) use ($csvHeaders, &$json) {
array_push
($json, array_combine($csvHeaders, $csvRow));
});
} catch (Exception $e) {
$status
= $e->getCode();
$json
['status'] = $status;
$json
['message'] = (string) $res->getBody();
$json
['error'] = $e->getMessage();
}

// Return our reformatted ShareASale response
return $response->withStatus($status)->withJson($json);
});
});

Com esse pequeno pedaço de infraestrutura no lugar, estou livre para começar a consumir os dados de feed do produto basicamente de qualquer lugar.


Você pode ler mais sobre este projeto no passo a passo técnico completo .