Initial commit

This commit is contained in:
2024-06-16 03:43:12 -05:00
commit ba3df432e5
3 changed files with 968 additions and 0 deletions

View File

@@ -0,0 +1,282 @@
<?php
/*
Plugin Name: Pirateship Live Rates
Plugin URI:
Description: Query Pirateship for the rates of any given package
Version: 0.1
Author: Cavemanon
Author URI:
*/
/*
Copyright (C) 2024 Cavemanon
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation version 3 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
if ( ! defined( 'WPINC' ) ){
die('security by preventing any direct access to your plugin file');
}
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
function pirateship_shipping_method() {
if (!class_exists('WC_Pirateship_Shipping_Method')) {
class WC_Pirateship_Shipping_Method extends WC_Shipping_Method {
public function __construct()
{
$this->id = 'pirateship-live-rates';
$this->method_title = __('Pirateship Shipping', 'pirateship-live-rates');
$this->method_description = __('Live rates from the Pirateship API', 'pirateship-live-rates');
// Contreis availability
$this->init();
$this->enabled = isset($this->settings['enabled']) ? $this->settings['enabled'] : 'yes';
$this->originCity = $this->settings['originCity'];
$this->originRegionCode = $this->settings['originRegionCode'];
$this->originZip = $this->settings['originZip'];
$this->fallbackPrice = $this->settings['fallbackPrice'];
}
/**
Load the settings API
*/
function init()
{
$this->init_form_fields();
$this->init_settings();
add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
}
function init_form_fields()
{
$this->form_fields = array(
'enabled' => array(
'title' => __('Enable', 'pirateship-live-rates'),
'type' => 'checkbox',
'default' => 'yes'
),
'originCity' => array(
'title' => __('Senders City', 'pirateship-live-rates'),
'type' => 'text',
'default' => 'Denver'
),
'originRegionCode' => array(
'title' => __('Origin Region Code (State Abreviation)', 'pirateship-live-rates'),
'type' => 'text',
'default' => 'SC'
),
'originZip' => array(
'title' => __('Origin ZIP Code', 'pirateship-live-rates'),
'type' => 'text',
'default' => '72014'
),
'fallbackPrice' => array(
'title' => __('API Failure Fallback Price', 'pirateship-live-rates'),
'type' => 'int',
'default' => '15'
)
);
}
function calc($package): array {
#Only calculate shipping when checking out
#if( is_checkout() == False) {
# return [];
#}
//No reason to ask if we don't even have a zip code.
if ($package["destination"]["postcode"] === '') {
#print("none found");
return [];
}
//Before we do any work, see if we got this request before.
$cacheKey = hash('sha256', serialize($package));
$cachedRate = get_transient($cacheKey);
//If we've had it before, just get it from cache.
if ($cachedRate != false) {
#print("cached");
return $cachedRate;
}
$weight = 0;
$cost = 0;
foreach ($package['contents'] as $item_id => $values) {
$_product = $values['data'];
$weight = $weight + $_product->get_weight() * $values['quantity'];
//TODO: Fix this so that shipping is per-item type rather than bulking everything together.
$width = $_product->get_width();
$length = $_product->get_length();
$height = $_product->get_height();
}
//API only accepts ounces
$weight = wc_get_weight($weight, 'lb') * 16;
//PHP reeks like shit from hell itself
//but somehow, wordpress extensions made it worse by making you need to
//VENDOR FUCKING DEPENDENCIES to get other people's code in
//
//So that's why this fucking garbage implementation of GraphQL is here
//GOD, what fucking happened to software engineering!?
//GraphQL API url for pirateship
$apiURL = 'https://ship.pirateship.com/api/graphql?opname=GetRates' ;
//GraphQL statement codified in JSON for the incoming query
$JSONQuery = json_encode(
array(
"operationName" => "GetRates",
"query" => 'query GetRates($originZip: String!, $originCity: String, $originRegionCode: String, $destinationZip: String, $isResidential: Boolean, $destinationCountryCode: String, $weight: Float, $dimensionX: Float, $dimensionY: Float, $dimensionZ: Float, $mailClassKeys: [String!]!, $packageTypeKeys: [String!]!, $pricingTypes: [String!], $showUpsRatesWhen2x7Selected: Boolean) { rates( originZip: $originZip originCity: $originCity originRegionCode: $originRegionCode destinationZip: $destinationZip isResidential: $isResidential destinationCountryCode: $destinationCountryCode weight: $weight dimensionX: $dimensionX dimensionY: $dimensionY dimensionZ: $dimensionZ mailClassKeys: $mailClassKeys packageTypeKeys: $packageTypeKeys pricingTypes: $pricingTypes showUpsRatesWhen2x7Selected: $showUpsRatesWhen2x7Selected ) { title deliveryDescription trackingDescription serviceDescription pricingDescription cubicTier mailClassKey mailClass { accuracy international __typename } packageTypeKey zone surcharges { title price __typename } carrier { carrierKey title __typename } totalPrice priceBaseTypeKey basePrice crossedTotalPrice pricingType pricingSubType ratePeriodId learnMoreUrl cheapest fastest __typename }}',
"variables" => array(
"originZip" => (string)$this->originZip,
"originCity" => (string)$this->originCity,
"originRegionCode" => (string)$this->originRegionCode,
"isResidential" => (bool)"true",
"destinationZip" => (string)$package["destination"]["postcode"],
"destinationCountryCode" => (string)$package["destination"]["country"],
"mailClassKeys" => array("PriorityExpress","First","ParcelSelect","Priority","PriorityMailExpressInternational","FirstClassPackageInternationalService","PriorityMailInternational","Priority_Cubic","Priority_Regional_Rate_Box_A","Priority_Regional_Rate_Box_B","Priority_Flat_Rate_Envelope","Priority_Legal_Flat_Rate_Envelope","Priority_Padded_Flat_Rate_Envelope","Priority_Small_Flat_Rate_Box","Priority_Medium_Flat_Rate_Box","Priority_Large_Flat_Rate_Box","Priority_APO_FPO_DPO_Large_Flat_Rate_Box","PriorityExpress_Flat_Rate_Envelope","PriorityExpress_Legal_Flat_Rate_Envelope","PriorityExpress_Padded_Flat_Rate_Envelope","PriorityMailInternational_Flat_Rate_Envelope","PriorityMailInternational_Small_Flat_Rate_Box","PriorityMailInternational_Medium_Flat_Rate_Box","PriorityMailInternational_Large_Flat_Rate_Box","PriorityMailExpressInternational_Flat_Rate_Envelope","ParcelSelect_Cubic","GroundAdvantage","GroundAdvantage_Cubic","01","02","03","07","08","11","12","13","14","59","65","03_cubic","93"),
"packageTypeKeys" => array("Parcel"),
"weight" => (int)$weight,
"dimensionX" => (int)$length,
"dimensionY" => (int)$width,
"dimensionZ" => (int)$height,
"showUpsRatesWhen2x7Selected" => (bool)"true"
),
)
);
$success = function() use (&$resp): bool {
return is_array($resp) && (int)$resp['response']['code'] === 200;
};
$tries = 1;
while ($tries--) {
$resp = wp_remote_post($apiURL, [
'timeout' => 15,
'sslverify' => true,
//We're expecting JSON, tell the server this or it will shit itself.
'headers' => array(
'content-type' => 'application/json'
),
'body' => $JSONQuery,
]);
if ($success()) {
break;
}
sleep(1);
}
if (!is_array($resp)) {
throw new Error("API request transport error");
}
if (!$success()) {
throw new Error("API request HTTP error");
}
$resp = (string)$resp["body"];
if ($resp === '') {
throw new Error("API response is empty");
}
#print($JSONQuery);
#print($resp);
#print(implode(',', array_keys($resp["body"])));
$prices = json_decode($resp, True);
if (array_key_exists("errors", $prices)){
$option = array(
'id' => "Fallback Price",
'label' => "Fallback Price - Failed to get shipping rate. Refresh page and report problem to Site Administrator if problem persists.",
'cost' => (int)$this->fallbackPrice,
);
$options = [];
array_push($options, $option);
return $options;
}
$options = [];
foreach($prices["data"] as $rateContainer) {
foreach($rateContainer as $rate) {
$title = $rate["title"];
$duration = $rate["deliveryDescription"];
$totalPrice = $rate["totalPrice"];
$option = array(
'id' => "$title",
'label' => "$title - $duration",
'cost' => $totalPrice
);
array_push($options, $option);
}
}
set_transient($cacheKey, $options, DAY_IN_SECONDS);
return $options;
}
public function calculate_shipping($package = []): void {
try {
$stampageRates = $this->calc($package);
}
catch (\Exception $e) {
$msg = $e->getMessage();
$debug->recordError($msg);
self::logger()->error($msg);
return;
}
foreach($stampageRates as $stampage) {
$this->add_rate($stampage);
}
}
}
}
}
add_action('woocommerce_shipping_init', 'pirateship_shipping_method');
function add_pirateship_shipping_method($methods)
{
$methods['pirateship_shipping_method'] = 'WC_Pirateship_Shipping_Method';
return $methods;
}
add_filter('woocommerce_shipping_methods', 'add_pirateship_shipping_method');
add_action('woocommerce_checkout_update_order_review', 'action_woocommerce_checkout_update_order_review', 10, 1);
function action_woocommerce_checkout_update_order_review( $posted_data )
{
global $woocommerce;
$packages = $woocommerce->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
}
}
}
?>