From fff721dd185f5b97f7ae7a6e64189e55887ff590 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Sun, 05 Jul 2026 18:36:57 +0000
Subject: [PATCH] =Cleaning up the Square integration (still a bit more to do yet). Also majorly overhauled /rest/ files to ignore a rest request 'user' paramater, and rely on get_current_user_id() instead.

---
 inc/integrations/Square.php |  298 ++++++++++++++++++++++++++++++++--------------------------
 1 files changed, 164 insertions(+), 134 deletions(-)

diff --git a/inc/integrations/Square.php b/inc/integrations/Square.php
index 9094778..5e1a831 100644
--- a/inc/integrations/Square.php
+++ b/inc/integrations/Square.php
@@ -7,10 +7,12 @@
 use JVBase\registrar\Fields;
 use JVBase\registrar\Posts;
 use JVBase\registrar\Registrar;
+use JVBase\ui\CRUDSkeleton;
 use WP_Error;
 use JVBase\ui\Checkout;
 use JVBase\managers\queue\TypeConfig;
 use JVBase\managers\queue\executors\IntegrationExecutor;
+use WP_Query;
 
 if (!defined('ABSPATH')) {
 	exit;
@@ -83,7 +85,18 @@
 	protected string $locationId = '';
 	protected array $locations = [];
 
-	public function __construct(?int $userID = null)
+	protected static array $instances = [];
+
+	public static function getInstance(?int $userID = null):self
+	{
+		$key = is_null($userID) ? 'base' : $userID;
+		if (!array_key_exists($key, self::$instances)) {
+			self::$instances[$key] = new static($userID);
+		}
+		return self::$instances[$key];
+	}
+
+	protected function __construct(?int $userID = null)
 	{
 		// Display properties
 		$this->title = 'Square';
@@ -196,7 +209,10 @@
 				'sync_to_square' => 'Sync Site to Square',
 			]
 		);
+
 		add_action('init', [$this, 'registerSquarePostTypes'], 5);
+		add_action('init', [$this, 'addDashboardPages'], 10);
+		add_action(BASE.'dashboard_page_orders', [$this, 'renderDashPage']);
 	}
 
 	/**
@@ -256,7 +272,7 @@
 						'type' => 'number',
 						'label' => 'Total Amount (cents)',
 					],
-					'post_status' => [
+					'square_payment_status' => [
 						'type' => 'select',
 						'label' => 'Order Status',
 						'options' => [
@@ -294,6 +310,7 @@
 					'customer_phone' => [
 						'type' => 'phone',
 						'label' => 'Customer Phone',
+						'section'=> 'your-account'
 					],
 					'special_instructions' => [
 						'type' => 'textarea',
@@ -1016,19 +1033,6 @@
 
 			if (!is_wp_error($response)) {
 				$this->processBatchSyncResponse($response, $map, $success, $errors);
-				$square_id = $response['objects'][0]['id'];
-				update_post_meta($postID, BASE . '_square_catalog_id', $square_id);
-				update_post_meta($postID, BASE . '_square_sync_status', 'synced');
-				update_post_meta($postID, BASE . '_square_last_sync', current_time('mysql'));
-
-				// Save variation IDs
-				if (!empty($response['objects'][0]['item_data']['variations'])) {
-					foreach ($response['objects'][0]['item_data']['variations'] as $index => $variation) {
-						update_post_meta($postID, BASE . '_square_variation_' . $index . '_id', $variation['id']);
-					}
-				}
-
-				$success[] = $postID;
 			} else {
 				// Handle batch request failure
 				$error_message = 'Batch sync failed';
@@ -1145,6 +1149,7 @@
 	 * @param string|null $square_image_id Previously uploaded Square image ID
 	 * @return array|WP_Error Catalog object or error
 	 */
+	//TODO: Get to work with Registrar settings
 	protected function buildCatalogObject(int $postID, ?string $square_image_id = null): array|WP_Error
 	{
 		$post = get_post($postID);
@@ -1474,6 +1479,7 @@
 	/**
 	 * Get valid fields for Square product type
 	 */
+	//TODO: This feels redundant now, with how we've defined fields in getAdditionalFields
 	private function getValidFieldsForProductType(string $product_type): array
 	{
 		$fields = ['name', 'description_html', 'sku', 'price', 'image_ids', 'category_id'];
@@ -1517,6 +1523,7 @@
 	/**
 	 * Handle customer authentication during checkout
 	 */
+	//TODO: Is this necessary?
 	public function handleCustomerAuth($data):WP_Error|array
 	{
 		$email = sanitize_email($data['email'] ?? '');
@@ -1567,7 +1574,8 @@
 					'message' => 'Email found. Would you like to create an account to save your order history?'
 				];
 			}
-		} else {
+		}
+
 			// Check Square for customer
 			$response = $this->postRequest('customers/search', [
 				'filter' => [
@@ -1590,7 +1598,6 @@
 					'message' => 'New customer'
 				];
 			}
-		}
 	}
 
 	private function createCustomerAccount(string $email):WP_Error|array
