Changeset 3449399
- Timestamp:
- 01/29/2026 09:32:33 AM (2 months ago)
- Location:
- ecommpay-payments/trunk
- Files:
-
- 16 edited
-
assets/js/backend.js (modified) (1 diff)
-
common/EcpCore.php (modified) (1 diff)
-
common/includes/EcpCallbacksHandler.php (modified) (1 diff)
-
common/includes/EcpGatewayOrder.php (modified) (1 diff)
-
common/includes/EcpGatewayPayment.php (modified) (10 diffs)
-
common/includes/EcpGatewayPaymentProvider.php (modified) (4 diffs)
-
common/models/EcpGatewayInfoOperation.php (modified) (1 diff)
-
common/models/EcpGatewayInfoProvider.php (modified) (1 diff)
-
common/modules/EcpModuleAdminUI.php (modified) (6 diffs)
-
composer.json (modified) (1 diff)
-
gateway-ecommpay.php (modified) (1 diff)
-
package.json (modified) (1 diff)
-
readme.txt (modified) (1 diff)
-
views/admin/sections/html-buttons.php (modified) (3 diffs)
-
views/html-meta-box-error.php (modified) (2 diffs)
-
views/html-meta-box-payment-info.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
ecommpay-payments/trunk/assets/js/backend.js
r3218798 r3449399 42 42 action: 'ecommpay_manual_transaction_actions', 43 43 post: this.postID.val(), 44 nonce: ajax_object.nonce, 44 45 }, 45 46 dataObject -
ecommpay-payments/trunk/common/EcpCore.php
r3440800 r3449399 61 61 * @since 2.0.0 62 62 */ 63 public const WC_ECP_VERSION = '4.2. 1';63 public const WC_ECP_VERSION = '4.2.2'; 64 64 65 65 public const ECOMMPAY_PAYMENT_METHOD = 'ecommpay'; -
ecommpay-payments/trunk/common/includes/EcpCallbacksHandler.php
r3336019 r3449399 130 130 */ 131 131 private function get_order( EcpGatewayInfoCallback $info ): EcpGatewayOrder { 132 $payment_id = $info->get_payment()->get_id() ?? $_GET['payment_id']; 132 $payment_id = $info->get_payment()->get_id(); 133 134 if ( ! $payment_id && isset( $_GET['payment_id'] ) ) { 135 $payment_id = sanitize_text_field( wp_unslash( $_GET['payment_id'] ) ); 136 } 133 137 134 138 $order_number = EcpGatewayOrder::get_order_id_from_callback( $info ); -
ecommpay-payments/trunk/common/includes/EcpGatewayOrder.php
r3440800 r3449399 63 63 public static function get_order_id_from_callback( EcpGatewayInfoCallback $info ) { 64 64 global $wpdb; 65 $payment_id = $info->get_payment()->get_id() ?? $_GET['payment_id']; 65 66 $payment_id = $info->get_payment()->get_id(); 67 68 if ( ! $payment_id && isset( $_GET['payment_id'] ) ) { 69 $payment_id = sanitize_text_field( wp_unslash( $_GET['payment_id'] ) ); 70 } 66 71 67 72 if ( ecp_HPOS_enabled() ) { -
ecommpay-payments/trunk/common/includes/EcpGatewayPayment.php
r3336019 r3449399 15 15 use DateTimeInterface; 16 16 use Exception; 17 use JsonSerializable; 17 18 18 19 defined( 'ABSPATH' ) || exit; … … 28 29 * @category Class 29 30 */ 30 class EcpGatewayPayment {31 class EcpGatewayPayment implements JsonSerializable { 31 32 32 33 … … 57 58 * @var ?EcpGatewayInfoCustomer 58 59 */ 59 private ?EcpGatewayInfoCustomer $customer ;60 private ?EcpGatewayInfoCustomer $customer = null; 60 61 61 62 /** … … 64 65 * @var ?EcpGatewayInfoAccount 65 66 */ 66 private ?EcpGatewayInfoAccount $account ;67 private ?EcpGatewayInfoAccount $account = null; 67 68 68 69 /** … … 71 72 * @var ?EcpGatewayInfoACS 72 73 */ 73 private ?EcpGatewayInfoACS $acs ;74 private ?EcpGatewayInfoACS $acs = null; 74 75 75 76 /** … … 155 156 $this->status_transition = new EcpGatewayPaymentStatusTransition( 156 157 [ 157 'old' => $old_status,158 'new' => $new_status,158 'old' => $old_status, 159 'new' => $new_status, 159 160 'note' => $note 160 161 ] … … 196 197 197 198 switch ( true ) { 198 case ! empty ( $transition->get_old() ):199 case ! empty( $transition->get_old() ): 199 200 if ( ! $transition->is_changed() ) { 200 201 return; … … 487 488 } ); 488 489 489 if ( empty ( $authorized_operations ) ) {490 if ( empty( $authorized_operations ) ) { 490 491 return null; 491 492 } … … 513 514 } 514 515 515 return ! empty ( $this->info->get_sum() ) ? $this->info->get_sum()->get_amount() : null;516 return ! empty( $this->info->get_sum() ) ? $this->info->get_sum()->get_amount() : null; 516 517 } 517 518 … … 664 665 return null; 665 666 } 667 668 /** 669 * <h2>Converts payment data to array for caching.</h2> 670 * <p>Serializes only the API data, excluding the WooCommerce order object.</p> 671 * <p>Implements JsonSerializable interface.</p> 672 * 673 * @return array Payment data as associative array 674 * @since 3.3.2 675 */ 676 public function jsonSerialize(): array { 677 return [ 678 'payment_id' => $this->get_id(), 679 'info' => $this->info ? $this->info->to_array() : null, 680 'customer' => $this->customer ? $this->customer->to_array() : null, 681 'account' => $this->account ? $this->account->to_array() : null, 682 'acs' => $this->acs ? $this->acs->to_array() : null, 683 'operations' => array_map(static function (EcpGatewayInfoOperation $operation): array { 684 return $operation->to_array(); 685 }, $this->operations ), 686 'errors' => array_map( static function ( EcpGatewayInfoError $error ): array { 687 return $error->to_array(); 688 }, $this->errors ), 689 ]; 690 } 691 692 /** 693 * <h2>Restores payment data from cached array.</h2> 694 * 695 * @param EcpGatewayOrder $order Parent order 696 * @param array $data Cached payment data 697 * 698 * @return static Restored payment object 699 * @since 3.3.2 700 */ 701 public static function fromCache( EcpGatewayOrder $order, array $data ): EcpGatewayPayment { 702 $payment = new static( $order ); 703 704 if ( ! empty( $data['info'] ) && is_array( $data['info'] ) ) { 705 $payment->set_info( new EcpGatewayInfoPayment( $data['info'] ) ); 706 } 707 708 if ( ! empty( $data['customer'] ) && is_array( $data['customer'] ) ) { 709 $payment->set_customer( new EcpGatewayInfoCustomer( $data['customer'] ) ); 710 } 711 712 if ( ! empty( $data['account'] ) && is_array( $data['account'] ) ) { 713 $payment->set_account( new EcpGatewayInfoAccount( $data['account'] ) ); 714 } 715 716 if ( ! empty( $data['acs'] ) && is_array( $data['acs'] ) ) { 717 $payment->set_acs( new EcpGatewayInfoACS( $data['acs'] ) ); 718 } 719 720 if ( ! empty( $data['operations'] ) && is_array( $data['operations'] ) ) { 721 foreach ( $data['operations'] as $op_data ) { 722 if ( is_array( $op_data ) ) { 723 $payment->add_operation( new EcpGatewayInfoOperation( $op_data ) ); 724 } 725 } 726 } 727 728 if ( ! empty( $data['errors'] ) && is_array( $data['errors'] ) ) { 729 foreach ( $data['errors'] as $err_data ) { 730 if ( is_array( $err_data ) ) { 731 $payment->errors[] = new EcpGatewayInfoError( $err_data ); 732 } 733 } 734 } 735 736 return $payment; 737 } 666 738 } -
ecommpay-payments/trunk/common/includes/EcpGatewayPaymentProvider.php
r3218798 r3449399 28 28 ecp_get_log()->debug( __( 'Reload?', 'woo-ecommpay' ), $reload ? __( 'Yes', 'woo-ecommpay' ) : __( 'No', 'woo-ecommpay' ) ); 29 29 30 if ( ! $reload && $this->is_transaction_caching_enabled() ) { 31 ecp_get_log()->info( __( 'Try loading payment data from cache...', 'woo-ecommpay' ) ); 32 $transient = get_transient( $this->get_transient_id( $order->get_payment_id() ) ); 33 34 if ( $transient ) { 35 // new EcpGatewayInfoStatus(json_decode($transient, true)) 36 $payment = @unserialize( $transient ); 37 38 if ( $payment instanceof EcpGatewayPayment ) { 39 ecp_get_log()->info( __( 'Payment loaded from cache. Cache data exists.', 'woo-ecommpay' ) ); 40 41 return $payment; 42 } 43 44 ecp_get_log()->warning( __( 'Cache data corrupted:', 'woo-ecommpay' ), $transient ); 45 } else { 46 ecp_get_log()->info( __( 'Invalid cache data.', 'woo-ecommpay' ) ); 30 if ( ! $reload ) { 31 $cached_payment = $this->tryLoadFromCache( $order ); 32 if ( $cached_payment ) { 33 return $cached_payment; 47 34 } 48 35 } … … 63 50 64 51 return $payment; 52 } 53 54 /** 55 * Tries to load payment from cache. 56 * 57 * @param EcpGatewayOrder $order 58 * 59 * @return EcpGatewayPayment|null Returns payment if found in cache, null otherwise 60 */ 61 private function tryLoadFromCache( EcpGatewayOrder $order ): ?EcpGatewayPayment { 62 if ( ! $this->is_transaction_caching_enabled() ) { 63 return null; 64 } 65 66 ecp_get_log()->info( __( 'Try loading payment data from cache...', 'woo-ecommpay' ) ); 67 68 $transient = get_transient( $this->get_transient_id( $order->get_payment_id() ) ); 69 70 if ( ! $transient ) { 71 ecp_get_log()->info( __( 'Invalid cache data.', 'woo-ecommpay' ) ); 72 73 return null; 74 } 75 76 $cached_data = json_decode( $transient, true ); 77 78 if ( ! is_array( $cached_data ) || empty( $cached_data['payment_id'] ) ) { 79 ecp_get_log()->warning( __( 'Cache data corrupted or invalid format', 'woo-ecommpay' ) ); 80 81 return null; 82 } 83 84 try { 85 $payment = EcpGatewayPayment::fromCache( $order, $cached_data ); 86 ecp_get_log()->info( __( 'Payment loaded from cache. Cache data exists.', 'woo-ecommpay' ) ); 87 88 return $payment; 89 } catch ( Exception $e ) { 90 ecp_get_log()->warning( 91 __( 'Failed to restore payment from cache:', 'woo-ecommpay' ), 92 $e->getMessage() 93 ); 94 95 return null; 96 } 65 97 } 66 98 … … 90 122 91 123 if ( count( $status->get_errors() ) > 0 ) { 124 $info = null; 92 125 if ( $status->try_get_payment( $info ) ) { 93 126 $payment->set_info( $info ); … … 130 163 $expiration = apply_filters( 'woocommerce_ecommpay_transaction_cache_expiration', $expiration ); 131 164 165 132 166 ecp_get_log()->debug( __( 'Expiration length:.', 'woo-ecommpay' ), $expiration ); 167 168 $json_data = json_encode( $payment, JSON_THROW_ON_ERROR ); 169 133 170 set_transient( 134 171 $this->get_transient_id( $payment->get_id() ), 135 serialize( $payment ),172 $json_data, 136 173 $expiration 137 174 ); -
ecommpay-payments/trunk/common/models/EcpGatewayInfoOperation.php
r3218798 r3449399 243 243 } 244 244 245 protected function packRules(): array { 246 return [ 247 self::FIELD_DATE => function ( $value ) { 248 return $value->format( DateTimeInterface::RFC3339 ); 249 }, 250 self::FIELD_CREATED_DATE => function ( $value ) { 251 return $value->format( DateTimeInterface::RFC3339 ); 252 }, 253 ]; 254 } 255 245 256 protected function unpackRules(): array { 246 257 return [ -
ecommpay-payments/trunk/common/models/EcpGatewayInfoProvider.php
r3218798 r3449399 113 113 } 114 114 115 protected function packRules(): array { 116 return [ 117 self::FIELD_DATE => function ( $value ) { 118 return $value->format( DateTimeInterface::RFC3339 ); 119 }, 120 ]; 121 } 115 122 116 123 /** -
ecommpay-payments/trunk/common/modules/EcpModuleAdminUI.php
r3336019 r3449399 28 28 public const ACTION_BUTTON_CLASS = 'ecp-action-button'; 29 29 public const WP_REFUND_BUTTON_SELECTOR = '.button.refund-items'; 30 31 public const ACTION_REFRESH = 'refresh'; 32 public const ACTION_CAPTURE = 'capture'; 33 public const ACTION_CANCEL = 'cancel'; 34 public const ACTION_REFUND = 'refund'; 35 36 /** 37 * List of all allowed payment actions for AJAX requests 38 */ 39 private const ALLOWED_ACTIONS = [ 40 self::ACTION_REFRESH, 41 self::ACTION_CAPTURE, 42 self::ACTION_CANCEL, 43 self::ACTION_REFUND, 44 ]; 45 46 /** 47 * List of allowed payment actions that perform API calls 48 */ 49 private const ALLOWED_API_ACTIONS = [ 50 self::ACTION_CAPTURE, 51 self::ACTION_CANCEL, 52 self::ACTION_REFUND, 53 ]; 30 54 31 55 /** … … 370 394 'ecommpay-backend', 371 395 'ajax_object', 372 [ 'ajax_url' => admin_url( 'admin-ajax.php' ) ] 396 [ 397 'ajax_url' => admin_url( 'admin-ajax.php' ), 398 'nonce' => wp_create_nonce( 'ecommpay_manual_action' ) 399 ] 373 400 ); 374 401 } … … 415 442 */ 416 443 public function ajax_manual_request_actions(): void { 444 // Security: Verify nonce for CSRF protection 445 check_ajax_referer( 'ecommpay_manual_action', 'nonce' ); 446 417 447 $param_action = wc_get_var( $_REQUEST['ecommpay_action'] ); 418 448 $param_post = wc_get_var( $_REQUEST['post'] ); … … 422 452 } 423 453 454 // Security: Validate action against whitelist to prevent code injection 455 if ( ! in_array( $param_action, self::ALLOWED_ACTIONS, true ) ) { 456 wp_die( 'Invalid payment action requested.' ); 457 } 458 424 459 if ( ! woocommerce_ecommpay_can_user_manage_payments( $param_action ) ) { 425 printf( 'Your user is not capable of %s payments.', $param_action ); 426 exit; 460 wp_die( 'Insufficient permissions for payment management.' ); 427 461 } 428 462 … … 430 464 431 465 switch ( $param_action ) { 432 case 'refresh':466 case self::ACTION_REFRESH: 433 467 $order->get_payment( true, true ); 434 468 break; … … 454 488 // Based on the current transaction state, we check if the requested action is allowed 455 489 if ( ! $order->is_action_allowed( $param_action ) ) { 456 // The action was not allowed .490 // The action was not allowed - don't expose internal details 457 491 throw new EcpGatewayAPIException( 458 492 sprintf( 459 'Action: "%s", is not allowed for order #%d, with type state "%s"', 460 $param_action, 461 $order->get_id(), 462 $transaction_info->get_current_type() 493 'Action is not allowed for order #%d with current transaction state', 494 $order->get_id() 463 495 ) 464 496 ); 465 497 } 466 498 467 // Check if the action method is available in the payment class 468 if ( ! method_exists( $api, $param_action ) ) { 469 throw new EcpGatewayAPIException( 470 sprintf( 471 'Unsupported action: "%s".', 472 $param_action 473 ) 474 ); 499 // Security: Double-check action is in whitelist before method call 500 if ( ! in_array( $param_action, self::ALLOWED_API_ACTIONS, true ) ) { 501 throw new EcpGatewayAPIException( 'Invalid payment action requested.' ); 475 502 } 476 503 477 $payment_amount = wc_get_var( $_REQUEST['$payment_amount'] );478 479 // Fetch amount if sent.480 $amount = $payment_amount !== null481 ? ecp_price_custom_to_multiplied(482 $payment_amount,483 $ transaction_info->get_currency()484 )485 : $transaction_info->get_remaining_balance();486 487 // Call the action method and parse the transaction id and order object488 $api->$param_action(489 $transaction_id,490 $order,491 ecp_price_multiplied_to_float( $amount, $transaction_info->get_currency() )492 );504 // Security: Use explicit method calls instead of variable function call 505 switch ( $param_action ) { 506 case self::ACTION_CAPTURE: 507 $api->capture( $order ); 508 break; 509 case self::ACTION_CANCEL: 510 $api->cancel( $order ); 511 break; 512 case self::ACTION_REFUND: 513 // For refund, find the unprocessed refund created by WooCommerce 514 $refund = $order->find_unprocessed_refund(); 515 $api->refund( $refund, $order ); 516 break; 517 default: 518 throw new EcpGatewayAPIException( 'Payment action not supported.' ); 519 } 493 520 } catch ( EcpGatewayAPIException $e ) { 494 echo $e->getMessage();521 echo esc_html( $e->getMessage() ); 495 522 $e->write_to_logs(); 496 523 exit; -
ecommpay-payments/trunk/composer.json
r3218798 r3449399 6 6 } 7 7 }, 8 "require": {} 8 "require": { 9 "ext-json": "*" 10 } 9 11 } -
ecommpay-payments/trunk/gateway-ecommpay.php
r3440800 r3449399 5 5 * GitHub Plugin URI: 6 6 * Description: Easy payment from WooCommerce by different methods in single Payment Page. 7 * Version: 4.2. 17 * Version: 4.2.2 8 8 * License: GPL2 9 9 * License URI: https://www.gnu.org/licenses/gpl-2.0.html -
ecommpay-payments/trunk/package.json
r3218798 r3449399 1 1 { 2 "name": "woocommerce-ecommpay", 3 "version": "1.0.0", 4 "description": "", 5 "scripts": { 6 "start": "wp-scripts start", 7 "build": "wp-scripts build", 8 "plugin-zip": "wp-scripts plugin-zip", 9 "check-engines": "wp-scripts check-engines", 10 "check-licenses": "wp-scripts check-licenses", 11 "format": "wp-scripts format", 12 "lint:css": "wp-scripts lint-style", 13 "lint:js": "wp-scripts lint-js", 14 "lint:md:docs": "wp-scripts lint-md-docs", 15 "lint:pkg-json": "wp-scripts lint-pkg-json", 16 "packages-update": "wp-scripts packages-update", 17 "test:e2e": "wp-scripts test-e2e", 18 "test:unit": "wp-scripts test-unit-js" 19 }, 20 "author": "ECOMMPAY", 21 "license": "GPL-2.0-only", 22 "devDependencies": { 23 "@types/applepayjs": "^14.0.9", 24 "@wordpress/scripts": "^26.19.0" 25 }, 26 "dependencies": { 27 "@wordpress/element": "^5.27.0", 28 "@wordpress/html-entities": "^3.50.0", 29 "use-debounce": "^10.0.0" 30 } 2 "name": "woocommerce-ecommpay", 3 "version": "1.0.0", 4 "description": "", 5 "scripts": { 6 "start": "wp-scripts start", 7 "build": "wp-scripts build", 8 "plugin-zip": "wp-scripts plugin-zip", 9 "check-engines": "wp-scripts check-engines", 10 "check-licenses": "wp-scripts check-licenses", 11 "format": "wp-scripts format", 12 "lint:css": "wp-scripts lint-style", 13 "lint:js": "wp-scripts lint-js", 14 "lint:md:docs": "wp-scripts lint-md-docs", 15 "lint:pkg-json": "wp-scripts lint-pkg-json", 16 "packages-update": "wp-scripts packages-update", 17 "test:e2e": "wp-scripts test-e2e", 18 "test:unit": "wp-scripts test-unit-js" 19 }, 20 "author": "ECOMMPAY", 21 "license": "GPL-2.0-only", 22 "devDependencies": { 23 "@types/applepayjs": "^14.0.9", 24 "@wordpress/scripts": "^26.19.0" 25 }, 26 "dependencies": { 27 "@wordpress/element": "^5.27.0", 28 "@wordpress/html-entities": "^3.50.0", 29 "use-debounce": "^10.0.0" 30 }, 31 "resolutions": { 32 "@babel/runtime": "^7.26.10", 33 "@babel/core": "^7.26.10" 34 } 31 35 } -
ecommpay-payments/trunk/readme.txt
r3440800 r3449399 3 3 Tags: card payments, apple pay, google pay, open banking, subscriptions, paypal, humm, ideal, klarna, payment gateway, woocommerce 4 4 Requires at least: 6.2 5 Tested up to: 6. 76 Stable tag: 4.2. 15 Tested up to: 6.9 6 Stable tag: 4.2.2 7 7 License: GPLv2 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html -
ecommpay-payments/trunk/views/admin/sections/html-buttons.php
r3218798 r3449399 18 18 19 19 <button class="button button-primary <?= esc_attr( EcpModuleAdminUI::ACTION_BUTTON_CLASS ); ?>" 20 data-ecp-action=" capture"20 data-ecp-action="<?= esc_attr( EcpModuleAdminUI::ACTION_CAPTURE ); ?>" 21 21 data-order-id="<?= esc_attr( $order->get_id() ); ?>"> 22 22 <?= ecpL( 'Capture', 'Capture payment from dashboard' ) . ' ' . $order->get_formatted_order_total(); ?> 23 23 </button> 24 24 <button class="button button-secondary <?= esc_attr( EcpModuleAdminUI::ACTION_BUTTON_CLASS ); ?>" 25 data-ecp-action=" cancel"25 data-ecp-action="<?= esc_attr( EcpModuleAdminUI::ACTION_CANCEL ); ?>" 26 26 data-order-id="<?= esc_attr( $order->get_id() ); ?>"> 27 27 <?= esc_html( ecpL( 'Cancel payment', 'Cancel payment from dashboard' ) ); ?> … … 31 31 const refundButtonSelector = '<?= esc_js( EcpModuleAdminUI::WP_REFUND_BUTTON_SELECTOR ); ?>' 32 32 const actionButtonClass = '<?= esc_js( EcpModuleAdminUI::ACTION_BUTTON_CLASS ); ?>' 33 const ACTION_CAPTURE = '<?= esc_js( EcpModuleAdminUI::ACTION_CAPTURE ); ?>' 34 const ACTION_CANCEL = '<?= esc_js( EcpModuleAdminUI::ACTION_CANCEL ); ?>' 33 35 34 36 // Refund button handler … … 45 47 const action = $(this).data('ecp-action') 46 48 const confirmMessages = { 47 capture: 'Are you sure you wish to process this capture? This action cannot be undone.',48 cancel: 'Are you sure you wish to process this cancel? This action cannot be undone.',49 [ACTION_CAPTURE]: 'Are you sure you wish to process this capture? This action cannot be undone.', 50 [ACTION_CANCEL]: 'Are you sure you wish to process this cancel? This action cannot be undone.', 49 51 } 50 52 -
ecommpay-payments/trunk/views/html-meta-box-error.php
r3218798 r3449399 3 3 * Template for ECOMMPAY Payment meta box error message. 4 4 */ 5 6 use common\modules\EcpModuleAdminUI; 5 7 6 8 ?> … … 20 22 <li class="wide"> 21 23 <strong class="ecp-amount"></strong> 22 <button type="button" data-action="refresh" class="button refresh-info button-secondary" name="save" 24 <button type="button" data-action="<?php echo esc_attr( EcpModuleAdminUI::ACTION_REFRESH ); ?>" 25 class="button refresh-info button-secondary" name="save" 23 26 value="Refresh">Refresh 24 27 </button> -
ecommpay-payments/trunk/views/html-meta-box-payment-info.php
r3218798 r3449399 18 18 use common\helpers\EcpGatewayPaymentStatus; 19 19 use common\models\EcpGatewayInfoPayment; 20 use common\modules\EcpModuleAdminUI; 20 21 21 22 ?> … … 119 120 <?php echo wp_kses_post( $amount ); ?> 120 121 </strong> 121 <button type="button" data-action="refresh" class="button refresh-info button-secondary" name="save" 122 <button type="button" data-action="<?php echo esc_attr( EcpModuleAdminUI::ACTION_REFRESH ); ?>" 123 class="button refresh-info button-secondary" name="save" 122 124 value="Refresh">Refresh 123 125 </button>
Note: See TracChangeset
for help on using the changeset viewer.