Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/pod.git

Splits code into encapsulated classes

AuthorDave Jarvis <email>
Date2026-01-18 00:08:16 GMT-0800
Commit9c9677f534a3aa6210eed01320a2617bb30e8a9a
Parent8648b58
Address.php
+<?php
+class Address {
+ private $fields = [];
+ private $missing = [];
+ private const KEEP = false;
+ private const UPPER = true;
+
+ public function __construct( array $input ) {
+ $this->fields = $input;
+ }
+
+ private function escape( $value ) {
+ return htmlspecialchars( $value ?? '', ENT_QUOTES, 'UTF-8' );
+ }
+
+ public function process( array $required ) {
+ foreach( $required as $field ) {
+ if( empty( $this->fields[ $field ] ) ) {
+ $this->missing[] = $field;
+ }
+ }
+
+ $rules = [
+ 'name' => [ 100, self::KEEP ],
+ 'street1' => [ 100, self::UPPER ],
+ 'street2' => [ 100, self::UPPER ],
+ 'city' => [ 50, self::UPPER ],
+ 'state' => [ 50, self::UPPER ],
+ 'postcode' => [ 20, self::UPPER ],
+ 'country' => [ 2, self::UPPER ],
+ 'phone' => [ 20, self::KEEP ],
+ 'email' => [ 254, self::KEEP ],
+ 'gifted' => [ 100, self::KEEP ]
+ ];
+
+ $clean = [];
+ foreach( $rules as $field => $config ) {
+ $value = mb_substr( trim( $this->fields[ $field ] ?? '' ), 0,
+ $config[ 0 ], 'UTF-8' );
+
+ $transform = [
+ self::KEEP => $value,
+ self::UPPER => mb_strtoupper( $value, 'UTF-8' )
+ ];
+
+ $clean[ $field ] = $transform[ $config[ 1 ] ];
+ }
+ $this->fields = $clean;
+
+ return empty( $this->missing );
+ }
+
+ public function export( Session $session ) {
+ if( empty( $this->missing ) ) {
+ $session->write( 'user_address', $this->fields );
+ }
+ }
+
+ public function configure( array &$payload ) {
+ $payload[ 'shipping_address' ] = [
+ 'phone_number' => $this->fields[ 'phone' ] ?? '',
+ 'name' => $this->fields[ 'name' ] ?? '',
+ 'email' => $this->fields[ 'email' ] ?? '',
+ 'city' => $this->fields[ 'city' ] ?? '',
+ 'country_code' => $this->fields[ 'country' ] ?? '',
+ 'postcode' => $this->fields[ 'postcode' ] ?? '',
+ 'state_code' => $this->fields[ 'state' ] ?? '',
+ 'street1' => $this->fields[ 'street1' ] ?? '',
+ 'street2' => $this->fields[ 'street2' ] ?? ''
+ ];
+ return $this->fields[ 'country' ] ?? '';
+ }
+
+ public function render() {
+ if( !empty( $this->missing ) ) {
+ echo "<h2>Missing fields</h2><ul>";
+ foreach( $this->missing as $field ) {
+ echo "<li>" . $this->escape( $field ) . "</li>";
+ }
+ echo "</ul>";
+ }
+ }
+}
Configuration.php
+<?php
+class Configuration {
+ private $settings = [];
+
+ public function load() {
+ $path = $this->homeDirectory() . '/.keys/lulu.config';
+ if( file_exists( $path ) ) {
+ $this->settings = json_decode( file_get_contents( $path ), true );
+ }
+ }
+
+ private function homeDirectory() {
+ $result = '';
+
+ if( function_exists( 'posix_getpwnam' ) ) {
+ $homedir = posix_getpwnam( get_current_user() );
+
+ if( !empty( $homedir[ 'dir' ] ) ) {
+ $result = $homedir[ 'dir' ];
+ }
+ }
+
+ if( empty( $result ) ) {
+ if( !empty( $_SERVER[ 'HOME' ] ) ) {
+ $result = $_SERVER[ 'HOME' ];
+ }
+ elseif( !empty( getenv( 'HOME' ) ) ) {
+ $result = getenv( 'HOME' );
+ }
+ elseif( function_exists( 'posix_getpwuid' ) &&
+ function_exists( 'posix_getuid' ) ) {
+ $userInfo = posix_getpwuid( posix_getuid() );
+
+ if( !empty( $userInfo[ 'dir' ] ) ) {
+ $result = $userInfo[ 'dir' ];
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function configure( Publisher $publisher ) {
+ $publisher->initialize(
+ $this->settings[ 'CLIENT_KEY' ] ?? '',
+ $this->settings[ 'URL_BASE' ] ?? '',
+ [
+ 'package' => $this->settings[ 'ORDER_PACKAGE' ] ?? '',
+ 'pages' => $this->settings[ 'ORDER_PAGE_COUNT' ] ?? 0,
+ 'quantity' => $this->settings[ 'ORDER_QUANTITY' ] ?? 1,
+ 'shipping' => $this->settings[ 'ORDER_SHIPPING' ] ?? ''
+ ]
+ );
+ }
+}
Order.php
+<?php
+class Order {
+ private $summary = [];
+ private $errorMessage = '';
+ private $statusCode = 0;
+
+ private function escape( $value ) {
+ return htmlspecialchars( $value ?? '', ENT_QUOTES, 'UTF-8' );
+ }
+
+ public function process( array $apiResponse, int $statusCode ) {
+ $this->statusCode = $statusCode;
+ if( $statusCode === 201 ) {
+ $this->summary = $apiResponse;
+ } else {
+ $this->errorMessage = $apiResponse[ 'shipping_address' ][ 'detail' ]
+ [ 'errors' ][ 0 ][ 'message' ] ?? $apiResponse[ 'detail' ] ??
+ "An unexpected error occurred (HTTP $statusCode).";
+ }
+ }
+
+ public function render() {
+ if( $this->statusCode === 201 ) {
+ $this->renderSummary();
+ } else {
+ $this->renderError();
+ }
+ }
+
+ private function renderSummary() {
+ $address = $this->summary[ 'shipping_address' ];
+ $currency = $this->escape( $this->summary[ 'currency' ] );
+ ?>
+ <h2>Order summary</h2>
+ <section>
+ <p class="address">
+ <strong><?= $this->escape( $address[ 'name' ] ) ?></strong><br>
+ <?= $this->escape( $address[ 'street1' ] ) ?><br>
+ <?php if( !empty( $address[ 'street2' ] ) ) { ?>
+ <?= $this->escape( $address[ 'street2' ] ) ?><br>
+ <?php } ?>
+ <?= $this->escape( $address[ 'city' ] ) ?>
+ <?= $this->escape( $address[ 'state_code' ] ) ?>
+ <?= $this->escape( $address[ 'postcode' ] ) ?><br>
+ <?= $this->escape( $address[ 'country_code' ] ) ?>
+ </p>
+ <table>
+ <tr>
+ <td>Book price</td>
+ <td><?= $this->escape( $this->summary[ 'line_item_costs' ][ 0 ]
+ [ 'total_cost_excl_tax' ] ) ?> <?= $currency ?></td>
+ </tr>
+ <tr>
+ <td>Shipping</td>
+ <td><?= $this->escape( $this->summary[ 'shipping_cost' ]
+ [ 'total_cost_excl_tax' ] ) ?> <?= $currency ?></td>
+ </tr>
+ <tr>
+ <td>Taxes</td>
+ <td><?= $this->escape( $this->summary[ 'total_tax' ] ) ?>
+ <?= $currency ?></td>
+ </tr>
+ <tr>
+ <td><strong>Amount due</strong></td>
+ <td><strong><?= $this->escape(
+ $this->summary[ 'total_cost_incl_tax' ] ) ?>
+ <?= $currency ?></strong></td>
+ </tr>
+ </table>
+ </section>
+ <?php
+ }
+
+ private function renderError() {
+ ?>
+ <h2>Shipping calculation error</h2>
+ <p><?= $this->escape( $this->errorMessage ) ?></p>
+ <form action='.'>
+ <button type='submit' onclick='history.back(); return false;'>
+ Try again
+ </button>
+ </form>
+ <?php
+ }
+}
Publisher.php
+<?php
+class Publisher {
+ private $key;
+ private $base;
+ private $specs;
+
+ private const PATH_AUTH = "auth/realms/glasstree/protocol/openid-connect/token";
+ private const PATH_COST = "print-job-cost-calculations/";
+
+ private const CURRENCY_MAP = [
+ 'AU'=>'AUD','CA'=>'CAD','GB'=>'GBP','AT'=>'EUR','BE'=>'EUR','CY'=>'EUR',
+ 'EE'=>'EUR','FI'=>'EUR','FR'=>'EUR','DE'=>'EUR','GR'=>'EUR','IE'=>'EUR',
+ 'IT'=>'EUR','LV'=>'EUR','LT'=>'EUR','LU'=>'EUR','MT'=>'EUR','NL'=>'EUR',
+ 'PT'=>'EUR','SK'=>'EUR','SI'=>'EUR','ES'=>'EUR'
+ ];
+
+ public function initialize( string $key, string $base, array $specs ) {
+ $this->key = $key;
+ $this->base = $base;
+ $this->specs = $specs;
+ }
+
+ public function process( Address $address, Order $order ) {
+ $token = $this->authenticate();
+
+ if( !$token ) {
+ $order->process( [ 'detail' => 'Authentication failed.' ], 401 );
+ return;
+ }
+
+ $payload = [
+ 'line_items' => [ [
+ 'page_count' => $this->specs[ 'pages' ],
+ 'quantity' => $this->specs[ 'quantity' ],
+ 'pod_package_id' => $this->specs[ 'package' ]
+ ] ],
+ 'shipping_option' => $this->specs[ 'shipping' ]
+ ];
+
+ $country = $address->configure( $payload );
+ $payload[ 'currency' ] = self::CURRENCY_MAP[ $country ] ?? 'USD';
+
+ $context = stream_context_create( [ 'http' => [
+ 'method' => 'POST',
+ 'header' => "Authorization: Bearer $token\r\nContent-Type: " .
+ "application/json\r\n",
+ 'content' => json_encode( $payload ),
+ 'ignore_errors' => true
+ ] ] );
+
+ $raw = @file_get_contents( $this->base . self::PATH_COST, false, $context );
+ $body = json_decode( $raw, true ) ?? [];
+
+ $code = 0;
+
+ if( !empty( $http_response_header ) ) {
+ preg_match( "/\d{3}/", $http_response_header[ 0 ], $matches );
+ $code = intval( $matches[ 0 ] );
+ }
+
+ // Preserve the name and country code for the order summary.
+ if( $code === 201 && isset( $body[ 'shipping_address' ] ) ) {
+ $sentName = $payload[ 'shipping_address' ][ 'name' ];
+ $sentCountry = $payload[ 'shipping_address' ][ 'country_code' ];
+
+ if( empty( $body[ 'shipping_address' ][ 'name' ] ) ) {
+ $body[ 'shipping_address' ][ 'name' ] = $sentName;
+ }
+ if( empty( $body[ 'shipping_address' ][ 'country_code' ] ) ) {
+ $body[ 'shipping_address' ][ 'country_code' ] = $sentCountry;
+ }
+ }
+
+ $order->process( $body, $code );
+ }
+
+ private function authenticate() {
+ $context = stream_context_create( [ 'http' => [
+ 'method' => 'POST',
+ 'header' => "Authorization: Basic " . base64_encode( $this->key ) .
+ "\r\nContent-Type: application/x-www-form-urlencoded\r\n",
+ 'content' => http_build_query( [ 'grant_type' => 'client_credentials' ] )
+ ] ] );
+ $response = @file_get_contents(
+ $this->base . self::PATH_AUTH, false, $context
+ );
+
+ return $response
+ ? ( json_decode( $response, true )[ 'access_token' ] ?? null )
+ : null;
+ }
+}
Session.php
+<?php
+class Session {
+ public function __construct() {
+ if( session_status() === PHP_SESSION_NONE ) {
+ session_start();
+ }
+ }
+
+ public function write( $key, $value ) {
+ $_SESSION[ $key ] = $value;
+ }
+}
payment.php
<?php
-function getHomeDirectory() {
- $result = '';
-
- if( function_exists( 'posix_getpwnam' ) ) {
- $homedir = posix_getpwnam( get_current_user() );
-
- if( !empty( $homedir[ 'dir' ] ) ) {
- $result = $homedir[ 'dir' ];
- }
- }
-
- if( empty( $result ) ) {
- if( !empty( $_SERVER[ 'HOME' ] ) ) {
- $result = $_SERVER[ 'HOME' ];
- }
- elseif( !empty( getenv( 'HOME' ) ) ) {
- $result = getenv( 'HOME' );
- }
- elseif( function_exists( 'posix_getpwuid' ) &&
- function_exists( 'posix_getuid' ) ) {
- $userInfo = posix_getpwuid( posix_getuid() );
-
- if( !empty( $userInfo[ 'dir' ] ) ) {
- $result = $userInfo[ 'dir' ];
- }
- }
- }
- return $result;
-}
-
-$configPath = getHomeDirectory() . '/.keys/lulu.config';
-$config = json_decode( file_get_contents( $configPath ), true );
-
-define( 'CLIENT_KEY', $config[ 'CLIENT_KEY' ] );
-define( 'URL_BASE', $config[ 'URL_BASE' ] );
-define( 'ORDER_PAGE_COUNT', $config[ 'ORDER_PAGE_COUNT' ] );
-define( 'ORDER_PACKAGE', $config[ 'ORDER_PACKAGE' ] );
-define( 'ORDER_QUANTITY', $config[ 'ORDER_QUANTITY' ] );
-define( 'ORDER_SHIPPING', $config[ 'ORDER_SHIPPING' ] );
-
-const URL_AUTH = URL_BASE . "auth/realms/glasstree/protocol/openid-connect/token";
-const URL_COST = URL_BASE . "print-job-cost-calculations/";
-
-const CURRENCY_MAP = [
- 'AU'=>'AUD','CA'=>'CAD','GB'=>'GBP',
- 'AT'=>'EUR','BE'=>'EUR','CY'=>'EUR','EE'=>'EUR','FI'=>'EUR','FR'=>'EUR',
- 'DE'=>'EUR','GR'=>'EUR','IE'=>'EUR','IT'=>'EUR','LV'=>'EUR','LT'=>'EUR',
- 'LU'=>'EUR','MT'=>'EUR','NL'=>'EUR','PT'=>'EUR','SK'=>'EUR','SI'=>'EUR',
- 'ES'=>'EUR'
-];
-
-$required = [
- "city", "country", "postcode", "state", "street1", "phone", "name", "email"
-];
-$missing = [];
-
-foreach( $required as $f ) {
- if( empty( $_POST[ $f ] ) ) {
- $missing[] = $f;
- }
-}
-
-$code = 0;
-$raw_response = "";
-$error_message = "";
-
-if( empty( $missing ) ) {
- define( 'KEEP', false );
- define( 'UPPER', true );
-
- $fields = [
- 'name' => [ 100, KEEP ],
- 'street1' => [ 100, UPPER ],
- 'street2' => [ 100, UPPER ],
- 'city' => [ 50, UPPER ],
- 'state' => [ 50, UPPER ],
- 'postcode' => [ 20, UPPER ],
- 'phone' => [ 20, KEEP ],
- 'email' => [ 254, KEEP ],
- 'gifted' => [ 100, KEEP ]
- ];
-
- $clean = [];
-
- foreach( $fields as $f => $cfg ) {
- $val = mb_substr( trim( $_POST[ $f ] ?? '' ), 0, $cfg[ 0 ], 'UTF-8' );
- $transform = [
- KEEP => $val,
- UPPER => mb_strtoupper( $val, 'UTF-8' )
- ];
- $clean[ $f ] = $transform[ $cfg[ 1 ] ];
- }
-
- $selected_currency = CURRENCY_MAP[ $_POST[ "country" ] ] ?? 'USD';
-
- $auth_context = stream_context_create( [
- "http" => [
- "method" => "POST",
- "header" => "Authorization: Basic " .
- base64_encode( CLIENT_KEY ) .
- "\r\nContent-Type: application/x-www-form-urlencoded\r\n",
- "content" => http_build_query( [ "grant_type" => "client_credentials" ] ),
- ],
- ] );
-
- $auth_json = file_get_contents( URL_AUTH, false, $auth_context );
- $auth_response = json_decode( $auth_json, 1 );
- $token = $auth_response[ "access_token" ] ?? null;
-
- if( $token ) {
- $cost_data = [
- "line_items" => [ [
- "page_count" => ORDER_PAGE_COUNT,
- "quantity" => ORDER_QUANTITY,
- "pod_package_id" => ORDER_PACKAGE
- ] ],
- "shipping_address" => [
- "phone_number" => $clean[ "phone" ],
- "name" => $clean[ "name" ],
- "email" => $clean[ "email" ],
- "city" => $clean[ "city" ],
- "country_code" => $_POST[ "country" ],
- "postcode" => $clean[ "postcode" ],
- "state_code" => $clean[ "state" ],
- "street1" => $clean[ "street1" ],
- "street2" => $clean[ "street2" ],
- ],
- "shipping_option" => ORDER_SHIPPING,
- "currency" => $selected_currency,
- ];
-
- $cost_context = stream_context_create( [
- "http" => [
- "method" => "POST",
- "header" => "Authorization: Bearer $token\r\n" .
- "Content-Type: application/json\r\n",
- "content" => json_encode( $cost_data ),
- "ignore_errors" => true,
- ],
- ] );
-
- $raw_response = file_get_contents( URL_COST, false, $cost_context );
-
- if( !empty( $http_response_header ) ) {
- preg_match( "/\d{3}/", $http_response_header[ 0 ], $m );
- $code = intval( $m[ 0 ] );
- }
-
- $api_data = json_decode( $raw_response, 1 ) ?? [];
-
- if( $code !== 201 ) {
- $error_message =
- $api_data[ 'shipping_address' ][ 'detail' ][ 'errors' ][ 0 ][ 'message' ]
- ?? $api_data[ 'detail' ]
- ?? "An unexpected error occurred (HTTP $code).";
- }
- } else {
- $error_message = "Authentication failed.";
- }
-}
-
-function escape( $data ) {
- if( is_array( $data ) ) {
- return array_map( 'escape', $data );
- }
+require_once 'Configuration.php';
+require_once 'Address.php';
+require_once 'Order.php';
+require_once 'Publisher.php';
+require_once 'Session.php';
- return htmlspecialchars( $data ?? '', ENT_QUOTES, 'UTF-8' );
-}
+$config = new Configuration();
+$address = new Address( $_POST );
+$order = new Order();
+$publisher = new Publisher();
+$session = new Session();
-$response = escape( array_merge(
- [ 'form' => $_POST, 'missing' => $missing, 'api_error' => $error_message ],
- $api_data[ 'shipping_address' ] ?? [],
- $api_data ?? []
-) );
+$config->load();
+$config->configure( $publisher );
+$valid = $address->process( [
+ "city", "country", "postcode", "state", "street1", "phone", "name", "email" ]
+);
+$address->export( $session );
?>
<!DOCTYPE html>
<h1>autónoma &ndash; payment</h1>
<div class="payment-content">
- <?php if( !empty( $response[ 'missing' ] ) ) { ?>
- <h2>Missing fields</h2>
- <ul>
- <?php foreach( $response[ 'missing' ] as $m ) { ?>
- <li><?= $m ?></li>
- <?php } ?>
- </ul>
- <?php } else { ?>
- <?php if( $code == 201 ) { ?>
- <h2>Order summary</h2>
- <section>
- <p class="address">
- <strong><?= $_POST[ 'name' ] ?></strong><br>
- <?= $response[ 'street1' ] ?><br>
- <?php if( !empty( $response[ 'street2' ] ) ) { ?>
- <?= $response[ 'street2' ] ?><br>
- <?php } ?>
- <?= $response[ 'city' ] ?>
- <?= $response[ 'state' ] ?>
- <?= $response[ 'postcode' ] ?><br>
- <?= $response[ 'country' ] ?><br>
- </p>
- <table>
- <tr>
- <td>Book price</td>
- <td><?= $response[ 'line_item_costs' ][0][ 'total_cost_excl_tax' ] ?> <?= $response[ 'currency' ] ?></td>
- </tr>
- <tr>
- <td>Shipping</td>
- <td><?= $response[ 'shipping_cost' ][ 'total_cost_excl_tax' ] ?> <?= $response[ 'currency' ] ?></td>
- </tr>
- <tr>
- <td>Taxes</td>
- <td><?= $response[ 'total_tax' ] ?> <?= $response[ 'currency' ] ?></td>
- </tr>
- <tr>
- <td><strong>Amount due</strong></td>
- <td><strong><?= $response[ 'total_cost_incl_tax' ] ?> <?= $response[ 'currency' ] ?></strong></td>
- </tr>
- </table>
- </section>
- <?php } else { ?>
- <h2>Shipping calculation error</h2>
- <p><?= $response[ 'api_error' ] ?></p>
- <form action=".">
- <button type="submit" onclick="history.back(); return false;">
- Try again
- </button>
- </form>
- <?php } ?>
- <?php } ?>
+ <?php
+ if( $valid ) {
+ $publisher->process( $address, $order );
+ $order->render();
+ } else {
+ $address->render();
+ }
+ ?>
</div>
</body>
</html>
-
Delta351 lines added, 224 lines removed, 127-line increase