@@ -1831,9 +1838,6 @@
 	/**
 	 * Handle order status webhook
 	 */
-	/**
-	 * Handle order status webhook - NOW UPDATES POST TYPE
-	 */
 	private function handleOrderWebhook(array $data): bool
 	{
 		$order_id = $data['object']['order']['id'] ?? '';
@@ -1845,7 +1849,7 @@
 		}
 
 		// Find the WP post for this order
-		$wp_order_id = get_option(BASE . 'square_order_map_' . $order_id);
+		$wp_order_id = $this->getOrderPost($order_id);
 
 		if ($wp_order_id) {
 			// Update the post meta
@@ -1872,10 +1876,6 @@
 				do_action(BASE . 'square_order_ready', $wp_order_id, $order_id);
 			}
 		}
-
-		// Also update transient cache for quick status checks
-		set_transient(BASE . 'square_order_' . $order_id, $state, HOUR_IN_SECONDS);
-
 		// Trigger action for other integrations
 		do_action(BASE . 'square_order_updated', $order_id, $state, $data);
 
@@ -1941,6 +1941,7 @@
 	 */
 	public function enqueueScripts(): void
 	{
+		jvbInlineStyles('forms');
 		$this->loadCredentials();
 		$sdk_url = $this->environment === 'production'
 			? 'https://web.squarecdn.com/v1/square.js'
@@ -1958,8 +1959,8 @@
 		wp_register_script(
 			'jvb-checkout',
 			JVB_URL . 'assets/js/min/checkout.min.js',
-			['jvb-utility', 'jvb-queue', 'jvb-a11y', 'jvb-cache', 'jvb-tabs', 'jvb-popup'],
-			'1.1.31',
+			['jvb-utility', 'jvb-queue', 'jvb-a11y', 'jvb-cache', 'jvb-tabs', 'jvb-popup', 'jvb-login'],
+			'1.1.32',
 			['strategy' => 'defer', 'in_footer' => true]
 		);
 
@@ -1968,7 +1969,7 @@
 			'jvb-square-checkout',
 			JVB_URL . 'assets/js/min/square.min.js',
 			['jvb-checkout', 'square-payments-sdk'],
-			'1.1.31',
+			'1.1.32',
 			['strategy' => 'defer', 'in_footer' => true]
 		);
 
@@ -1979,9 +1980,7 @@
 			'application_id' => $this->credentials['client_id'] ?? '',
 			'location_id'    => $this->locationId,
 			'environment'    => $this->environment,
-			'api_url'        => rest_url('jvb/v1/square/'),
-			'nonce'          => wp_create_nonce('wp_rest'),
-			'currency'       => get_option(BASE . 'currency', 'CAD'),
+			'currency'       => $this->getCurrency(),
 			'is_logged_in'   => is_user_logged_in(),
 			'user_email'     => is_user_logged_in() ? wp_get_current_user()->user_email : '',
 		]);
@@ -1994,7 +1993,7 @@
 	/**
 	 * Get or create Square customer
 	 */
