first commit
This commit is contained in:
81
classes/UspsV3Client.php
Normal file
81
classes/UspsV3Client.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
class UspsV3Client
|
||||
{
|
||||
private $token;
|
||||
private $isLive;
|
||||
private $baseUrl;
|
||||
|
||||
public function __construct($token, $isLive = false)
|
||||
{
|
||||
$this->token = $token;
|
||||
$this->isLive = $isLive;
|
||||
// URLs from the OpenAPI spec
|
||||
$this->baseUrl = $this->isLive
|
||||
? 'https://apis.usps.com/prices/v3'
|
||||
: 'https://apis-tem.usps.com/prices/v3';
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Domestic Prices v3 API
|
||||
* Endpoint: /base-rates/search
|
||||
*/
|
||||
public function getDomesticRate($payload)
|
||||
{
|
||||
return $this->post('/base-rates/search', $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call International Prices v3 API
|
||||
* Endpoint: /base-rates/search (Under International Base path)
|
||||
* Note: The spec shows a different base URL for International
|
||||
*/
|
||||
public function getInternationalRate($payload)
|
||||
{
|
||||
// International uses a different base URL structure in the spec
|
||||
$intlBaseUrl = $this->isLive
|
||||
? 'https://apis.usps.com/international-prices/v3'
|
||||
: 'https://apis-tem.usps.com/international-prices/v3';
|
||||
|
||||
return $this->post('/base-rates/search', $payload, $intlBaseUrl);
|
||||
}
|
||||
|
||||
private function post($endpoint, $payload, $overrideUrl = null)
|
||||
{
|
||||
$url = ($overrideUrl ? $overrideUrl : $this->baseUrl) . $endpoint;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $this->token,
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
|
||||
return ['error' => 'CURL Error: ' . $error];
|
||||
}
|
||||
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
// Check for HTTP errors (400, 401, 403, etc)
|
||||
if ($httpCode >= 400) {
|
||||
$msg = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown Error';
|
||||
if (isset($data['error']['errors'][0]['detail'])) {
|
||||
$msg .= ' - ' . $data['error']['errors'][0]['detail'];
|
||||
}
|
||||
return ['error' => "HTTP $httpCode: $msg"];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
61
override/modules/zh_uspslabels/zh_uspslabels.php
Normal file
61
override/modules/zh_uspslabels/zh_uspslabels.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Zh_UspsLabelsOverride extends Zh_UspsLabels
|
||||
{
|
||||
/**
|
||||
* Intercept the rate calculation call.
|
||||
*/
|
||||
public function getPackageShippingCost($params, $shipping_cost, $products)
|
||||
{
|
||||
// 1. Check if Bridge Module exists and is active
|
||||
/** @var Usps_Api_Bridge $bridge */
|
||||
$bridge = Module::getInstanceByName('usps_api_bridge');
|
||||
|
||||
if ($bridge && $bridge->active) {
|
||||
|
||||
// 2. Check Debug IP Logic
|
||||
// If configured, only these IPs use the new API.
|
||||
// Everyone else continues using the old logic (parent).
|
||||
if ($bridge->isIpAllowed()) {
|
||||
|
||||
// 3. Attempt to calculate rate via Bridge
|
||||
// We pass '$this' (the original module instance) to access its config settings
|
||||
$newRate = $bridge->calculateRate($params, $shipping_cost, $products, $this);
|
||||
|
||||
// If Bridge returns a valid numeric rate, use it.
|
||||
// If it returns FALSE (api error, no token, etc), fall back to old logic.
|
||||
if ($newRate !== false && $newRate !== null) {
|
||||
return $newRate;
|
||||
}
|
||||
|
||||
$bridge->log("Bridge returned false/null. Falling back to Legacy API.");
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fallback to Legacy Logic
|
||||
return parent::getPackageShippingCost($params, $shipping_cost, $products);
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept the "Check API" button in Back Office
|
||||
*/
|
||||
public function ajaxProcessCheckApiConnection()
|
||||
{
|
||||
/** @var Usps_Api_Bridge $bridge */
|
||||
|
||||
$bridge = Module::getInstanceByName('usps_api_bridge');
|
||||
|
||||
if ($bridge && $bridge->active && $bridge->isIpAllowed()) {
|
||||
// We can implement a specific test function in the bridge later
|
||||
// For now, we just let it connect to OAuth
|
||||
// $bridge->testApiConnection();
|
||||
// return;
|
||||
}
|
||||
|
||||
parent::ajaxProcessCheckApiConnection();
|
||||
}
|
||||
}
|
||||
438
usps_api_bridge.php
Normal file
438
usps_api_bridge.php
Normal file
@@ -0,0 +1,438 @@
|
||||
<?php
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Usps_Api_Bridge extends Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->name = 'usps_api_bridge';
|
||||
$this->tab = 'shipping_logistics';
|
||||
$this->version = '1.0.0';
|
||||
$this->author = 'Panariga';
|
||||
$this->need_instance = 0;
|
||||
$this->bootstrap = true;
|
||||
|
||||
parent::__construct();
|
||||
|
||||
$this->displayName = $this->l('USPS API Bridge (OAuth2)');
|
||||
$this->description = $this->l('Modern OAuth2 Bridge for the legacy ZH USPS Labels module.');
|
||||
|
||||
$this->confirmUninstall = $this->l('Are you sure? This will disable the connection to the new USPS API.');
|
||||
}
|
||||
|
||||
public function install()
|
||||
{
|
||||
return parent::install() &&
|
||||
$this->registerHook('actionAdminControllerSetMedia') && // Just in case we need JS later
|
||||
Configuration::updateValue('USPS_BRIDGE_LIVE_MODE', 0) &&
|
||||
Configuration::updateValue('USPS_BRIDGE_DEBUG_IPS', '') &&
|
||||
Configuration::updateValue('USPS_BRIDGE_LOGGING', 1);
|
||||
}
|
||||
|
||||
public function uninstall()
|
||||
{
|
||||
// Uninstall the override automatically to prevent errors
|
||||
return parent::uninstall() &&
|
||||
Configuration::deleteByName('USPS_BRIDGE_CLIENT_ID') &&
|
||||
Configuration::deleteByName('USPS_BRIDGE_CLIENT_SECRET') &&
|
||||
Configuration::deleteByName('USPS_BRIDGE_ACCESS_TOKEN');
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
if (Tools::isSubmit('submitUspsBridgeConf')) {
|
||||
Configuration::updateValue('USPS_BRIDGE_CLIENT_ID', Tools::getValue('USPS_BRIDGE_CLIENT_ID'));
|
||||
Configuration::updateValue('USPS_BRIDGE_CLIENT_SECRET', Tools::getValue('USPS_BRIDGE_CLIENT_SECRET'));
|
||||
Configuration::updateValue('USPS_BRIDGE_LIVE_MODE', Tools::getValue('USPS_BRIDGE_LIVE_MODE'));
|
||||
Configuration::updateValue('USPS_BRIDGE_DEBUG_IPS', Tools::getValue('USPS_BRIDGE_DEBUG_IPS'));
|
||||
Configuration::updateValue('USPS_BRIDGE_LOGGING', Tools::getValue('USPS_BRIDGE_LOGGING'));
|
||||
|
||||
// Clear token on save to force refresh with new credentials
|
||||
Configuration::deleteByName('USPS_BRIDGE_ACCESS_TOKEN');
|
||||
Configuration::deleteByName('USPS_BRIDGE_TOKEN_EXPIRY');
|
||||
|
||||
return $this->displayConfirmation($this->l('Settings updated & Token cache cleared'));
|
||||
}
|
||||
|
||||
return $this->renderForm();
|
||||
}
|
||||
|
||||
public function renderForm()
|
||||
{
|
||||
$fields_form = [
|
||||
'form' => [
|
||||
'legend' => [
|
||||
'title' => $this->l('USPS OAuth2 Configuration'),
|
||||
'icon' => 'icon-cogs',
|
||||
],
|
||||
'input' => [
|
||||
[
|
||||
'type' => 'switch',
|
||||
'label' => $this->l('Live Mode (Production API)'),
|
||||
'name' => 'USPS_BRIDGE_LIVE_MODE',
|
||||
'is_bool' => true,
|
||||
'values' => [
|
||||
['id' => 'active_on', 'value' => 1, 'label' => $this->l('Yes')],
|
||||
['id' => 'active_off', 'value' => 0, 'label' => $this->l('No')],
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Consumer Key (Client ID)'),
|
||||
'name' => 'USPS_BRIDGE_CLIENT_ID',
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'text',
|
||||
'label' => $this->l('Consumer Secret'),
|
||||
'name' => 'USPS_BRIDGE_CLIENT_SECRET',
|
||||
'required' => true,
|
||||
],
|
||||
[
|
||||
'type' => 'textarea',
|
||||
'label' => $this->l('Debug Allowed IPs'),
|
||||
'name' => 'USPS_BRIDGE_DEBUG_IPS',
|
||||
'desc' => $this->l('Comma separated IPs. If set, ONLY these IPs will use the New API. Everyone else uses the old module logic. Leave empty to enable for everyone.'),
|
||||
],
|
||||
[
|
||||
'type' => 'switch',
|
||||
'label' => $this->l('Enable API Logging'),
|
||||
'name' => 'USPS_BRIDGE_LOGGING',
|
||||
'is_bool' => true,
|
||||
'values' => [
|
||||
['id' => 'active_on', 'value' => 1, 'label' => $this->l('Yes')],
|
||||
['id' => 'active_off', 'value' => 0, 'label' => $this->l('No')],
|
||||
],
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'title' => $this->l('Save'),
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$helper = new HelperForm();
|
||||
$helper->show_toolbar = false;
|
||||
$helper->table = $this->table;
|
||||
$helper->module = $this;
|
||||
$helper->default_form_language = $this->context->language->id;
|
||||
$helper->identifier = $this->identifier;
|
||||
$helper->submit_action = 'submitUspsBridgeConf';
|
||||
$helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false) . '&configure=' . $this->name . '&tab_module=' . $this->tab . '&module_name=' . $this->name;
|
||||
$helper->token = Tools::getAdminTokenLite('AdminModules');
|
||||
|
||||
$helper->tpl_vars = [
|
||||
'fields_value' => [
|
||||
'USPS_BRIDGE_CLIENT_ID' => Configuration::get('USPS_BRIDGE_CLIENT_ID'),
|
||||
'USPS_BRIDGE_CLIENT_SECRET' => Configuration::get('USPS_BRIDGE_CLIENT_SECRET'),
|
||||
'USPS_BRIDGE_LIVE_MODE' => Configuration::get('USPS_BRIDGE_LIVE_MODE'),
|
||||
'USPS_BRIDGE_DEBUG_IPS' => Configuration::get('USPS_BRIDGE_DEBUG_IPS'),
|
||||
'USPS_BRIDGE_LOGGING' => Configuration::get('USPS_BRIDGE_LOGGING'),
|
||||
],
|
||||
];
|
||||
|
||||
return $helper->generateForm([$fields_form]);
|
||||
}
|
||||
|
||||
/**
|
||||
* THE CORE BRIDGE LOGIC
|
||||
*/
|
||||
public function calculateRate($params, $shipping_cost, $products, $originalModule)
|
||||
{
|
||||
require_once(dirname(__FILE__) . '/classes/UspsV3Client.php');
|
||||
|
||||
// 1. Get OAuth Token
|
||||
$token = $this->getAccessToken();
|
||||
if (!$token) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Identify which Service (Method) PrestaShop is asking for
|
||||
// The old module links PS Carrier ID -> Internal Method Code (e.g. "USA_1")
|
||||
$carrierId = $params->id_carrier;
|
||||
|
||||
// We query the OLD module's table directly to find the code for this carrier
|
||||
$sql = 'SELECT code FROM `' . _DB_PREFIX_ . 'uspsl_method` WHERE id_carrier = ' . (int)$carrierId;
|
||||
$methodCode = Db::getInstance()->getValue($sql);
|
||||
|
||||
if (!$methodCode) {
|
||||
// This carrier isn't a USPS carrier controlled by the module
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Map Old Code to New API Enum
|
||||
$newApiClass = $this->mapServiceCodeToApiClass($methodCode);
|
||||
if (!$newApiClass) {
|
||||
$this->log("Mapping failed for code: " . $methodCode);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Pack the Products (Using Old Module's Logic)
|
||||
// This calculates how many boxes and their dimensions/weights
|
||||
$packedBoxes = $originalModule->getHelper()->getCarrierHelper()->packProducts($products, $params->id);
|
||||
|
||||
if (empty($packedBoxes)) {
|
||||
$this->log("Box packer returned empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5. Initialize API Client
|
||||
$client = new UspsV3Client($token, (bool)Configuration::get('USPS_BRIDGE_LIVE_MODE'));
|
||||
$totalPrice = 0;
|
||||
|
||||
// 6. Get Origin/Dest addresses
|
||||
$originZip = $this->getOriginZip($originalModule);
|
||||
$destAddress = new Address($params->id_address_delivery);
|
||||
|
||||
// Clean zip codes (USPS V3 expects 5 digits for domestic)
|
||||
$originZip = substr(preg_replace('/[^0-9]/', '', $originZip), 0, 5);
|
||||
$destZip = substr(preg_replace('/[^0-9]/', '', $destAddress->postcode), 0, 5);
|
||||
|
||||
$isInternational = ($destAddress->id_country != Country::getByIso('US'));
|
||||
|
||||
// 7. Loop through every box and get a rate
|
||||
foreach ($packedBoxes as $packedBox) {
|
||||
|
||||
// Convert Weight: Old module uses grams internally usually, spec needs Pounds/Ounces
|
||||
// We assume packedBox->getWeight() is in grams based on typical PS behavior
|
||||
// The old module has a UnitConverter class we can borrow
|
||||
$weightInLbs = $originalModule->getHelper()->getUnitConverter()->convertUnitFromTo(
|
||||
$packedBox->getWeight(),
|
||||
'g',
|
||||
'lbs',
|
||||
3
|
||||
);
|
||||
|
||||
// Get Dimensions (in Inches)
|
||||
$box = $packedBox->getBox(); // This returns the Box object
|
||||
// The Box object from BoxPacker library stores dims in mm usually,
|
||||
// we need to convert to Inches.
|
||||
$length = $originalModule->getHelper()->getUnitConverter()->convertUnitFromTo($box->getOuterLength(), 'mm', 'in', 2);
|
||||
$width = $originalModule->getHelper()->getUnitConverter()->convertUnitFromTo($box->getOuterWidth(), 'mm', 'in', 2);
|
||||
$height = $originalModule->getHelper()->getUnitConverter()->convertUnitFromTo($box->getOuterDepth(), 'mm', 'in', 2);
|
||||
|
||||
// Build Payload
|
||||
$payload = [
|
||||
'originZIPCode' => $originZip,
|
||||
'weight' => $weightInLbs,
|
||||
'length' => $length,
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'mailClass' => $newApiClass,
|
||||
'priceType' => 'COMMERCIAL', // Defaulting to Commercial
|
||||
'mailingDate' => date('Y-m-d', strtotime('+1 day')), // Future date is safer
|
||||
'processingCategory' => 'MACHINABLE', // Defaulting to simple logic
|
||||
'rateIndicator' => 'SP' // Single Piece (Standard)
|
||||
];
|
||||
|
||||
// -- HANDLE FLAT RATES --
|
||||
// If the old box name contains "Flat Rate", we must map it to correct rateIndicator
|
||||
// (e.g. "USPS Medium Flat Rate Box" -> "FB")
|
||||
$rateIndicator = $this->mapBoxToRateIndicator($box->getReference()); // Assuming reference holds name
|
||||
if ($rateIndicator) {
|
||||
$payload['rateIndicator'] = $rateIndicator;
|
||||
// Dimensions technically don't matter for Flat Rate, but API might require them anyway
|
||||
}
|
||||
|
||||
if ($isInternational) {
|
||||
$payload['destinationCountryCode'] = Country::getIsoById($destAddress->id_country);
|
||||
$payload['originZIPCode'] = $originZip;
|
||||
// International API needs extra fields? Spec says originZIPCode is required.
|
||||
|
||||
$response = $client->getInternationalRate($payload);
|
||||
} else {
|
||||
$payload['destinationZIPCode'] = $destZip;
|
||||
$payload['destinationEntryFacilityType'] = 'NONE'; // Required by V3
|
||||
|
||||
$response = $client->getDomesticRate($payload);
|
||||
}
|
||||
|
||||
// Process Response
|
||||
if (isset($response['error'])) {
|
||||
$this->log("API Error: " . $response['error']);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($response['totalBasePrice'])) {
|
||||
$totalPrice += (float)$response['totalBasePrice'];
|
||||
} elseif (isset($response['rateOptions'][0]['totalBasePrice'])) {
|
||||
// Sometimes it returns an array of options
|
||||
$totalPrice += (float)$response['rateOptions'][0]['totalBasePrice'];
|
||||
} else {
|
||||
$this->log("API Response missing price: " . print_r($response, true));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add handling fees if any from original module logic
|
||||
return $totalPrice + $shipping_cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Get Origin Zip from Old Module DB
|
||||
*/
|
||||
private function getOriginZip($originalModule)
|
||||
{
|
||||
// The old module stores addresses in `ps_uspsl_address`
|
||||
// We look for the one marked 'origin' = 1
|
||||
$sql = 'SELECT postcode FROM `' . _DB_PREFIX_ . 'uspsl_address` WHERE origin = 1 AND active = 1';
|
||||
$zip = Db::getInstance()->getValue($sql);
|
||||
return $zip ? $zip : '90210'; // Fallback if configuration is missing
|
||||
}
|
||||
|
||||
/**
|
||||
* MAPPING LOGIC: Old Module Codes -> New API Enums
|
||||
*/
|
||||
private function mapServiceCodeToApiClass($oldCode)
|
||||
{
|
||||
// Mappings based on your provided file classes/Model/Method.php
|
||||
// and the New API Spec Enums
|
||||
$map = [
|
||||
// DOMESTIC
|
||||
'USA_0' => 'USPS_GROUND_ADVANTAGE', // Was First-Class
|
||||
'USA_1' => 'PRIORITY_MAIL',
|
||||
'USA_3' => 'PRIORITY_MAIL_EXPRESS',
|
||||
'USA_6' => 'MEDIA_MAIL',
|
||||
'USA_7' => 'LIBRARY_MAIL',
|
||||
'USA_1058' => 'USPS_GROUND_ADVANTAGE',
|
||||
|
||||
// INTERNATIONAL
|
||||
'INT_1' => 'PRIORITY_MAIL_EXPRESS_INTERNATIONAL',
|
||||
'INT_2' => 'PRIORITY_MAIL_INTERNATIONAL',
|
||||
'INT_15' => 'FIRST-CLASS_PACKAGE_INTERNATIONAL_SERVICE',
|
||||
'INT_4' => 'GLOBAL_EXPRESS_GUARANTEED'
|
||||
];
|
||||
|
||||
return isset($map[$oldCode]) ? $map[$oldCode] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* MAPPING LOGIC: Flat Rate Boxes
|
||||
* Maps the internal name of the box to the API 'rateIndicator'
|
||||
*/
|
||||
private function mapBoxToRateIndicator($boxReference)
|
||||
{
|
||||
// You provided the PredefinedBox.php file earlier.
|
||||
// We map those names to New API 'rateIndicator' enum.
|
||||
|
||||
// Example Reference: "USPS Medium Flat Rate Box" or "MediumFlatRateBox"
|
||||
// We do a loose match
|
||||
|
||||
if (stripos($boxReference, 'Medium Flat Rate Box') !== false) return 'FB';
|
||||
if (stripos($boxReference, 'Large Flat Rate Box') !== false) return 'PL';
|
||||
if (stripos($boxReference, 'Small Flat Rate Box') !== false) return 'FS';
|
||||
if (stripos($boxReference, 'Flat Rate Envelope') !== false) return 'FE';
|
||||
if (stripos($boxReference, 'Padded Flat Rate Envelope') !== false) return 'FP';
|
||||
if (stripos($boxReference, 'Legal Flat Rate Envelope') !== false) return 'FA';
|
||||
|
||||
return false; // Not a flat rate box, uses standard rates
|
||||
}
|
||||
|
||||
// ... (rest of the class from Step 1: OAuth logic, etc)
|
||||
|
||||
/**
|
||||
* Manages OAuth2 Token life cycle
|
||||
*/
|
||||
private function getAccessToken()
|
||||
{
|
||||
$token = Configuration::get('USPS_BRIDGE_ACCESS_TOKEN');
|
||||
$expiry = Configuration::get('USPS_BRIDGE_TOKEN_EXPIRY');
|
||||
|
||||
// Add 60 seconds buffer
|
||||
if ($token && $expiry > (time() + 60)) {
|
||||
return $token;
|
||||
}
|
||||
|
||||
return $this->refreshAccessToken();
|
||||
}
|
||||
|
||||
private function refreshAccessToken()
|
||||
{
|
||||
$clientId = Configuration::get('USPS_BRIDGE_CLIENT_ID');
|
||||
$clientSecret = Configuration::get('USPS_BRIDGE_CLIENT_SECRET');
|
||||
$isLive = (bool)Configuration::get('USPS_BRIDGE_LIVE_MODE');
|
||||
|
||||
// URLs based on documentation (Verification pending next step)
|
||||
$url = $isLive
|
||||
? 'https://api.usps.com/oauth2/v3/token'
|
||||
: 'https://api-cat.usps.com/oauth2/v3/token';
|
||||
|
||||
$this->log("Requesting New Token from: " . $url);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
|
||||
'client_id' => $clientId,
|
||||
'client_secret' => $clientSecret,
|
||||
'grant_type' => 'client_credentials'
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$this->log("CURL Error: " . curl_error($ch));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$data = json_decode($response, true);
|
||||
|
||||
if ($httpCode == 200 && isset($data['access_token'])) {
|
||||
$expiresIn = isset($data['expires_in']) ? (int)$data['expires_in'] : 3599;
|
||||
|
||||
Configuration::updateValue('USPS_BRIDGE_ACCESS_TOKEN', $data['access_token']);
|
||||
Configuration::updateValue('USPS_BRIDGE_TOKEN_EXPIRY', time() + $expiresIn);
|
||||
|
||||
$this->log("Token refreshed successfully.");
|
||||
return $data['access_token'];
|
||||
}
|
||||
|
||||
$this->log("Token Request Failed: " . print_r($response, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current visitor IP is allowed to use New API
|
||||
*/
|
||||
public function isIpAllowed()
|
||||
{
|
||||
$allowedIps = Configuration::get('USPS_BRIDGE_DEBUG_IPS');
|
||||
|
||||
// If empty, everyone is allowed (Production ready)
|
||||
if (empty($allowedIps)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ips = array_map('trim', explode(',', $allowedIps));
|
||||
$currentIp = Tools::getRemoteAddr();
|
||||
|
||||
$allowed = in_array($currentIp, $ips);
|
||||
|
||||
if (!$allowed) {
|
||||
// Optional: Log that we skipped logic due to IP (might be too spammy)
|
||||
// $this->log("IP $currentIp not in debug list. Using Old API.");
|
||||
}
|
||||
|
||||
return $allowed;
|
||||
}
|
||||
|
||||
public function log($message)
|
||||
{
|
||||
if (Configuration::get('USPS_BRIDGE_LOGGING')) {
|
||||
PrestaShopLogger::addLog(
|
||||
'[USPS-BRIDGE] ' . (is_array($message) ? json_encode($message) : $message),
|
||||
1,
|
||||
null,
|
||||
'Usps_Api_Bridge',
|
||||
1,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user