Changeset 3391546
- Timestamp:
- 11/07/2025 03:36:36 AM (5 months ago)
- Location:
- pay-with-flex/trunk
- Files:
-
- 6 edited
-
pay-with-flex.php (modified) (3 diffs)
-
readme.txt (modified) (2 diffs)
-
src/Resource/Price.php (modified) (3 diffs)
-
src/Resource/Product.php (modified) (4 diffs)
-
src/Resource/Resource.php (modified) (3 diffs)
-
vendor/composer/installed.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
pay-with-flex/trunk/pay-with-flex.php
r3390698 r3391546 4 4 * Plugin Name: Flex HSA/FSA Payments 5 5 * Description: Accept HSA/FSA payments directly in the checkout flow. 6 * Version: 3.1.1 16 * Version: 3.1.12 7 7 * Plugin URI: https://wordpress.org/plugins/pay-with-flex/ 8 8 * Author: Flex … … 518 518 } 519 519 /** 520 * React to the payment method being enabled. 521 */ 522 function payment_method_enabled(): void { 523 $gateway = payment_gateway(); 524 // Refresh the settings from the database so we are working with the latest version. 525 $gateway->init_settings(); 526 // If no API key is present, then there is nothing to do. 527 if ( empty( $gateway->api_key() ) ) { 528 return; 529 } 530 // Check to see if the webhook needs to be updated. 531 $webhook = Webhook::from_wc( $gateway ); 532 if ( $webhook->can( $webhook->needs() ) ) { 533 flex_update_webhook_async(); 534 } 535 for ( $i = 1;; $i++ ) { 520 * Enqueues a page of products to be synced. 521 * 522 * @param int $page The page number to enqueue. 523 * @param int $retries The number of retries that have been attempted. 524 * 525 * @throws \Exception If the product fails to be updated. 526 */ 527 function flex_product_sync_spawn( int $page, int $retries = 0 ): void { 528 flex_enqueue_async_action( hook: 'flex_product_sync', args: array( $page, $retries ), retries: $retries ); 529 } 530 /** 531 * Syncs a page of products with Flex. 532 * 533 * @param int $page The page number to sync. 534 * @param int $retries The number of retries that have been attempted. 535 * 536 * @throws FlexException If the API key is not set. 537 * @throws \Throwable Any caught exceptions. 538 */ 539 function flex_product_sync( int $page, int $retries = 0 ): void { 540 try { 541 $gateway = payment_gateway(); 542 if ( empty( $gateway->api_key() ) ) { 543 throw new FlexException( 'API Key is not set' ); 544 } 536 545 /** 537 546 * Fetch all of the products we support. … … 541 550 $products = wc_get_products( 542 551 array( 543 'page d' => $i,544 'type' => array_merge( Product::WC_TYPES, Price::WC_TYPES ),552 'page' => $page, 553 'type' => array_merge( Product::WC_TYPES, Price::WC_TYPES ), 545 554 ) 546 555 ); 547 if ( empty( $products ) ) {548 break;549 }550 556 // Enqueue all of them to be updated. 551 557 foreach ( $products as $product ) { 552 558 wc_update_product( $product->get_id(), $product ); 553 559 } 560 } catch ( \Throwable $previous ) { 561 flex_product_sync_spawn( $page, $retries + 1 ); 562 throw $previous; 563 } 564 } 565 add_action( hook_name: 'flex_product_sync', callback: __NAMESPACE__ . '\flex_product_sync' ); 566 /** 567 * React to the payment method being enabled. 568 */ 569 function payment_method_enabled(): void { 570 $gateway = payment_gateway(); 571 // Refresh the settings from the database so we are working with the latest version. 572 $gateway->init_settings(); 573 // If no API key is present, then there is nothing to do. 574 if ( empty( $gateway->api_key() ) ) { 575 return; 576 } 577 // Check to see if the webhook needs to be updated. 578 $webhook = Webhook::from_wc( $gateway ); 579 if ( $webhook->can( $webhook->needs() ) ) { 580 flex_update_webhook_async(); 581 } 582 $result = wc_get_products( 583 array( 584 'type' => array_merge( Product::WC_TYPES, Price::WC_TYPES ), 585 'paginate' => \true, 586 ) 587 ); 588 for ( $i = 1; $i <= $result->max_num_pages; $i++ ) { 589 flex_product_sync_spawn( $i ); 554 590 } 555 591 } -
pay-with-flex/trunk/readme.txt
r3390698 r3391546 4 4 Requires at least: 6.8 5 5 Tested up to: 6.8 6 Stable tag: 3.1.1 16 Stable tag: 3.1.12 7 7 Requires PHP: 8.1 8 8 License: GPLv3 or later … … 55 55 56 56 == Changelog == 57 58 = 3.1.12 = 59 * Fixed a bug that prevented products and prices from being created if they already exist under a different account. 60 * Changed the plugin and payment method activation behavior. Product & Price syncing is now spawned asyncoursly which significantly improves the plugin activation speed. 57 61 58 62 = 3.1.11 = -
pay-with-flex/trunk/src/Resource/Price.php
r3352167 r3391546 12 12 use Automattic\WooCommerce\Enums\ProductType; 13 13 use Flex\Exception\FlexException; 14 use Flex\Exception\FlexResponseException; 14 15 /** 15 16 * Flex Price … … 205 206 * @param ResourceAction $action The action to perform. 206 207 * 207 * @throws FlexException If anything goes wrong. 208 * @throws FlexException If the response is malformed. 209 * @throws FlexResponseException Rethrows exception if it cannot be handled. 208 210 */ 209 211 public function exec( \Flex\Resource\ResourceAction $action ): void { … … 223 225 $existing = new self( id: $this->id, active: \false, product: $this->product ); 224 226 // Retrieve the existing price so we do not drop any existing values on re-creation. 225 $data = $this->remote_request( '/v1/prices/' . $this->id ); 226 if ( isset( $data['price'] ) && is_array( $data['price'] ) ) { 227 // Remove fields that we no longer care about. 228 unset( $data['price']['price_id'] ); 229 unset( $data['price']['price'] ); 230 $price = array_merge( $data['price'], $this->jsonSerialize() ); 231 } 232 } 233 $data = $this->remote_request( 234 match ( $action ) { 235 \Flex\Resource\ResourceAction::CREATE => '/v1/prices', 236 \Flex\Resource\ResourceAction::UPDATE => '/v1/prices/' . $this->id, 237 }, 238 array( 239 'method' => 'POST', 240 'flex' => array( 'data' => array( 'price' => $price ) ), 241 ) 242 ); 243 if ( ! isset( $data['price'] ) ) { 244 throw new FlexException( 'Missing price in response.' ); 245 } 246 $this->extract( $data['price'] ); 227 try { 228 $data = $this->remote_request( '/v1/prices/' . $this->id ); 229 if ( isset( $data['price'] ) && is_array( $data['price'] ) ) { 230 // Remove fields that we no longer care about. 231 unset( $data['price']['price_id'] ); 232 unset( $data['price']['price'] ); 233 $price = array_merge( $data['price'], $this->jsonSerialize() ); 234 } 235 } catch ( FlexResponseException $e ) { 236 if ( $e->code() !== 404 ) { 237 throw $e; 238 } 239 $existing = null; 240 } 241 } 242 try { 243 $data = $this->remote_request( 244 match ( $action ) { 245 \Flex\Resource\ResourceAction::CREATE => '/v1/prices', 246 \Flex\Resource\ResourceAction::UPDATE => '/v1/prices/' . $this->id, 247 }, 248 array( 249 'method' => 'POST', 250 'flex' => array( 'data' => array( 'price' => $price ) ), 251 ) 252 ); 253 if ( ! isset( $data['price'] ) ) { 254 throw new FlexException( 'Missing price in response.' ); 255 } 256 $this->extract( $data['price'] ); 257 } catch ( FlexResponseException $e ) { 258 // Ensure the price exists before trying again. 259 if ( $e->code() === 422 ) { 260 $prev = $this->product->id(); 261 $this->product->exec( \Flex\Resource\ResourceAction::REFRESH ); 262 // If the product ids no longer match then the product was re-created. 263 if ( $this->product->id() !== $prev ) { 264 $this->exec( $action ); 265 return; 266 } 267 } 268 if ( \Flex\Resource\ResourceAction::CREATE === $action ) { 269 throw $e; 270 } 271 // Recreate the price. 272 if ( $e->code() === 404 ) { 273 $this->exec( \Flex\Resource\ResourceAction::CREATE ); 274 return; 275 } 276 throw $e; 277 } 247 278 if ( null !== $this->wc ) { 248 279 $this->apply_to( $this->wc ); -
pay-with-flex/trunk/src/Resource/Product.php
r3352167 r3391546 12 12 use Automattic\WooCommerce\Enums\ProductType; 13 13 use Flex\Exception\FlexException; 14 use Flex\Exception\FlexResponseException; 14 15 /** 15 16 * Flex Product … … 185 186 return match ( $action ) { 186 187 \Flex\Resource\ResourceAction::CREATE => \true, 187 \Flex\Resource\ResourceAction::UPDATE => null !== $this->id,188 \Flex\Resource\ResourceAction::UPDATE, \Flex\Resource\ResourceAction::REFRESH => null !== $this->id, 188 189 default => \false, 189 190 }; … … 194 195 * @param ResourceAction $action The action to perform. 195 196 * 196 * @throws FlexException If anything goes wrong. 197 * @throws FlexException If the response is malformed. 198 * @throws FlexResponseException Rethrows exception if it cannot be handled. 197 199 */ 198 200 public function exec( \Flex\Resource\ResourceAction $action ): void { … … 206 208 $existing = new self( name: $this->name, id: $this->id, active: \false ); 207 209 // Retrieve the existing product so we do not drop any existing values on re-creation. 208 $data = $this->remote_request( '/v1/products/' . $this->id ); 209 if ( isset( $data['product'] ) && is_array( $data['product'] ) ) { 210 // Remove fields that we no longer care about. 211 unset( $data['product']['product_id'] ); 212 $product = array_merge( $data['product'], $this->jsonSerialize() ); 213 } 214 } 215 $data = $this->remote_request( 216 match ( $action ) { 217 \Flex\Resource\ResourceAction::CREATE => '/v1/products', 218 \Flex\Resource\ResourceAction::UPDATE => '/v1/products/' . $this->id, 219 }, 220 array( 221 'method' => match ( $action ) { 222 \Flex\Resource\ResourceAction::CREATE => 'POST', 223 \Flex\Resource\ResourceAction::UPDATE => 'PATCH', 210 try { 211 $data = $this->remote_request( '/v1/products/' . $this->id ); 212 if ( isset( $data['product'] ) && is_array( $data['product'] ) ) { 213 // Remove fields that we no longer care about. 214 unset( $data['product']['product_id'] ); 215 $product = array_merge( $data['product'], $this->jsonSerialize() ); 216 } 217 } catch ( FlexResponseException $e ) { 218 if ( $e->code() !== 404 ) { 219 throw $e; 220 } 221 $existing = null; 222 } 223 } 224 try { 225 $data = $this->remote_request( 226 match ( $action ) { 227 \Flex\Resource\ResourceAction::CREATE => '/v1/products', 228 \Flex\Resource\ResourceAction::UPDATE, \Flex\Resource\ResourceAction::REFRESH => '/v1/products/' . $this->id, 224 229 }, 225 'flex' => array( 'data' => array( 'product' => $product ) ), 226 ) 227 ); 228 if ( ! isset( $data['product'] ) ) { 229 throw new FlexException( 'Missing product in response.' ); 230 } 231 $this->extract( $data['product'] ); 230 array( 231 'method' => match ( $action ) { 232 \Flex\Resource\ResourceAction::CREATE => 'POST', 233 \Flex\Resource\ResourceAction::UPDATE => 'PATCH', 234 \Flex\Resource\ResourceAction::REFRESH => 'GET', 235 }, 236 'flex' => match ( $action ) { 237 \Flex\Resource\ResourceAction::CREATE, \Flex\Resource\ResourceAction::UPDATE => array( 'data' => array( 'product' => $product ) ), 238 \Flex\Resource\ResourceAction::REFRESH => array(), 239 }, 240 ) 241 ); 242 if ( ! isset( $data['product'] ) ) { 243 throw new FlexException( 'Missing product in response.' ); 244 } 245 $this->extract( $data['product'] ); 246 } catch ( FlexResponseException $e ) { 247 if ( \Flex\Resource\ResourceAction::CREATE === $action ) { 248 throw $e; 249 } 250 if ( $e->code() !== 404 ) { 251 throw $e; 252 } 253 // If an update or refresh was being performed and the API returned a 404, then re-create it. 254 $this->exec( \Flex\Resource\ResourceAction::CREATE ); 255 return; 256 } 232 257 if ( null !== $this->wc ) { 233 258 $this->apply_to( $this->wc ); -
pay-with-flex/trunk/src/Resource/Resource.php
r3390698 r3391546 89 89 $span = sentry()->getSpan(); 90 90 if ( null !== $span ) { 91 $headers['traceparent'] = $span->toW3CTraceparent(); 91 $headers['baggage'] = $span->toBaggage(); 92 $headers['sentry-trace'] = $span->toTraceparent(); 92 93 } 94 $method = $args['method'] ?? 'GET'; 93 95 /** 94 96 * The metadata to add to the breadcrumb … … 96 98 */ 97 99 $meta = array( 98 'method' => $ args['method'] ?? 'GET',100 'method' => $method, 99 101 'url' => $base . $path, 100 102 ); … … 120 122 $code = wp_remote_retrieve_response_code( $response ); 121 123 $meta['status_code'] = $code; 124 $body = wp_remote_retrieve_body( $response ); 125 if ( ! $body ) { 126 sentry()->addBreadcrumb( new Breadcrumb( category: 'request', level: Breadcrumb::LEVEL_ERROR, type: Breadcrumb::TYPE_HTTP, metadata: $meta ) ); 127 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped 128 throw new FlexResponseException( $response, "Missing Response Body {$method} {$path} {$code}" ); 129 } 122 130 if ( $code < 200 || $code >= 300 ) { 123 131 sentry()->addBreadcrumb( new Breadcrumb( category: 'request', level: Breadcrumb::LEVEL_ERROR, type: Breadcrumb::TYPE_HTTP, metadata: $meta ) ); 124 132 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped 125 throw new FlexResponseException( $response, 'Flex responded with a ' . $code ); 126 } 127 $body = wp_remote_retrieve_body( $response ); 128 if ( ! $body ) { 129 sentry()->addBreadcrumb( new Breadcrumb( category: 'request', level: Breadcrumb::LEVEL_ERROR, type: Breadcrumb::TYPE_HTTP, metadata: $meta ) ); 130 // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped 131 throw new FlexResponseException( $response, 'Missing response body.' ); 133 throw new FlexResponseException( $response, "Response Failed {$method} {$path} {$code} {$body}" ); 132 134 } 133 135 try { -
pay-with-flex/trunk/vendor/composer/installed.php
r3390698 r3391546 3 3 namespace Flex; 4 4 5 return array('root' => array('name' => '__root__', 'pretty_version' => 'dev-main', 'version' => 'dev-main', 'reference' => ' 9627e2c3de7c8ca3d3a549b771ff6cdbbba73cf5', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => \false), 'versions' => array('__root__' => array('pretty_version' => 'dev-main', 'version' => 'dev-main', 'reference' => '9627e2c3de7c8ca3d3a549b771ff6cdbbba73cf5', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => \false), 'guzzlehttp/psr7' => array('pretty_version' => '2.8.0', 'version' => '2.8.0.0', 'reference' => '21dc724a0583619cd1652f673303492272778051', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), 'dev_requirement' => \false), 'jean85/pretty-package-versions' => array('pretty_version' => '2.1.1', 'version' => '2.1.1.0', 'reference' => '4d7aa5dab42e2a76d99559706022885de0e18e1a', 'type' => 'library', 'install_path' => __DIR__ . '/../jean85/pretty-package-versions', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-factory' => array('pretty_version' => '1.1.0', 'version' => '1.1.0.0', 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-factory', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-factory-implementation' => array('dev_requirement' => \false, 'provided' => array(0 => '1.0')), 'psr/http-message' => array('pretty_version' => '2.0', 'version' => '2.0.0.0', 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-message-implementation' => array('dev_requirement' => \false, 'provided' => array(0 => '1.0')), 'psr/log' => array('pretty_version' => '3.0.2', 'version' => '3.0.2.0', 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'dev_requirement' => \false), 'ralouphie/getallheaders' => array('pretty_version' => '3.0.3', 'version' => '3.0.3.0', 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), 'dev_requirement' => \false), 'sentry/sentry' => array('pretty_version' => '4.18.0', 'version' => '4.18.0.0', 'reference' => '75f7efb7d435d24767c93d0081b8edf228be5772', 'type' => 'library', 'install_path' => __DIR__ . '/../sentry/sentry', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/deprecation-contracts' => array('pretty_version' => 'v3.6.0', 'version' => '3.6.0.0', 'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/options-resolver' => array('pretty_version' => 'v7.3.3', 'version' => '7.3.3.0', 'reference' => '0ff2f5c3df08a395232bbc3c2eb7e84912df911d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/options-resolver', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/polyfill-php84' => array('pretty_version' => 'v1.33.0', 'version' => '1.33.0.0', 'reference' => 'd8ced4d875142b6a7426000426b8abc631d6b191', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php84', 'aliases' => array(), 'dev_requirement' => \false)));5 return array('root' => array('name' => '__root__', 'pretty_version' => 'dev-main', 'version' => 'dev-main', 'reference' => '0e52014530a31bb82f7684bbaeaf666d3d730fc1', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => \false), 'versions' => array('__root__' => array('pretty_version' => 'dev-main', 'version' => 'dev-main', 'reference' => '0e52014530a31bb82f7684bbaeaf666d3d730fc1', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev_requirement' => \false), 'guzzlehttp/psr7' => array('pretty_version' => '2.8.0', 'version' => '2.8.0.0', 'reference' => '21dc724a0583619cd1652f673303492272778051', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), 'dev_requirement' => \false), 'jean85/pretty-package-versions' => array('pretty_version' => '2.1.1', 'version' => '2.1.1.0', 'reference' => '4d7aa5dab42e2a76d99559706022885de0e18e1a', 'type' => 'library', 'install_path' => __DIR__ . '/../jean85/pretty-package-versions', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-factory' => array('pretty_version' => '1.1.0', 'version' => '1.1.0.0', 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-factory', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-factory-implementation' => array('dev_requirement' => \false, 'provided' => array(0 => '1.0')), 'psr/http-message' => array('pretty_version' => '2.0', 'version' => '2.0.0.0', 'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/http-message', 'aliases' => array(), 'dev_requirement' => \false), 'psr/http-message-implementation' => array('dev_requirement' => \false, 'provided' => array(0 => '1.0')), 'psr/log' => array('pretty_version' => '3.0.2', 'version' => '3.0.2.0', 'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3', 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), 'dev_requirement' => \false), 'ralouphie/getallheaders' => array('pretty_version' => '3.0.3', 'version' => '3.0.3.0', 'reference' => '120b605dfeb996808c31b6477290a714d356e822', 'type' => 'library', 'install_path' => __DIR__ . '/../ralouphie/getallheaders', 'aliases' => array(), 'dev_requirement' => \false), 'sentry/sentry' => array('pretty_version' => '4.18.0', 'version' => '4.18.0.0', 'reference' => '75f7efb7d435d24767c93d0081b8edf228be5772', 'type' => 'library', 'install_path' => __DIR__ . '/../sentry/sentry', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/deprecation-contracts' => array('pretty_version' => 'v3.6.0', 'version' => '3.6.0.0', 'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/options-resolver' => array('pretty_version' => 'v7.3.3', 'version' => '7.3.3.0', 'reference' => '0ff2f5c3df08a395232bbc3c2eb7e84912df911d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/options-resolver', 'aliases' => array(), 'dev_requirement' => \false), 'symfony/polyfill-php84' => array('pretty_version' => 'v1.33.0', 'version' => '1.33.0.0', 'reference' => 'd8ced4d875142b6a7426000426b8abc631d6b191', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/polyfill-php84', 'aliases' => array(), 'dev_requirement' => \false)));
Note: See TracChangeset
for help on using the changeset viewer.