-	private function getOrCreateSquareCustomer(array $customer_info): ?string
+	public function getOrCreateSquareCustomer(array $customer_info): ?string
 	{
 		if (empty($customer_info['email'])) {
 			return null;
@@ -2027,92 +2026,6 @@
 		return null;
 	}
 
-	/**
-	 * Save order reference for status tracking
-	 */
-	public function saveOrderReference($data): array
-	{
-		$order_id = sanitize_text_field($data['order_id'] ?? '');
-		$payment_id = sanitize_text_field($data['payment_id'] ?? '');
-
-		if (!$order_id) {
-			return ['success' => false, 'message' => 'Invalid order data'];
-		}
-
-		// Save to user if logged in
-		if (is_user_logged_in()) {
-			$user_id = get_current_user_id();
-			$orders = get_user_meta($user_id, BASE . '_square_orders', true) ?: [];
-			$orders[] = [
-				'order_id' => $order_id,
-				'payment_id' => $payment_id,
-				'date' => current_time('mysql'),
-				'customer' => $data['customer'] ?? []
-			];
-
-			// Keep last 50 orders
-			if (count($orders) > 50) {
-				$orders = array_slice($orders, -50);
-			}
-
-			update_user_meta($user_id, BASE . '_square_orders', $orders);
-		}
-
-		return [
-			'success' => true,
-			'order_id' => $order_id,
-			'message' => 'Order saved'
-		];
-	}
-	/**
-	 * Save order to user meta
-	 */
-	private function saveOrderToUser(int $user_id, string $order_id): void
-	{
-		$orders = get_user_meta($user_id, BASE . '_square_orders', true) ?: [];
-		$orders[] = [
-			'order_id' => $order_id,
-			'date' => current_time('mysql')
-		];
-
-		// Keep only last 50 orders
-		if (count($orders) > 50) {
-			$orders = array_slice($orders, -50);
-		}
-
-		update_user_meta($user_id, BASE . '_square_orders', $orders);
-	}
-
-	/**
-	 * Get order status (for customer feedback)
-	 */
-	public function getOrderStatus($data): WP_Error|array
-	{
-		$order_id = sanitize_text_field($data['order_id'] ?? '');
-
-		if (!$order_id) {
-			return new WP_Error('error', 'Order ID required');
-		}
-
-		// Fetch from Square
-		$response = $this->getRequest('v2/orders/' . $order_id);
-
-		if (is_wp_error($response)) {
-			return new WP_Error('error', 'Could not fetch order status');
-		}
-
-		$order = $response['order'] ?? [];
-		$status_data = [
-			'state' => $order['state'] ?? 'UNKNOWN',
-			'fulfillment_eta' => $order['fulfillments'][0]['pickup_details']['pickup_at'] ?? null
-		];
-
-		return [
-			'success' => true,
-			'status' => $status_data['state'],
-			'eta' => $status_data['fulfillment_eta']
-		];
-	}
 
 	/**
 	 * Process delete from Square
@@ -2427,8 +2340,12 @@
 			return false;
 		}
 
-		// Update cached payment status
-		set_transient(BASE . 'square_payment_' . $payment_id, $status, HOUR_IN_SECONDS);
+		if ($order_id) {
+			$order = $this->getOrderPost($order_id);
+			if ($order) {
+				Meta::forPost($order)->set('square_payment_status', $status);
+			}
+		}
 
 		// Trigger action for other integrations
 		do_action(BASE . 'square_payment_updated', $payment_id, $status, $order_id, $data);
@@ -2729,6 +2646,9 @@
 		];
 	}
 	public function getAdditionalFields(?string $content_type = null):array {
+		if ($content_type === 'customer') {
+			return $this->getCustomerFields();
+		}
 		if ($content_type && array_key_exists($content_type, $this->contentTypes)){
 			$array = $this->contentTypes[$content_type];
 			return array_combine(
@@ -2745,6 +2665,58 @@
             $array
         );
 	}
+
+	protected function getCustomerFields():array
+	{
+		return [
+			'customer_id'	=> [
+				'type'	=> 'text',
+				'label'	=> 'Square Customer ID',
+				'hidden'=> true,
+				'section'=> 'your-account'
+			],
+			'address_line_1'	=> [
+				'type'	=> 'text',
+				'label'	=> 'Address Line 1',
+				'hint'	=> 'ex: 6551 111 St NW',
+				'required'	=> true,
+				'section'	=> 'address'
+			],
+			'address_line_2'	=> [
+				'type'	=> 'text',
+				'label'	=> 'Address Line 2',
+				'hint'	=> 'ex: Unit 2',
+				'section'	=> 'address'
+			],
+			'city'	=> [
+				'type'	=> 'text',
+				'label'=> 'City',
+				'section'	=> 'address',
+				'required'	=> true,
+			],
+			'state' => [
+				'type'	=> 'text',
+				'label' => 'Province',
+				'hint' => 'The two-character code, example: AB',
+				'default'=> 'AB',
+				'section'	=> 'address',
+				'required'	=> true,
+			],
+			'zip_code' => [
+				'type'	=> 'text',
+				'label'	=> 'Postal Code',
+				'section'=> 'address'
+			],
+			'countryCode'	=> [
+				'type'	=> 'text',
+				'label'	=> 'Country Code',
+				'hint'	=> 'The tw-character country code, example: CA',
+				'default'	=> 'CA',
+				'section'	=> 'address',
+				'required'	=> true,
+			]
+		];
+	}
 	protected function setBaseFields():array
 	{
 		return [
@@ -3327,7 +3299,7 @@
 		}
 	}
 
-	private function createSquareOrder(array $items, ?string $customer_id, array $data): array|WP_Error
+	public function createSquareOrder(array $items, ?string $customer_id, array $data): array|WP_Error
 	{
 		// Build line items for Square
 		$line_items = [];
@@ -3384,7 +3356,7 @@
 		return $this->postRequest('orders', $order_data);
 	}
 
-	private function createSquarePayment(
+	public function createSquarePayment(
 		string $source_id,
 		string $idempotency_key,
 		int $amount_cents,
@@ -3415,7 +3387,7 @@
 		return $this->postRequest('payments', $payment_data);
 	}
 
-	private function saveOrderToWordPress(array $order_data): int
+	public function saveOrderToWordPress(array $order_data): int
 	{
 		// Extract customer info
 		$customer_email = $order_data['customer']['email'] ?? '';
@@ -3436,7 +3408,7 @@
 
 		// Create order post
 		$order_post_id = wp_insert_post([
-			'post_type' => BASE . '_square_orders',
+			'post_type' => BASE.$this->orderPostType,
 			'post_title' => 'Order #' . $order_data['square_order_id'],
 			'post_status' => 'publish',
 			'post_author' => $user_id // Associate with user if logged in
@@ -3470,9 +3442,6 @@
 			'updated_at' => current_time('mysql')
 		]);
 
-		// Index by Square order ID for quick webhook lookups
-		update_option(BASE . 'square_order_map_' . $order_data['square_order_id'], $order_post_id);
-
 		return $order_post_id;
 	}
 
@@ -3527,17 +3496,10 @@
 
 	public function checkOrderStatus(string $order_id): ?string
 	{
-		// Check transient cache first
-		$cached = get_transient(BASE . 'square_order_' . $order_id);
-		if ($cached) {
-			return $cached;
-		}
-
 		// Fetch from Square
 		$response = $this->getRequest('orders/' . $order_id);
 		if (!is_wp_error($response)) {
 			$state = $response['order']['state'] ?? null;
-			set_transient(BASE . 'square_order_' . $order_id, $state, HOUR_IN_SECONDS);
 			return $state;
 		}
 
@@ -3613,4 +3575,72 @@
 
 		return $result;
 	}
+
+	public function addDashboardPages():void
+	{
+		$page = JVB()->dashboard()->addPage('Your Orders', 'orders', 'receipt');
+		$page->setScripts(['jvb-crud']);
+	}
+	public function renderDashPage():void
+	{
+		$instance = $this->determineInstance();
+
+		$crud = new CRUDSkeleton();
+		$crud->icon('receipt');
+		$crud->title('Your Orders','Here you can see your past orders, and reorder from there if you\'d like');
+		$crud->content($this->orderPostType, 'Order', 'Orders');
+		$crud->addSearch();
+		$crud->addCapabilities(['view']);
+		$crud->setEmptyState(sprintf(
+			'<div class="empty-state">
+				<h3>%sNothing here%s</h3>
+				<p>It doesn\'t look like you have any orders yet.</p>
+				<p>Head on over to <a href="%s">our menu</a> and make your first order!</p>
+			</div>',
+			jvbDashIcon($crud->getIcon()),
+			jvbDashIcon($crud->getIcon()),
+			get_post_type_archive_link(BASE.'menu_item')
+		));
+
+
+		$crud->render();
+	}
+	protected function determineInstance():self
+	{
+		if (current_user_can('manage_options')) {
+			return self::$instances['base'];
+		}
+		return self::getInstance(get_current_user_id());
+	}
+
+	public function getOrderPost(string $order_id):int|false
+	{
+		$posts = new WP_Query([
+			'post_type'			=> BASE.$this->orderPostType,
+			'posts_per_page'	=> 1,
+			'meta_key'			=> BASE.'square_order_id',
+			'meta_value'		=> $order_id,
+			'fields'			=> 'ids',
+		]);
+		wp_reset_postdata();
+		return $posts->have_posts() ? $posts[0] : false;
+	}
+	public function getOrderHistory(int $user_id):array
+	{
+		$posts = new WP_Query([
+			'post_type'			=> BASE.$this->orderPostType,
+			'posts_per_page'	=> 25,
+			'author'			=> $user_id,
+			'orderby'			=> 'date',
+			'order'				=> 'desc',
+			'fields'			=> 'ids',
+		]);
+		wp_reset_postdata();
+		return array_map(function ($post) {
+			$fields = Meta::forPost($post);
+			$fields['wp_order_id'] = $post;
+			return $fields;
+		}, $posts->posts);
+
+	}
 }

--
Gitblit v1.10.0