Jake Vanderwerf
2026-07-05 fff721dd185f5b97f7ae7a6e64189e55887ff590
inc/rest/routes/IntegrationsSquareRoutes.php
@@ -2,82 +2,84 @@
namespace JVBase\rest\routes;
use JVBase\rest\RestRouteManager;
use JVBase\integrations\Square;
use JVBase\meta\Meta;
use JVBase\rest\Rest;
use Exception;
use JVBase\rest\Route;
use WP_REST_Request;
use WP_REST_Response;
use Exception;
if (!defined('ABSPATH')) {
   exit; // Exit if accessed directly
}
class IntegrationsSquareRoutes extends RestRouteManager
class IntegrationsSquareRoutes extends Rest
{
   public function registerRoutes():void
   {
      register_rest_route('jvb/v1/square', '/process-payment', [
         'methods' => 'POST',
         'callback' => [$this, 'handlePaymentProcessing'],
         'permission_callback' => '__return_true' // Adjust based on your auth
      ]);
      Route::for('square/process-payment')
         ->post([$this, 'handlePaymentProcessing'])
         ->auth('public')
         ->rateLimit(2)
         ->register();
      register_rest_route('jvb/v1/square', '/saved-cards', [
         'methods' => 'GET',
         'callback' => [$this, 'getSavedCards'],
         'permission_callback' => 'is_user_logged_in'
      ]);
      Route::for('square/saved-cards')
         ->post([$this, 'getSavedCards'])
         ->auth('user')
         ->rateLimit(5)
         ->register();
      Route::for('square/order-history')
         ->get([$this, 'getOrderHistory'])
         ->auth('user')
         ->rateLimit(5)
         ->register();
      register_rest_route('jvb/v1/square', '/order-history', [
         'methods' => 'GET',
         'callback' => [$this, 'getOrderHistory'],
         'permission_callback' => 'is_user_logged_in'
      ]);
      register_rest_route('jvb/v1/square', '/order-status/(?P<order_id>[a-zA-Z0-9_-]+)', [
         'methods' => 'GET',
         'callback' => [$this, 'getOrderStatus'],
         'permission_callback' => '__return_true' // Allow guests with order ID
      ]);
      Route::for(Route::pattern('square/order-status/{order_id}'))
         ->get([$this, 'getOrderStatus'])
         ->auth('public')
         ->rateLimit(20)
         ->register();
   }
   public function handlePaymentProcessing($request): array
   //TODO: Are we processing this through our server at all? Or is it in the javascript going straight to square?
   public function handlePaymentProcessing($request):WP_REST_Response
   {
      $data = $request->get_json_params();
      // Generate idempotency key from cart_id + timestamp
      // This ensures retries use SAME key
      $cart_id = $data['cart_id'] ?? '';
      if (!$cart_id) {
         return ['success' => false, 'message' => 'Missing cart ID'];
         return $this->validationError(['message'=>'Missing cart ID']);
      }
      // Check if we already processed this cart
      $existing_order = get_transient(BASE . 'cart_order_' . $cart_id);
      if ($existing_order) {
         // Return cached result - prevents double charge
         return $existing_order;
         return $this->success($existing_order);
      }
      // Generate idempotency key tied to this specific cart
      $idempotency_key = 'cart_' . $cart_id . '_' . time();
      // Store key to prevent reprocessing
      //TODO: Should we just use our Cache.php?
      set_transient(BASE . 'cart_idempotency_' . $cart_id, $idempotency_key, HOUR_IN_SECONDS);
      // Validate required fields
      $required = ['source_id', 'amount', 'items', 'customer'];
      foreach ($required as $field) {
         if (empty($data[$field])) {
            return [
               'success' => false,
               'message' => "Missing required field: {$field}"
            ];
            return $this->validationError(['message' => "Missing required field: {$field}"]);
         }
      }
      try {
         /** @var Square $square */
         $square = JVB()->connect('square');
         // Step 1: Get or create Square customer
         $customer_id = $square->getOrCreateSquareCustomer($data['customer']);
@@ -97,7 +99,7 @@
         // Step 3: Create Payment in Square
         $payment_response = $square->createSquarePayment(
            $data['source_id'],
            $data['idempotency_key'],
            $idempotency_key,
            $data['amount'],
            $order_id,
            $customer_id
@@ -133,107 +135,59 @@
         set_transient(BASE . 'cart_order_' . $cart_id, $result, HOUR_IN_SECONDS);
         return $result;
         return $this->success($result);
      } catch (Exception $e) {
         $this->logError('Payment processing failed', [
            'error' => $e->getMessage(),
            'idempotency_key' => $data['idempotency_key']
         ]);
         return [
            'success' => false,
            'message' => $e->getMessage()
         ];
         return $this->error($e->getMessage());
      }
   }
   public function getSavedCards($request): array
   public function getSavedCards(WP_REST_Request $request):WP_REST_Response
   {
      $data = $request->get_params();
      error_log('Getting Saved Cards: '.print_r($data, true));
      $user_id = get_current_user_id();
      if (!$user_id) {
         return ['success' => false, 'message' => 'Not logged in'];
         return $this->success(['cards' => []]);
      }
      $square = JVB()->connect('square');
      // Get Square customer ID for this user
      $square_customer_id = get_user_meta($user_id, BASE . '_square_customer_id', true);
      if (!$square_customer_id) {
         return ['success' => true, 'cards' => []];
         return $this->success(['cards' => []]);
      }
      // Fetch cards from Square (2025-compliant - separate endpoint)
      $cards_response = $square->getRequest('cards?customer_id=' . $square_customer_id);
      if (is_wp_error($cards_response)) {
         return ['success' => false, 'message' => 'Failed to fetch cards'];
      }
      return [
         'success' => true,
         'cards' => $cards_response['cards'] ?? []
      ];
      return $this->success(['cards' => Square::getInstance()->getUserCards( $square_customer_id)]);
   }
   public function getOrderHistory($request): array
   public function getOrderHistory(WP_REST_Request $request):WP_REST_Response
   {
      $user_id = get_current_user_id();
      if (!$user_id) {
         return ['success' => false, 'message' => 'Not logged in'];
         return $this->validationError(['message' => 'Not logged in']);
      }
      // Get orders from custom post type
      $orders = get_posts([
         'post_type' => BASE . '_sq_orders',
         'author' => $user_id,
         'posts_per_page' => 50,
         'orderby' => 'date',
         'order' => 'DESC'
      ]);
      $order_data = [];
      foreach ($orders as $order) {
         $meta = new \JVBase\meta\MetaManager($order->ID, 'post');
         $order_data[] = [
            'wp_order_id' => $order->ID,
            'square_order_id' => $meta->getValue('square_order_id'),
            'status' => $meta->getValue('status'),
            'amount' => $meta->getValue('amount'),
            'items' => $meta->getValue('items'),
            'created_at' => $meta->getValue('created_at'),
            'pickup_time' => $meta->getValue('pickup_time')
         ];
      }
      return [
         'success' => true,
         'orders' => $order_data
      ];
      return $this->success(['orders' => Square::getInstance()->getOrderHistory($user_id)]);
   }
   public function getOrderStatus($request): array
   public function getOrderStatus(WP_REST_Request $request):WP_REST_Response
   {
      $order_id = $request->get_param('order_id');
      // Find WP post by Square order ID
      $wp_order_id = get_option(BASE . 'square_order_map_' . $order_id);
      $wp_order_id = Square::getInstance()->getOrderPost($order_id);
      if (!$wp_order_id) {
         return ['success' => false, 'message' => 'Order not found'];
         return $this->error('Order not found');
      }
      $meta = new \JVBase\meta\MetaManager($wp_order_id, 'post');
      $meta = Meta::forPost($wp_order_id);
      $fields = $meta->getAll(['square_payment_status', 'fulfillment_status', 'pickup_time', 'items']);
      return [
         'success' => true,
         'order' => [
            'status' => $meta->getValue('status'),
            'fulfillment_status' => $meta->getValue('fulfillment_status'),
            'pickup_time' => $meta->getValue('pickup_time'),
            'items' => $meta->getValue('items')
         ]
      ];
      return $this->success(['order' => $fields]);
   }
}