From c68aefb847b09daa0697de7684d3451e2e68ce1e Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Thu, 09 Jul 2026 23:10:23 +0000
Subject: [PATCH] =Refactor of Integrations.php to be a bit more useful with a suite of methods for create, update, delete back and forths for integrations where sharing is enabled. Also considering a single instance with a connect method to connect as the site (no user) vs connecting as an individual user - rather than recreating instances for every user.
---
inc/integrations/Square.php | 267 ++++++++++++-----------------------------------------
1 files changed, 60 insertions(+), 207 deletions(-)
diff --git a/inc/integrations/Square.php b/inc/integrations/Square.php
index 5e1a831..c0191a3 100644
--- a/inc/integrations/Square.php
+++ b/inc/integrations/Square.php
@@ -28,6 +28,7 @@
*/
class Square extends Integrations
{
+ protected static string $syncCustomer = 'square_sync_customer';
protected array $allowedContent = [
'REGULAR',
'FOOD_AND_BEV',
@@ -90,14 +91,16 @@
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);
+ self::$instances[$key] = new self($userID);
}
return self::$instances[$key];
}
protected function __construct(?int $userID = null)
{
+
// Display properties
$this->title = 'Square';
$this->icon = 'square-logo';
@@ -786,8 +789,8 @@
'name' => $variation['name'],
'pricing_type' => 'FIXED_PRICING',
'price_money' => [
- 'amount' => intval($variation['_square_price'] * 100), // Convert to cents
- 'currency' => 'USD'
+ 'amount' => $this->formatPrice($variation['_square_price']??0),
+ 'currency' => $this->getCurrency()
]
]
];
@@ -801,8 +804,8 @@
'name' => 'Regular',
'pricing_type' => 'FIXED_PRICING',
'price_money' => [
- 'amount' => intval(($itemData['_square_price'] ?? 0) * 100),
- 'currency' => 'USD'
+ 'amount' => $this->formatPrice($itemData['_square_price'] ?? 0),
+ 'currency' => $this->getCurrency()
]
]
];
@@ -858,7 +861,6 @@
return;
}
- add_action('wp_login', [$this, 'trackUserLogin'], 10, 2);
add_action('wp_enqueue_scripts', [$this, 'enqueueScripts']);
add_filter('jvbAdditionalActions', [Checkout::class, 'render']);
@@ -880,8 +882,6 @@
add_filter('jvb_checkout_browse_text', function () {
return 'browse our menu';
});
- // Register queue executor types
- add_action('init', [$this, 'registerQueueTypes'], 10);
}
/**
@@ -910,29 +910,9 @@
]);
}
- public function registerQueueTypes(): void
+ protected function registerAdditionalQueueTypes(IntegrationExecutor $executor): void
{
$queue = JVB()->queue();
- $executor = new IntegrationExecutor();
-
- $queue->registry()->register(self::$syncTo, new TypeConfig(
- executor: $executor,
- chunkKey: 'items',
- chunkSize: 50,
- maxRetries: 3
- ));
-
- $queue->registry()->register(self::$deleteFrom, new TypeConfig(
- executor: $executor,
- chunkKey: 'external_ids',
- chunkSize: 200,
- maxRetries: 2
- ));
-
- $queue->registry()->register(self::$syncFrom, new TypeConfig(
- executor: $executor,
- maxRetries: 3
- ));
$queue->registry()->register(self::$syncCustomer, new TypeConfig(
executor: $executor,
@@ -954,15 +934,15 @@
*/
protected function handleTheSavePost(int $postID, \WP_Post $post, bool $update, array $settings): void
{
+ error_log('==== [Square]::handleTheSavePost ====');
$this->queueOperation(self::$syncTo, [
'items' => [$postID],
- 'user_id' => $this->userID,
+ 'user' => user_can($post->post_author, 'manage_options') ? null : $post->post_author
], [
'priority' => 'high',
'delay' => 30,
]);
-
- update_post_meta($postID, BASE . '_square_sync_status', 'queued');
+ Meta::forPost($postID)->set('_'.$this->service_name.'_sync_status', 'queued');
}
/**
@@ -970,16 +950,18 @@
*/
public function handleDeletePost(int $postID): void
{
- $square_id = get_post_meta($postID, BASE . '_square_catalog_id', true);
+ $item_id = $this->getServiceItemID($postID);
- if ($square_id) {
- $this->queueOperation(self::$deleteFrom, [
- 'external_ids' => [$square_id],
- 'post_id' => $postID,
- ], [
- 'priority' => 'high',
- ]);
+ if (empty($item_id)) {
+ return;
}
+ $this->queueOperation(self::$deleteFrom, [
+ 'external_ids' => [$item_id],
+ 'post_id' => $postID,
+ ], [
+ 'priority' => 'high',
+ ]);
+
}
/**
@@ -1063,6 +1045,9 @@
private function processBatchSyncResponse(array $response, array $map, array &$success, array &$errors):void
{
+ error_log('==== SQUARE::processBatchSyncResponse =====');
+ error_log('Full response: '.print_r($response, true));
+
// Handle successful objects
if (!empty($response['objects'])) {
foreach ($response['objects'] as $object) {
@@ -1162,6 +1147,14 @@
// Get existing Square catalog ID if it exists
$existing_square_id = get_post_meta($postID, BASE . '_square_catalog_id', true);
+ $registrar = Registrar::getInstance($post_type);
+ $product_type = 'FOOD_AND_BEV';
+ if ($registrar) {
+ $conf = $registrar->getIntegration($this->service_name);
+ if ($conf) {
+ $product_type = $conf->getContentType();
+ }
+ }
// Build the base catalog object
$catalog_object = [
@@ -1170,7 +1163,7 @@
'item_data' => [
'name' => $post->post_title,
'description' => wp_strip_all_tags($post->post_content),
- 'product_type' => 'FOOD_AND_BEV',
+ 'product_type' => $product_type,
'variations' => [],
'is_taxable' => true,
]
@@ -1182,10 +1175,9 @@
}
// Add variations
- $variations = $meta->get('product_variations');
+ $variations = $meta->get('_square_product_variations');
if (empty($variations)) {
// Create default variation if none exist
- $price = floatval($meta->get('price') ?: 0);
$catalog_object['item_data']['variations'][] = [
'type' => 'ITEM_VARIATION',
'id' => $existing_square_id ? null : '#'.BASE.'menu_item_' . $postID . '_var_default',
@@ -1194,32 +1186,41 @@
'ordinal' => 0,
'pricing_type' => 'FIXED_PRICING',
'price_money' => [
- 'amount' => intval($price * 100), // Convert dollars to cents
- 'currency' => 'CAD'
+ 'amount' => $this->formatPrice($meta->get('_square_price')),
+ 'currency' => $this->getCurrency()
],
'sellable' => true,
'stockable' => true
]
];
} else {
+ $resetVariations = false;
foreach ($variations as $index => $variation) {
- $existing_var_id = get_post_meta($postID, BASE . '_square_variation_' . $index . '_id', true);
+ $id = '#'.BASE.'menu_item_' . $postID . '_var_' . $index;
+ if (empty($variation['item_id'])) {
+ $resetVariations = true;
+ $variations[$index]['item_id'] = $id;
+ $variation['item_id'] = $id;
+ }
$catalog_object['item_data']['variations'][] = [
'type' => 'ITEM_VARIATION',
- 'id' => $existing_var_id ?: '#'.BASE.'menu_item_' . $postID . '_var_' . $index,
+ 'id' => $variation['item_id'],
'item_variation_data' => [
'name' => $variation['name'] ?? 'Variation ' . ($index + 1),
'ordinal' => $index,
'pricing_type' => 'FIXED_PRICING',
'price_money' => [
- 'amount' => intval(floatval($variation['price'] ?? 0) * 100),
- 'currency' => 'CAD'
+ 'amount' =>$this->formatPrice($variation['price'] ?? 0),
+ 'currency' => $this->getCurrency()
],
'sellable' => true,
'stockable' => true
]
];
}
+ if ($resetVariations) {
+ $meta->set('_square_product_variations', $variations);
+ }
}
// Add categories if they exist
@@ -1262,98 +1263,7 @@
return $catalog_object;
}
- /**
- * Build variations from repeater field
- */
- private function buildVariations(int $postID, array $values, string $post_type): array
- {
- $variations = [];
- $product_variations = $values['product_variations'] ?? [];
- // Get variation field mapping
- $variation_map = $this->getVariationMapping($post_type);
-
- // If we have repeater variations
- if (!empty($product_variations) && is_array($product_variations)) {
- foreach ($product_variations as $index => $variation_data) {
- // Skip empty variations
- if (empty($variation_data['name']) && empty($variation_data['_square_sku'])) {
- continue;
- }
-
- $variation = [
- 'type' => 'ITEM_VARIATION',
- 'id' => '#' . $post_type . '_' . $postID . '_var_' . $index,
- 'item_variation_data' => [
- 'name' => $variation_data['name'] ?? 'Variation ' . ($index + 1),
- 'ordinal' => $index,
- 'pricing_type' => 'FIXED_PRICING'
- ]
- ];
-
- // Check for existing Square variation ID
- $square_var_id = get_post_meta($postID, BASE . '_square_variation_' . $index . '_id', true);
- if ($square_var_id) {
- $variation['id'] = $square_var_id;
- }
-
- // Map variation fields
- foreach ($variation_map as $square_field => $wp_field) {
- if (isset($variation_data[$wp_field])) {
- switch ($square_field) {
- case 'price':
- $variation['item_variation_data']['price_money'] = [
- 'amount' => intval($variation_data[$wp_field] * 100), // Convert to cents
- 'currency' => 'CAD'
- ];
- break;
-
- case 'sku':
- $variation['item_variation_data']['_square_sku'] = $variation_data[$wp_field];
- break;
-
- case 'track_inventory':
- $variation['item_variation_data']['track_inventory'] = (bool)$variation_data[$wp_field];
- break;
-
- case 'service_duration':
- if (!empty($variation_data[$wp_field])) {
- $variation['item_variation_data']['service_data'] = [
- 'duration_minutes' => intval($variation_data[$wp_field])
- ];
- }
- break;
-
- default:
- $variation['item_variation_data'][$square_field] = $variation_data[$wp_field];
- break;
- }
- }
- }
-
- $variations[] = $variation;
- }
- }
-
- // If no variations exist, create a default one from base price
- if (empty($variations) && !empty($values['price'])) {
- $variations[] = [
- 'type' => 'ITEM_VARIATION',
- 'id' => '#' . $post_type . '_' . $postID . '_var_default',
- 'item_variation_data' => [
- 'name' => 'Regular',
- 'ordinal' => 0,
- 'pricing_type' => 'FIXED_PRICING',
- 'price_money' => [
- 'amount' => intval($values['price'] * 100),
- 'currency' => 'CAD'
- ]
- ]
- ];
- }
-
- return $variations;
- }
/**
* Get variation mapping for post type
@@ -1616,13 +1526,9 @@
];
}
- // Generate username from email
- $username = sanitize_user(current(explode('@', $email)));
- $username = $this->generateUniqueUsername($username);
-
// Create user account without password (they'll set it via email)
$user_id = wp_create_user(
- $username,
+ $email,
wp_generate_password(20, true, true), // Temporary random password
$email
);
@@ -1637,7 +1543,7 @@
// Set user role (assuming you have a customer role defined)
$user = new \WP_User($user_id);
- $user->set_role(BASE.'foodie'); // Or whatever role
+ $user->set_role(BASE.'foodie');
// Generate password reset key
$reset_key = get_password_reset_key($user);
@@ -1653,7 +1559,7 @@
// Link to Square customer if exists
$square_customer_id = $this->getOrCreateSquareCustomer([
'email' => $email,
- 'name' => $username
+ 'name' => $email
]);
if ($square_customer_id) {
@@ -1668,22 +1574,6 @@
}
/**
- * Generate unique username
- */
- private function generateUniqueUsername(string $base): string
- {
- $username = $base;
- $counter = 1;
-
- while (username_exists($username)) {
- $username = $base . $counter;
- $counter++;
- }
-
- return $username;
- }
-
- /**
* Send welcome email with password setup
*/
private function sendWelcomeEmail(\WP_User $user, string $reset_key): void
@@ -1699,7 +1589,7 @@
"%s\n\n" .
"Once you've set your password, you can:\n" .
"- View your order history\n" .
- "- Save your favorite items\n" .
+// "- Save your favorite items\n" .
"- Speed up checkout with saved payment methods\n\n" .
"If you didn't create this account, please ignore this email.\n\n" .
"Thanks,\n",
@@ -1715,49 +1605,6 @@
);
}
- /**
- * Track user login for security
- */
- public function trackUserLogin(string $user_login, \WP_User $user): void
- {
- // Check if user has Square integration
- $role = jvbUserRole($user->ID);
- $registrar = Registrar::getInstance($role);
- if ($registrar) {
- $config = $registrar->getIntegration($this->service_name);
- if ($config->isCustomer()) {
- $login_count = (int)get_user_meta($user->ID, BASE . '_square_login_count', true);
- $login_count++;
-
- update_user_meta($user->ID, BASE . '_square_login_count', $login_count);
- update_user_meta($user->ID, BASE . '_square_last_login', current_time('mysql'));
-
- // Check if password reset is needed
- if ($login_count % self::PASSWORD_RESET_INTERVAL === 0) {
- $this->schedulePasswordReset($user->ID);
- }
- }
- }
- }
-
- /**
- * Schedule password reset for security
- */
- private function schedulePasswordReset(int $user_id): void
- {
- update_user_meta($user_id, BASE . '_square_password_reset_required', true);
-
- // Send notification
- $user = get_user_by('ID', $user_id);
- if ($user) {
- JVB()->email()->sendEmail(
- $user->user_email,
- '['.get_bloginfo('name').'] Security Code',
- 'For your security, enter this code to continue accessing your account and saved payment methods.',
- );
- }
- }
-
/******************************************************************
* WEBHOOK HANDLING
******************************************************************/
@@ -3643,4 +3490,10 @@
}, $posts->posts);
}
+
+ public function formatPrice(string|float|int $priceValue):int
+ {
+ //Convert dollars to cents
+ return intval(floatval($priceValue) * 100);
+ }
}
--
Gitblit v1.10.0