Plugin Directory

Changeset 3462779


Ignore:
Timestamp:
02/16/2026 05:47:51 PM (3 weeks ago)
Author:
patternsinthecloud
Message:

version 2.12.0

Location:
autoship-cloud/trunk
Files:
18 added
19 edited

Legend:

Unmodified
Added
Removed
  • autoship-cloud/trunk/app/Core/FeatureManagerInterface.php

    r3453277 r3462779  
    2525     */
    2626    public function is_enabled( string $feature ): bool;
     27
     28    /**
     29     * Gets the value indicating if a payment gateway feature is enabled.
     30     *
     31     * This method checks both the main 'payments' feature flag and the
     32     * individual gateway feature flag.
     33     *
     34     * @param string $gateway_id The payment gateway ID (e.g., 'stripe', 'braintree_credit_card').
     35     *
     36     * @return bool
     37     */
     38    public function is_payment_gateway_enabled( string $gateway_id ): bool;
    2739}
  • autoship-cloud/trunk/app/Core/Implementations/WordPressFeatureManager.php

    r3453277 r3462779  
    1010
    1111use Autoship\Core\FeatureManagerInterface;
     12use Autoship\Services\Logging\Logger;
    1213
    1314/**
    1415 * WordPress implementation of the feature manager.
     16 *
     17 * For payment gateway flags (payments_gateway_*), this implementation supports
     18 * a two-tier priority chain:
     19 *
     20 * 1. QPilot remote flags (authoritative when available)
     21 * 2. Hardcoded defaults (fallback) - the $features array below
    1522 *
    1623 * @package Autoship
     
    1825 */
    1926class WordPressFeatureManager implements FeatureManagerInterface {
     27
     28    /**
     29     * Prefix identifying payment gateway feature flags.
     30     *
     31     * @var string
     32     */
     33    const PAYMENT_GATEWAY_PREFIX = 'payments_gateway_';
    2034
    2135    /**
     
    4054        'quicklinks'                           => true,
    4155        'qmc_components'                       => true,
     56        'payments'                             => true,
     57
     58        // Per-gateway payment feature flags.
     59        'payments_gateway_stripe'              => false,  // Stripe enabled.
     60        'payments_gateway_stripe_sepa'         => false,  // Stripe SEPA enabled.
     61        'payments_gateway_authorize_net'       => false,  // Authorize.Net enabled.
     62        'payments_gateway_braintree'           => false, // Braintree disabled by default.
     63        'payments_gateway_square'              => false, // Square enabled.
     64        'payments_gateway_paypal'              => false, // PayPal enabled.
     65        'payments_gateway_nmi'                 => false, // NMI disabled by default.
     66        'payments_gateway_cybersource'         => false, // CyberSource disabled by default.
     67        'payments_gateway_trustcommerce'       => false, // TrustCommerce disabled by default.
     68        'payments_gateway_sage'                => false, // Sage disabled by default.
     69        'payments_gateway_checkoutcom'         => false, // Checkout.com disabled by default.
     70        'payments_gateway_opayo'               => false, // Opayo disabled by default.
     71        'payments_gateway_paya'                => false, // Paya disabled by default.
    4272    );
    4373
    4474    /**
     75     * Mapping of gateway IDs to feature keys.
     76     *
     77     * Multiple WooCommerce gateway IDs can map to a single feature flag.
     78     *
     79     * @var array<string, string>
     80     */
     81    protected array $gateway_feature_map = array(
     82        'stripe'                        => 'stripe',
     83        'stripe_sepa'                   => 'stripe_sepa',
     84        'authorize_net_cim_credit_card' => 'authorize_net',
     85        'braintree_credit_card'         => 'braintree',
     86        'braintree_paypal'              => 'braintree',
     87        'square_credit_card'            => 'square',
     88        'square_cash_app_pay'           => 'square',
     89        'paypal'                        => 'paypal',
     90        'ppcp-gateway'                  => 'paypal',
     91        'nmi_gateway_woocommerce'       => 'nmi',
     92        'cybersource_credit_card'       => 'cybersource',
     93        'trustcommerce'                 => 'trustcommerce',
     94        'sagepaymentsusaapi'            => 'sage',
     95        'checkoutcom_card_payment'      => 'checkoutcom',
     96        'opayo_direct'                  => 'opayo',
     97        'paya_gateway_credit_card'      => 'paya',
     98    );
     99
     100    /**
     101     * A callable that returns an array of remotely enabled feature flag names,
     102     * or null if remote flags are unavailable.
     103     *
     104     * @var callable|null
     105     */
     106    private $remote_flags_provider = null;
     107
     108    /**
     109     * Cached result from the remote flags provider.
     110     *
     111     * @var array<string>|null
     112     */
     113    private ?array $remote_flags = null;
     114
     115    /**
     116     * Whether remote flags have already been resolved.
     117     *
     118     * @var bool
     119     */
     120    private bool $remote_flags_resolved = false;
     121
     122    /**
     123     * Set the remote flags provider.
     124     *
     125     * The provider is a callable that returns an array of enabled feature flag
     126     * names from QPilot, or null if remote flags are unavailable.
     127     *
     128     * @param callable $provider A callable returning array<string>|null.
     129     *
     130     * @return void
     131     */
     132    public function set_remote_flags_provider( callable $provider ): void {
     133        $this->remote_flags_provider = $provider;
     134        $this->remote_flags          = null;
     135        $this->remote_flags_resolved = false;
     136    }
     137
     138    /**
    45139     * Gets the value indicating if the feature is enabled or not.
    46140     *
     141     * For payment gateway flags (payments_gateway_*), the priority chain is:
     142     * 1. QPilot remote flags (authoritative when available)
     143     * 2. Hardcoded default in $features array
     144     *
     145     * For all other flags, returns the hardcoded default.
     146     *
    47147     * @param string $feature The name of the feature.
    48148     *
     
    50150     */
    51151    public function is_enabled( string $feature ): bool {
     152        // For payment gateway flags, check QPilot remote flags first.
     153        if ( $this->is_payment_gateway_feature( $feature ) ) {
     154            $remote_flags = $this->resolve_remote_flags();
     155            Logger::log( 'Feature Manager', "Checking feature '{$feature}' - Remote flags: " . ( is_array( $remote_flags ) ? implode( ', ', $remote_flags ) : 'null' ) );
     156            if ( null !== $remote_flags ) {
     157                return in_array( $feature, $remote_flags, true );
     158            }
     159        }
     160
     161        // Hardcoded default (for all flags, and fallback for payment flags).
    52162        return ! empty( $this->features[ $feature ] );
    53163    }
     164
     165    /**
     166     * Gets the value indicating if a payment gateway feature is enabled.
     167     *
     168     * This method checks both the main 'payments' feature flag and the
     169     * individual gateway feature flag.
     170     *
     171     * @param string $gateway_id The payment gateway ID (e.g., 'stripe', 'braintree_credit_card').
     172     *
     173     * @return bool
     174     */
     175    public function is_payment_gateway_enabled( string $gateway_id ): bool {
     176        // First check if the main payments module is enabled.
     177        if ( ! $this->is_enabled( 'payments' ) ) {
     178            return false;
     179        }
     180
     181        // Normalize gateway ID to feature key and check.
     182        $feature_key = self::PAYMENT_GATEWAY_PREFIX . $this->normalize_gateway_id( $gateway_id );
     183
     184        return $this->is_enabled( $feature_key );
     185    }
     186
     187    /**
     188     * Normalize a gateway ID to its feature key.
     189     *
     190     * Maps various WooCommerce gateway IDs to their corresponding feature keys.
     191     * For example, 'braintree_credit_card' and 'braintree_paypal' both map to 'braintree'.
     192     *
     193     * @param string $gateway_id The gateway ID.
     194     *
     195     * @return string The normalized feature key.
     196     */
     197    protected function normalize_gateway_id( string $gateway_id ): string {
     198        // Check if we have a specific mapping.
     199        if ( isset( $this->gateway_feature_map[ $gateway_id ] ) ) {
     200            return $this->gateway_feature_map[ $gateway_id ];
     201        }
     202
     203        // Fall back to sanitizing the gateway ID.
     204        return str_replace( array( '-', ' ' ), '_', strtolower( $gateway_id ) );
     205    }
     206
     207    /**
     208     * Check if a feature name is a payment gateway feature.
     209     *
     210     * @param string $feature The feature name.
     211     *
     212     * @return bool True if the feature is a payment gateway flag.
     213     */
     214    private function is_payment_gateway_feature( string $feature ): bool {
     215        return 0 === strpos( $feature, self::PAYMENT_GATEWAY_PREFIX );
     216    }
     217
     218    /**
     219     * Lazily resolve remote feature flags from the provider.
     220     *
     221     * The provider is only called once per request. Subsequent calls
     222     * return the cached result.
     223     *
     224     * @return array<string>|null The list of enabled flag names, or null if unavailable.
     225     */
     226    private function resolve_remote_flags(): ?array {
     227        if ( $this->remote_flags_resolved ) {
     228            return $this->remote_flags;
     229        }
     230
     231        $this->remote_flags_resolved = true;
     232
     233        if ( null === $this->remote_flags_provider ) {
     234            return null;
     235        }
     236
     237        $this->remote_flags = call_user_func( $this->remote_flags_provider );
     238
     239        return $this->remote_flags;
     240    }
    54241}
  • autoship-cloud/trunk/app/Core/Plugin.php

    r3453277 r3462779  
    1818use Autoship\Core\Implementations\WordPressOAuthService;
    1919use Autoship\Core\Implementations\WordPressSettings;
     20use Autoship\Services\QPilot\QPilotServiceFactory;
    2021use Autoship\Modules\Nextime\NextimeModule;
     22use Autoship\Modules\Payments\PaymentsModule;
    2123use Autoship\Modules\Quicklaunch\QuicklaunchModule;
    2224use Autoship\Modules\QuickLinks\QuickLinksModule;
     
    2426use Autoship\Services\Logging\AutoshipLogger;
    2527use Autoship\Services\Logging\Implementations\WordPressLoggingSettings;
     28use Autoship\Services\Logging\Logger;
    2629use Autoship\Services\Logging\LoggerInterface;
    2730use Autoship\Services\Logging\LoggingSettingsInterface;
     
    7477     * @throws Exception If the boot process fails.
    7578     */
    76     public function boot():void {
     79    public function boot(): void {
    7780        $this->register_core_services();
    7881        $this->initialize_logging();
     
    9093            FeatureManagerInterface::class,
    9194            function () {
    92                 return new WordPressFeatureManager();
     95                $manager = new WordPressFeatureManager();
     96                $manager->set_remote_flags_provider( array( $this, 'fetch_qpilot_feature_flags' ) );
     97
     98                return $manager;
    9399            }
    94100        );
     
    220226            $this->module_manager->register_module( new QuickLinksModule() );
    221227        }
     228
     229        if ( $features->is_enabled( 'payments' ) ) {
     230            $this->module_manager->register_module( new PaymentsModule() );
     231        }
    222232    }
    223233
     
    227237     * @return void
    228238     */
    229     protected function boot_modules():void {
     239    protected function boot_modules(): void {
    230240        $this->module_manager->boot();
    231241    }
     
    247257    protected function uninstall_modules(): void {
    248258        $this->module_manager->uninstall();
     259    }
     260
     261    /**
     262     * Fetch feature flags from the QPilot API.
     263     *
     264     * Retrieves both global and site-specific flags, merges them,
     265     * and caches the result in a WordPress transient for 1 hour.
     266     *
     267     * @return array<string>|null List of enabled flag names, or null on failure.
     268     */
     269    public function fetch_qpilot_feature_flags(): ?array {
     270        $cache_key = 'autoship_qpilot_feature_flags';
     271        $cached    = get_transient( $cache_key );
     272
     273        if ( false !== $cached ) {
     274            Logger::debug( 'Feature Flags', 'Returning cached remote flags.' );
     275            return $cached;
     276        }
     277
     278        $token   = get_option( 'autoship_token_auth', '' );
     279        $site_id = (int) get_option( 'autoship_site_id', 0 );
     280
     281        if ( empty( $token ) || $site_id <= 0 ) {
     282            Logger::debug( 'Feature Flags', 'No QPilot credentials configured. Using hardcoded defaults.' );
     283            return null;
     284        }
     285
     286        try {
     287            $environment = $this->container->get( EnvironmentInterface::class );
     288            $client      = QPilotServiceFactory::create(
     289                $environment->get_api_url(),
     290                $token,
     291                $site_id
     292            );
     293        } catch ( \Exception $e ) {
     294            Logger::error( 'Feature Flags', 'Failed to initialize QPilot client: ' . $e->getMessage() );
     295            return null;
     296        }
     297
     298        // Fetch global and site-specific flags independently so one
     299        // failure does not prevent the other from being used.
     300        $global_flags   = array();
     301        $global_success = false;
     302        $site_flags     = array();
     303        $site_success   = false;
     304
     305        try {
     306            $global_flags   = $client->get_feature_flags();
     307            $global_success = true;
     308        } catch ( \Exception $e ) {
     309            Logger::error( 'Feature Flags', 'Failed to fetch global feature flags: ' . $e->getMessage() );
     310        }
     311
     312        try {
     313            $site_flags   = $client->get_site_feature_flags();
     314            $site_success = true;
     315        } catch ( \Exception $e ) {
     316            Logger::error( 'Feature Flags', 'Failed to fetch site feature flags: ' . $e->getMessage() );
     317        }
     318
     319        // If both calls failed, return null to fall back to hardcoded defaults.
     320        if ( ! $global_success && ! $site_success ) {
     321            Logger::error( 'Feature Flags', 'Both QPilot feature flag endpoints failed. Using hardcoded defaults.' );
     322            return null;
     323        }
     324
     325        $all_flags = array_values( array_unique( array_merge( $global_flags, $site_flags ) ) );
     326
     327        set_transient( $cache_key, $all_flags, 5 * MINUTE_IN_SECONDS );
     328
     329        Logger::debug( 'Feature Flags', 'Resolved remote flags: ' . implode( ', ', $all_flags ) );
     330
     331        return $all_flags;
    249332    }
    250333
  • autoship-cloud/trunk/app/Domain/PaymentIntegrationFactory.php

    r3387754 r3462779  
    5151                case 'stripe':
    5252                case 'stripe_sepa':
     53                case 'fkwcs_stripe':
     54                case 'fkwcs_stripe_ach':
     55                case 'link':
    5356                    return StripePaymentIntegration::build( $gateway_id, $settings );
    5457                case 'nmi_gateway_woocommerce_credit_card':
     
    6568                    return CyberSourceV2PaymentIntegration::build( $gateway_id, $settings );
    6669                case 'square_credit_card':
     70                case 'square_cash_app_pay':
    6771                    return SquarePaymentIntegration::build( $gateway_id, $settings );
    6872                case 'ppec_paypal':
     
    7680                case 'sagepaydirect':
    7781                    return SagePaymentIntegration::build( $gateway_id, $settings );
    78                 case 'fkwcs_stripe':
    79                 case 'fkwcs_stripe_ach':
    80                     return FunnelKitPaymentIntegration::build( $gateway_id, $settings );
    8182                case 'airwallex_card':
    8283                    return AirwallexPaymentIntegration::build( $gateway_id, $settings );
  • autoship-cloud/trunk/app/Domain/PaymentIntegrations/AuthorizeNetPaymentIntegration.php

    r3303202 r3462779  
    99namespace Autoship\Domain\PaymentIntegrations;
    1010
    11 use Autoship\Domain\PaymentIntegration;
     11use Autoship\Domain\AbstractPaymentGateway;
     12use Autoship\Services\Logging\Logger;
    1213use Autoship\Domain\PaymentMethodType;
    1314use Exception;
     15use QPilotPaymentData;
     16use WC_Order;
     17use WC_Payment_Token;
    1418
    1519/**
     
    1923 * @since 2.8.7
    2024 */
    21 class AuthorizeNetPaymentIntegration extends PaymentIntegration {
     25class AuthorizeNetPaymentIntegration extends AbstractPaymentGateway {
    2226
    2327    /**
     
    2933        'authorize_net_cim_credit_card',
    3034    );
     35
     36    /**
     37     * Initializes the payment integration.
     38     *
     39     * @return void
     40     */
     41    public function initialize(): void {
     42        Logger::log( 'Authorize.Net Payment Integration', 'Initializing Authorize.Net payment integration.' );
     43    }
    3144
    3245    /**
     
    8699        return true;
    87100    }
     101
     102    /**
     103     * Handles the SkyVerge add payment method transaction result filter.
     104     * Adds the payment method to QPilot when a transaction is approved.
     105     *
     106     * @param array  $result The transaction result.
     107     * @param object $response The gateway API response.
     108     * @param object $order The order object.
     109     * @param object $client The gateway client instance.
     110     * @return array The transaction result.
     111     */
     112    public function add_transaction_payment_method( $result, $response, $order, $client ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
     113        $types = autoship_standard_gateway_id_types();
     114
     115        if ( $response->transaction_approved() && ! isset( $types['authorize_net_cim_credit_card'] ) ) {
     116            autoship_add_non_wc_token_payment_method( $response, $order, 'authorize_net_cim_credit_card' );
     117        }
     118
     119        return $result;
     120    }
     121
     122    /**
     123     * Get order payment data for QPilot.
     124     *
     125     * @param int      $order_id The order ID.
     126     * @param WC_Order $order The order object.
     127     *
     128     * @return ?QPilotPaymentData The payment data.
     129     */
     130    public function get_order_payment_data( int $order_id, WC_Order $order ): ?QPilotPaymentData {
     131        $token_string = $order->get_meta( '_wc_authorize_net_cim_credit_card_payment_token' );
     132        $customer_id  = $order->get_meta( '_wc_authorize_net_cim_credit_card_customer_id' );
     133
     134        if ( ! empty( $token_string ) && ! empty( $customer_id ) ) {
     135            $payment_data = new QPilotPaymentData();
     136            $card_type    = $order->get_meta( '_wc_authorize_net_cim_credit_card_card_type' );
     137            $last_four    = $order->get_meta( '_wc_authorize_net_cim_credit_card_account_four' );
     138
     139            // Authnet Stores Expiration in YY-MM format so we need to adjust for Autoship
     140            // Expiration in format 23-02 should be 0223.
     141            $expiry_date = $order->get_meta( '_wc_authorize_net_cim_credit_card_card_expiry_date' );
     142            $expiration  = explode( '-', $expiry_date );
     143
     144            $payment_data->description         = isset( $expiration[1] ) ? sprintf( '%s ending in %s (expires %s)', ucfirst( $card_type ), $last_four, $expiration[1] . '/' . $expiration[0] ) : sprintf( '%s ending in %s', ucfirst( $card_type ), $last_four );
     145            $payment_data->type                = 'AuthorizeNet';
     146            $payment_data->gateway_payment_id  = $token_string;
     147            $payment_data->gateway_customer_id = $customer_id;
     148            $payment_data->last_four           = $last_four;
     149            $payment_data->expiration          = isset( $expiration[1] ) ? $expiration[1] . $expiration[0] : null;
     150
     151            return $payment_data;
     152        }
     153
     154        return null;
     155    }
     156
     157    /**
     158     * Add payment method data for QPilot.
     159     *
     160     * @param array            $payment_method_data The payment method data.
     161     * @param string           $type The payment method type.
     162     * @param WC_Payment_Token $token The payment token.
     163     * @return array The modified payment method data.
     164     */
     165    public function add_payment_method( array $payment_method_data, string $type, WC_Payment_Token $token ): array {
     166        // Apply the test filters.
     167        $test_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_test_ext', '_test', 'authorize_net_cim_credit_card' );
     168
     169        // ex. _wc_authorize_net_cim_credit_card_payment_tokens_test or _wc_authorize_net_cim_credit_card_payment_tokens.
     170        $meta_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_ext', '', $test_ext, 'authorize_net_cim_credit_card' );
     171
     172        // Authnet uses the customer id from user meta as Gateway Customer ID.
     173        $user_id                                  = $token->get_user_id();
     174        $payment_method_data['gatewayCustomerId'] = get_user_meta( $user_id, 'wc_authorize_net_cim_customer_profile_id' . $meta_ext, true );
     175
     176        return $payment_method_data;
     177    }
     178
     179    /* i. authorize.net Payment Method - Functions moved to src/payments-authorize-net.php */
     180
     181    /**
     182     * Adds the Skyverge Payment Method to QPilot After Checkout
     183     * Fired when a payment is processed for an order.
     184     *
     185     * @param WC_Order $order The WC Order object.
     186     * @param object   $gateway The payment gateway instance.
     187     */
     188    public function add_skyverge_payment_method( WC_Order $order, object $gateway ) {
     189
     190        // Get the types to see if this is legacy or new.
     191        $types = autoship_standard_gateway_id_types();
     192
     193        if ( ! isset( $types[ $gateway->id ] ) ) {
     194            return;
     195        }
     196
     197        // Retrieve the Token based off the token id & run it through the partial token filter.
     198        $token = autoship_get_related_tokenized_id( $order->payment->token, true );
     199
     200        // Check for failed token retrieval.
     201        if ( is_null( $token ) || empty( $token ) || ! $token ) {
     202            return;
     203        }
     204
     205        $token = autoship_tokenize_non_fully_implemented_token_classes( $token );
     206
     207        // Upsert the Token to the API.
     208        autoship_add_general_payment_method( $token );
     209    }
     210
     211    /**
     212     * Fires after a new Skyverge payment method is added by a customer.
     213     *
     214     * @param string $token new token.
     215     */
     216    public function add_my_account_skyverge_payment_method( string $token ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
     217
     218        if ( is_checkout() ) {
     219            return;
     220        }
     221
     222        // Retrieve the Token based off the token id & run it through the partial token filter.
     223        $token = autoship_get_related_tokenized_id( $token, true );
     224
     225        // Check for failed token retrieval.
     226        if ( is_null( $token ) || empty( $token ) || ! $token ) {
     227            return;
     228        }
     229
     230        $token = autoship_tokenize_non_fully_implemented_token_classes( $token );
     231
     232        autoship_add_general_payment_method( $token );
     233    }
     234
     235    /**
     236     * Authorize.Net Gateway: Fires additional autoship actions after a payment method is saved.
     237     *
     238     * @param string $token_id new token ID.
     239     */
     240    public function after_save_authorize_net_payment_method_notice( string $token_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
     241
     242        // Get the types to see if this is legacy or new Authnet.
     243        $types = autoship_standard_gateway_id_types();
     244
     245        // Only Display Notice if this is the legacy Authnet Gateway.
     246        if ( ! isset( $types['authorize_net_cim_credit_card'] ) ) {
     247            autoship_after_save_payment_method_autoship_action_notice( $token_id, 'authorize_net_cim_credit_card', '' );
     248        }
     249    }
     250
     251    /**
     252     * Delete payment method validation.
     253     *
     254     * @param bool             $valid Current validation status.
     255     * @param string           $type The payment method type.
     256     * @param WC_Payment_Token $token The payment token.
     257     * @param object           $method The QPilot payment method.
     258     * @return bool Whether the deletion is valid.
     259     */
     260    public function delete_payment_method( bool $valid, string $type, WC_Payment_Token $token, object $method ): bool {
     261        if ( 'AuthorizeNet' === $type ) {
     262            // Apply the test filters.
     263            $test_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_test_ext', '_test', 'authorize_net_cim_credit_card' );
     264            $meta_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_ext', '', $test_ext, 'authorize_net_cim_credit_card' );
     265
     266            $user_id     = $token->get_user_id();
     267            $customer_id = get_user_meta( $user_id, 'wc_authorize_net_cim_customer_profile_id' . $meta_ext, true );
     268
     269            return ( $method->gatewayCustomerId === $customer_id && $method->gatewayPaymentId === $token->get_token() ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     270        }
     271
     272        return $valid;
     273    }
     274
     275    /**
     276     * Add metadata to payment.
     277     *
     278     * @param array    $payment_meta The payment metadata.
     279     * @param WC_Order $order The order object.
     280     * @param array    $payment_method The payment method.
     281     * @return array The updated payment metadata.
     282     */
     283    public function add_metadata( array $payment_meta, WC_Order $order, array $payment_method ): array {
     284        $expiration = $payment_method['expiration'] ?? '';
     285        $exp_year   = ! empty( $expiration ) && ( strlen( $expiration ) > 2 ) ? substr( $expiration, - 2 ) : $expiration;
     286        $exp_month  = ! empty( $expiration ) && ( strlen( $expiration ) > 2 ) ? substr( $expiration, 0, 2 ) : $expiration;
     287
     288        $metadata = array(
     289            '_wc_authorize_net_cim_credit_card_trans_id' => $payment_meta['transId'],
     290            '_wc_authorize_net_cim_credit_card_authorization_code' => $payment_meta['authCode'],
     291            '_wc_authorize_net_cim_credit_card_customer_id' => $payment_method['gatewayCustomerId'],
     292            '_wc_authorize_net_cim_credit_card_account_four' => $payment_method['lastFourDigits'],
     293            '_wc_authorize_net_cim_credit_card_card_expiry_date' => $exp_year . '-' . $exp_month,
     294            '_wc_authorize_net_cim_credit_card_charge_captured' => 'yes',
     295        );
     296
     297        $order->set_transaction_id( $payment_meta['transId'] );
     298
     299        foreach ( $metadata as $key => $value ) {
     300            $order->update_meta_data( $key, $value );
     301        }
     302
     303        $order->save();
     304
     305        return $payment_meta;
     306    }
     307
     308    /**
     309     * Display apply to all orders button.
     310     *
     311     * @param array $list_item The list item data.
     312     * @param mixed $payment_token The payment token (WC_Payment_Token or SkyVerge payment profile).
     313     * @return string The button HTML.
     314     */
     315    public function display_apply_to_all_orders_button( array $list_item, $payment_token ): array {
     316        return autoship_display_apply_payment_method_to_all_scheduled_orders_skyverge_btn( $list_item, $payment_token, null, 'authorize_net_cim_credit_card' );
     317    }
    88318}
  • autoship-cloud/trunk/app/Domain/PaymentIntegrations/PayPalPaymentIntegration.php

    r3352156 r3462779  
    99namespace Autoship\Domain\PaymentIntegrations;
    1010
    11 use Autoship\Domain\PaymentIntegration;
     11use Autoship\Domain\AbstractPaymentGateway;
     12use Autoship\Services\Logging\Logger;
    1213use Autoship\Domain\PaymentMethodType;
    1314use Exception;
     15use WC_Payment_Tokens;
     16use QPilotPaymentData;
     17use WC_Order;
     18use RuntimeException;
     19use WC_Payment_Token;
    1420
    1521/**
     
    1925 * @since 2.8.7
    2026 */
    21 class PayPalPaymentIntegration extends PaymentIntegration {
     27class PayPalPaymentIntegration extends AbstractPaymentGateway {
     28
    2229    /**
    2330     * The allowed payment gateways for this integration.
     
    2936        'ppec_paypal',
    3037    );
     38
     39    /**
     40     * Initializes the payment integration.
     41     *
     42     * @return void
     43     */
     44    public function initialize(): void {
     45        Logger::log( 'PayPal Payment Integration', 'Initializing PayPal payment integration.' );
     46    }
    3147
    3248    /**
     
    5066        $integration->set_authorize_only( false );
    5167
    52         $environment = 'sandbox' === $settings['environment'] ? 'test' : 'live';
     68        $environment = 'sandbox' === ( $settings['environment'] ?? '' ) ? 'test' : 'live';
    5369
    5470        if ( 'test' === $environment ) {
     
    8197        }
    8298
    83         // NMI should contain the username, password and signature.
     99        // PayPal requires the username, password and signature.
    84100        if ( empty( $this->get_api_account() ) || empty( $this->get_api_key_1() ) || empty( $this->get_api_key_2() ) ) {
    85101            return false;
     
    88104        return true;
    89105    }
     106
     107    /**
     108     * Get order payment data for QPilot.
     109     *
     110     * @param int      $order_id The order ID.
     111     * @param WC_Order $order The order object.
     112     *
     113     * @return ?QPilotPaymentData The payment data.
     114     */
     115    public function get_order_payment_data( int $order_id, WC_Order $order ): ?QPilotPaymentData {
     116        // PayPal Express Checkout stores the billing agreement ID.
     117        $payment_agreement_id = $order->get_meta( '_ppec_billing_agreement_id' );
     118
     119        if ( ! empty( $payment_agreement_id ) ) {
     120            $payment_data                     = new QPilotPaymentData();
     121            $card_type                        = 'PayPal';
     122            $last_four                        = substr( $payment_agreement_id, -4 );
     123            $payment_data->description        = sprintf( '%s ending in %s', ucfirst( $card_type ), $last_four );
     124            $payment_data->type               = 'PayPal';
     125            $payment_data->gateway_payment_id = $payment_agreement_id;
     126
     127            return $payment_data;
     128        }
     129
     130        $gateway_payment_token = array();
     131        $gateway_customer_id   = null;
     132        $gateway_payment_token = null;
     133        $user_id               = $order->get_user_id();
     134        $wc_tokens             = WC_Payment_Tokens::get_customer_tokens( $user_id, 'ppcp-gateway' );
     135
     136        if ( $wc_tokens ) {
     137            $gateway_customer_id = get_user_meta( $user_id, '_ppcp_target_customer_id', true );
     138            if ( ! $gateway_customer_id ) {
     139                $gateway_customer_id = get_user_meta( $user_id, 'ppcp_customer_id', true );
     140            }
     141
     142            $customer_tokens = autoship_get_paypal_payments_tokens_for_customer( $gateway_customer_id );
     143
     144            $customer_token_ids = array();
     145            foreach ( $customer_tokens as $customer_token ) {
     146                $customer_token_ids[] = $customer_token['id'];
     147            }
     148
     149            foreach ( $wc_tokens as $token ) {
     150                if ( ! in_array( $token->get_token(), $customer_token_ids, true ) ) {
     151                    continue;
     152                }
     153                $gateway_payment_token = $token->get_token();
     154                break;
     155            }
     156        } else {
     157            // Get payment data from PayPal if not found in WP.
     158            $order_id_value = $order->get_meta( \WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway::ORDER_ID_META_KEY );
     159            $order_id       = $order_id_value ? $order_id_value : wc_clean( wp_unslash( $_POST['paypal_order_id'] ?? '' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     160            $order_endpoint = \WooCommerce\PayPalCommerce\PPCP::container()->get( 'api.endpoint.order' );
     161
     162            if ( is_string( $order_id ) && $order_id ) {
     163                try {
     164                    $ordder = $order_endpoint->order( $order_id );
     165                } catch ( RuntimeException ) {
     166                    throw new Exception( esc_html( __( 'Could not retrieve PayPal order:' ) ), 'woocommerce-paypal-payments' );
     167                }
     168
     169                $payment_source = $ordder->payment_source();
     170                assert( $payment_source instanceof \WooCommerce\PayPalCommerce\ApiClient\Entity\PaymentSource );
     171                $payment_vault_attributes = $payment_source->properties()->attributes->vault ?? null;
     172                if ( $payment_vault_attributes ) {
     173                    $gateway_customer_id   = $payment_vault_attributes->customer->id ?? '';
     174                    $gateway_payment_token = $payment_vault_attributes->id ?? '';
     175                }
     176            }
     177        }
     178
     179        if ( ! empty( $gateway_payment_token ) ) {
     180            $payment_data                       = new QPilotPaymentData();
     181            $payment_data->description          = 'PayPal Payments';
     182            $payment_data->type                 = 'PayPalV3';
     183            $payment_data->gateway_payment_id   = $gateway_payment_token;
     184            $payment_data->gateway_customer_id  = $gateway_customer_id;
     185            $payment_data->gateway_payment_type = 25;
     186
     187            return $payment_data;
     188        }
     189
     190        return null;
     191    }
     192
     193    /**
     194     * Add payment method data for QPilot.
     195     *
     196     * @param array            $payment_method_data The payment method data.
     197     * @param string           $type The payment method type.
     198     * @param WC_Payment_Token $token The payment token.
     199     * @return array The modified payment method data.
     200     */
     201    public function add_payment_method( array $payment_method_data, string $type, WC_Payment_Token $token ): array {
     202        // PayPal Express Checkout doesn't use a customer ID in the same way as credit card gateways.
     203        // The billing agreement ID serves as both the payment token and identifier.
     204        // PayPal Payments adjustments.
     205        if ( 'PayPalV3' === $type ) {
     206            $gateway        = $token->get_gateway_id();
     207            $wc_customer_id = $token->get_user_id();
     208
     209            if ( 'ppcp-credit-card-gateway' === $gateway ) {
     210                $payment_method_data['gatewayPaymentType'] = 26;
     211            }
     212
     213            if ( 'ppcp-gateway' === $gateway ) {
     214                $payment_method_data['gatewayPaymentType'] = 25;
     215                $payment_method_data['description']        = 'PayPal Payments';
     216            }
     217
     218            $gateway_customer_id = get_user_meta( $wc_customer_id, '_ppcp_target_customer_id', true );
     219            if ( ! $gateway_customer_id ) {
     220                $gateway_customer_id = get_user_meta( $wc_customer_id, 'ppcp_customer_id', true );
     221            }
     222
     223            if ( $gateway_customer_id ) {
     224                $payment_method_data['gatewayCustomerId'] = $gateway_customer_id;
     225            }
     226        }
     227
     228        return $payment_method_data;
     229    }
     230
     231    /**
     232     * Delete payment method validation.
     233     *
     234     * @param bool             $valid Current validation status.
     235     * @param string           $type The payment method type.
     236     * @param WC_Payment_Token $token The payment token.
     237     * @param object           $method The QPilot payment method.
     238     * @return bool Whether the deletion is valid.
     239     */
     240    public function delete_payment_method( bool $valid, string $type, WC_Payment_Token $token, object $method ): bool {
     241        if ( 'PayPal' === $type ) {
     242            // PayPal uses the billing agreement ID as the gateway payment ID.
     243            return ( $method->gatewayPaymentId === $token->get_token() ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     244        }
     245
     246        if ( 'PayPalV3' === $type ) {
     247            $gateway_customer_id = get_user_meta( $token->get_user_id(), '_ppcp_target_customer_id', true );
     248            if ( ! $gateway_customer_id ) {
     249                $gateway_customer_id = get_user_meta( $token->get_user_id(), 'ppcp_customer_id', true );
     250            }
     251
     252            return ( $method->gatewayCustomerId === $gateway_customer_id && $method->gatewayPaymentId === $token->get_token() ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     253        }
     254
     255        return $valid;
     256    }
     257
     258    /**
     259     * Adds Support for the PayPal Payments (PayPal V3)
     260     *
     261     * @param array $types The current types array.
     262     *
     263     * @return array The filtered types
     264     */
     265    public function add_paypal_v3_token_support( $types ) {
     266
     267        if ( class_exists( '\WooCommerce\PayPalCommerce\PPCP' ) ) {
     268            $ppcp_settings = \WooCommerce\PayPalCommerce\PPCP::container()->get( 'wcgateway.settings' );
     269            if ( $ppcp_settings->has( 'vault_enabled' ) && $ppcp_settings->get( 'vault_enabled' ) ) {
     270                $types['ppcp-gateway'] = 'PayPalV3';
     271            }
     272
     273            if ( $ppcp_settings->has( 'vault_enabled_dcc' ) && $ppcp_settings->get( 'vault_enabled_dcc' ) ) {
     274                $types['ppcp-credit-card-gateway'] = 'PayPalV3';
     275            }
     276        }
     277
     278        return $types;
     279    }
     280
     281    /**
     282     * Add metadata to payment.
     283     *
     284     * @param array    $payment_meta The payment metadata.
     285     * @param WC_Order $order The order object.
     286     * @param array    $payment_method The payment method.
     287     * @return array The updated payment metadata.
     288     */
     289    public function add_metadata( array $payment_meta, WC_Order $order, array $payment_method ): array {
     290        // Set the status for the transaction.
     291        $status = 'Completed' === ( $payment_meta['PAYMENTSTATUS'] ?? '' ) ? $payment_meta['PAYMENTSTATUS'] : ( $payment_meta['PAYMENTSTATUS'] ?? '' ) . '_' . ( $payment_meta['PENDINGREASON'] ?? '' );
     292
     293        $metadata = array(
     294            '_woo_pp_txnData' => array(
     295                'refundable_txns' => array(
     296                    array(
     297                        'txnID'  => $payment_meta['TRANSACTIONID'] ?? '',
     298                        'amount' => $order->get_total(),
     299                        'status' => $status,
     300                    ),
     301                ),
     302            ),
     303        );
     304
     305        $order->set_transaction_id( $payment_meta['TRANSACTIONID'] ?? '' );
     306
     307        foreach ( $metadata as $key => $value ) {
     308            $order->update_meta_data( $key, $value );
     309        }
     310
     311        $order->save();
     312
     313        return $payment_meta;
     314    }
     315
     316    /**
     317     * Adds the refund metadata for PayPal v2
     318     *
     319     * @param array    $payment_meta The gateway response data.
     320     * @param WC_Order $order The wc order.
     321     * @param array    $payment_method The associated payment method data.
     322     */
     323    public function add_adjusted_gateway_metadata_ppep_paypal( $payment_meta, $order, $payment_method ) {
     324
     325        $order->update_meta_data( 'payment_token_id', $payment_method['gatewayPaymentId'] );
     326        $order->update_meta_data( '_ppcp_paypal_order_id', $payment_meta['id'] );
     327        $order->update_meta_data( '_ppcp_paypal_intent', 'CAPTURE' );
     328        $order->save();
     329
     330        /**
     331         * Check if Test Mode or Live
     332         *
     333         * @HACK Need better way to test if this is a live or test payment process.
     334         */
     335        if ( isset( $payment_meta['links'] ) && ! empty( $payment_meta['links'] ) && ( strpos( $payment_meta['links'][0]['href'], 'https://api.sandbox.paypal.com' ) !== false ) ) {
     336            $order->update_meta_data( '_ppcp_paypal_payment_mode', 'sandbox' );
     337            $order->save();
     338        }
    90339}
     340}
  • autoship-cloud/trunk/app/Domain/PaymentIntegrations/SquarePaymentIntegration.php

    r3352156 r3462779  
    99namespace Autoship\Domain\PaymentIntegrations;
    1010
    11 use Autoship\Domain\PaymentIntegration;
     11use Autoship\Domain\AbstractPaymentGateway;
    1212use Autoship\Domain\PaymentMethodType;
     13use Autoship\Services\Logging\Logger;
    1314use Exception;
     15use QPilotPaymentData;
     16use WC_Order;
     17use WC_Payment_Token;
    1418
    1519/**
     
    1923 * @since 2.8.7
    2024 */
    21 class SquarePaymentIntegration extends PaymentIntegration {
     25class SquarePaymentIntegration extends AbstractPaymentGateway {
     26
     27    /**
     28     * Indicates if integration has been initialized.
     29     *
     30     * @var bool
     31     */
     32    private static bool $initialized = false;
     33
     34    /**
     35     * Initializing.
     36     *
     37     * @return void
     38     */
     39    public function initialize(): void {
     40        if ( self::$initialized ) {
     41            return;
     42        }
     43
     44        Logger::trace( 'Square Initialize', 'Initializing filters and actions for Square' );
     45
     46        // Hook for adding the payment method.
     47        add_action( 'wc_payment_gateway_square_payment_method_added', array( $this, 'autoship_after_save_square_credit_card_payment_method_notice' ), 10, 3 );
     48
     49        // Hook for displaying apply payment method to all scheduled orders button.
     50        add_filter( 'wc_square_my_payment_methods_table_method_actions', array( $this, 'autoship_display_apply_payment_method_to_all_scheduled_orders_square_btn' ), 10, 3 );
     51
     52        // Square force save payment method filter.
     53        add_filter( 'wc_square_credit_card_force_save_source', array( self::class, 'force_save_source' ) );
     54
     55        // Square create customer request filter.
     56        add_filter( 'wc_square_create_customer_request', array( self::class, 'filter_customer_request' ), 30, 2 );
     57
     58        // Hook for adding gateway metadata during scheduled order updates.
     59        add_action( 'autoship_update_scheduled_orders_on_processing_square_gateway', array( $this, 'add_adjusted_gateway_metadata' ), 10, 3 );
     60
     61        // Hook for deleting the payment method.
     62        add_action( 'wc_payment_gateway_square_credit_card_payment_method_deleted', array( $this, 'delete_payment_method' ), 10, 2 );
     63
     64        self::$initialized = true;
     65    }
    2266
    2367    /**
     
    2872    private static array $allowed = array(
    2973        'square_credit_card',
     74        'square_cash_app_pay',
    3075    );
    3176
     
    59104            $integration->set_test_mode( true );
    60105        } else {
    61             // The option table said sandbox_token instead of production_token. Need to validate this.
    62             $integration->set_api_key_1( $options['sandbox_token'] ?? '' );
     106            $integration->set_api_key_1( $options['production_token'] ?? '' );
    63107            $integration->set_api_key_2( $options['production_location_id'] ?? '' );
    64108            $integration->set_test_mode( false );
     
    89133        return true;
    90134    }
     135
     136    /**
     137     * Get order payment data for QPilot.
     138     *
     139     * @param int      $order_id The order ID.
     140     * @param WC_Order $order The order object.
     141     *
     142     * @return ?QPilotPaymentData The payment data.
     143     */
     144    public function get_order_payment_data( int $order_id, WC_Order $order ): ?QPilotPaymentData {
     145        $payment_method = $order->get_payment_method();
     146
     147        if ( ! in_array( $payment_method, self::$allowed, true ) ) {
     148            return null;
     149        }
     150
     151        $token_id    = $order->get_meta( '_square_credit_card_payment_token' );
     152        $customer_id = $order->get_meta( '_square_customer_id' );
     153
     154        if ( ! empty( $token_id ) ) {
     155            $payment_data = new QPilotPaymentData();
     156            $card_type    = $order->get_meta( '_wc_square_credit_card_card_type' );
     157            $last_four    = $order->get_meta( '_wc_square_credit_card_account_four' );
     158            $expiry_date  = $order->get_meta( '_wc_square_credit_card_card_expiry_date' );
     159
     160            $expiry_date                       = explode( '-', $expiry_date );
     161            $expiry_date[0]                    = strlen( $expiry_date[0] ) > 2 ? substr( $expiry_date[0], - 2 ) : $expiry_date[0];
     162            $payment_data->description         = sprintf( '%s ending in %s (expires %s)', ucfirst( $card_type ), $last_four, $expiry_date[1] . '/' . $expiry_date[0] );
     163            $payment_data->type                = 'Square';
     164            $payment_data->gateway_payment_id  = $token_id;
     165            $payment_data->gateway_customer_id = $customer_id;
     166            $payment_data->last_four           = $last_four;
     167
     168            // Get Expiration in MMYY format for Qpilot.
     169            $expiration               = $expiry_date[1] . $expiry_date[0];
     170            $payment_data->expiration = $expiration;
     171
     172            return $payment_data;
     173        }
     174
     175        return null;
     176    }
     177
     178    /**
     179     * Adds the Square credit card Payment Method to QPilot
     180     * Does not use the woocommerce_payment_tokens tables
     181     *
     182     * @param array                                                    $result The result array.
     183     * @param \SV_WC_Payment_Gateway_API_Create_Payment_Token_Response $response The API response object.
     184     * @param \WC_Order                                                $order The WC Order object.
     185     * @param \SV_WC_Payment_Gateway_Direct                            $client The Direct Gateway instance.
     186     *
     187     * @return mixed
     188     * @see wc_payment_gateway_' . $this->get_id() . '_add_payment_method_transaction_result hook.
     189     *
     190     * Result: {
     191     * @type string $message notice message to render
     192     * @type bool $success true to redirect to my account, false to stay on page
     193     * }
     194     */
     195    public function add_payment_method_data( $result, $response, $order, $client ) {
     196        // Check if the transaction has been approved.
     197        if ( $response->transaction_approved() ) {
     198            autoship_add_non_wc_token_payment_method( $response, $order, 'square_credit_card' );
     199        }
     200
     201        return $result;
     202    }
     203
     204    /**
     205     * Add payment method data for QPilot.
     206     *
     207     * @param array            $payment_method_data The payment method data.
     208     * @param string           $type The payment method type.
     209     * @param WC_Payment_Token $token The payment token.
     210     * @return array The modified payment method data.
     211     */
     212    public function add_payment_method( array $payment_method_data, string $type, WC_Payment_Token $token ): array {
     213        $ext_type = 'square_credit_card';
     214
     215        // Apply the test filters.
     216        $test_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_test_ext', '_test', $ext_type );
     217        // ex. _wc_authorize_net_cim_credit_card_payment_tokens_test or _wc_authorize_net_cim_credit_card_payment_tokens.
     218        $meta_ext = apply_filters( 'autoship_payment_method_sandbox_metadata_field_ext', '', $test_ext, $ext_type );
     219
     220        $user_id                                  = $token->get_user_id();
     221        $payment_method_data['gatewayCustomerId'] = get_user_meta( $user_id, 'wc_square_customer_id' . $meta_ext, true );
     222
     223        return $payment_method_data;
     224    }
     225
     226    /**
     227     * Square_credit_card Gateway: Fires additional autoship actions after a payment method is saved.
     228     *
     229     * @param string                              $token_id new token ID.
     230     * @param int                                 $user_id user ID.
     231     * @param \SV_WC_Payment_Gateway_API_Response $response API response object.
     232     */
     233    public function autoship_after_save_square_credit_card_payment_method_notice( $token_id, $user_id, $response ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
     234        autoship_after_save_payment_method_autoship_action_notice( $token_id, 'square_credit_card', '' );
     235    }
     236
     237    /**
     238     * Square
     239     * Non-Standard skyverge framework Gateways actions
     240     * Outputs the apply action button after each payment method
     241     *
     242     * @param array                                     $list_item The list item array.
     243     * @param \SV_WC_Payment_Gateway_Payment_Token      $payment_token The payment token object.
     244     * @param \SV_WC_Payment_Gateway_My_Payment_Methods $instance The instance of the My Payment Methods class.
     245     */
     246    public function autoship_display_apply_payment_method_to_all_scheduled_orders_square_btn( $list_item, $payment_token, $instance ) {
     247        return autoship_display_apply_payment_method_to_all_scheduled_orders_skyverge_btn( $list_item, $payment_token, $instance, 'square_credit_card' );
     248    }
     249
     250    /**
     251     * Delete payment method validation.
     252     *
     253     * @param bool             $valid Current validation status.
     254     * @param string           $type The payment method type.
     255     * @param WC_Payment_Token $token The payment token.
     256     * @param object           $method The QPilot payment method.
     257     * @return bool Whether the deletion is valid.
     258     */
     259    public function delete_payment_method( bool $valid, string $type, WC_Payment_Token $token, object $method ): bool {
     260        $token_id = $token->get_token();
     261        $user_id  = $token->get_user_id();
     262
     263        return autoship_delete_non_wc_token_payment_method( $token_id, null, 'Square', $user_id );
     264    }
     265
     266    /**
     267     * Add metadata for Square.
     268     *
     269     * @param array    $payment_meta The payment metadata.
     270     * @param WC_Order $order The order object.
     271     * @return void
     272     */
     273    public function add_adjusted_gateway_metadata( array $payment_meta, WC_Order $order ): void {
     274        // Set the status for the transaction.
     275        $payment = $payment_meta['payment'];
     276
     277        // Adjust Card Expiration.
     278        $exp  = substr( $payment['card_details']['card']['exp_year'], - 2 );
     279        $exp .= '-' . $payment['card_details']['card']['exp_month'];
     280
     281        // Format the date.
     282        $date = autoship_get_datetime( $payment['created_at'] );
     283
     284        $metadata = array(
     285            '_wc_square_credit_card_square_order_id'      => $payment['order_id'],
     286            '_wc_square_credit_card_square_location_id'   => $payment['location_id'],
     287            '_wc_square_credit_card_card_type'            => strtolower( $payment['card_details']['card']['card_brand'] ),
     288            '_wc_square_credit_card_card_expiry_date'     => $exp,
     289            '_wc_square_credit_card_charge_captured'      => 'yes',
     290            '_wc_square_credit_card_authorization_amount' => $payment['approved_money']['amount'],
     291            '_wc_square_credit_card_account_four'         => $payment['card_details']['card']['last_4'],
     292            '_wc_square_credit_card_customer_id'          => $payment['customer_id'],
     293            '_wc_square_credit_card_trans_date'           => $date->format( 'Y-m-d H:i:s' ),
     294            '_wc_square_credit_card_trans_id'             => $payment['id'],
     295            '_wc_square_credit_card_authorization_code'   => $payment['id'],
     296            '_wc_square_credit_card_square_version'       => WC_SQUARE_PLUGIN_VERSION,
     297        );
     298
     299        $order->set_transaction_id( $payment['id'] );
     300
     301        foreach ( $metadata as $key => $value ) {
     302            $order->update_meta_data( $key, $value );
     303        }
     304
     305        $order->save();
     306    }
     307
     308    /**
     309     * Static helper method for Square force save compatibility.
     310     *
     311     * @param bool $val The current value.
     312     * @return bool
     313     */
     314    public static function force_save_source( bool $val ): bool {
     315        if ( autoship_cart_has_valid_autoship_items() ) {
     316            return true;
     317        }
     318        return $val;
     319    }
     320
     321    /**
     322     * Static helper method for Square customer request filtering.
     323     *
     324     * @param array    $request The Square customer request.
     325     * @param WC_Order $order The WC Order object.
     326     * @return array
     327     */
     328    public static function filter_customer_request( array $request, WC_Order $order ): array {
     329        if ( autoship_order_total_scheduled_items( $order ) > 0 ) {
     330            // Flag as a recurring payment.
     331            $request['note'] = 'Autoship recurring payment customer';
     332        }
     333        return $request;
     334    }
    91335}
  • autoship-cloud/trunk/app/Domain/PaymentIntegrations/StripePaymentIntegration.php

    r3299545 r3462779  
    99namespace Autoship\Domain\PaymentIntegrations;
    1010
    11 use Autoship\Domain\PaymentIntegration;
     11use Autoship\Domain\AbstractPaymentGateway;
     12use Autoship\Services\Logging\Logger;
    1213use Autoship\Domain\PaymentMethodType;
    1314use Exception;
     15use QPilotPaymentData;
     16use WC_Order;
     17use WC_Payment_Token;
     18use WC_Stripe_API;
     19use WC_Payment_Token_CC;
     20use WC_Stripe_Helper;
    1421
    1522/**
     
    1926 * @since 2.8.7
    2027 */
    21 class StripePaymentIntegration extends PaymentIntegration {
     28class StripePaymentIntegration extends AbstractPaymentGateway {
     29
     30    /**
     31     * Initializing.
     32     *
     33     * @return void
     34     */
     35    public function initialize(): void {
     36        Logger::log( 'Stripe Payment Integration', 'Initializing Stripe payment integration.' );
     37    }
    2238
    2339    /**
     
    2945        'stripe',
    3046        'stripe_sepa',
     47        'fkwcs_stripe',
     48        'link',
    3149    );
    3250
     
    5068        $integration->set_method_name( $settings['title'] ?? '' );
    5169        $integration->set_authorize_only( false );
     70
     71        if ( 'fkwcs_stripe' === $gateway_id ) {
     72            return self::build_funnelkit_integration( $integration );
     73        }
    5274
    5375        $environment = 'yes' === $settings['testmode'] ? 'test' : 'live';
     
    6789
    6890    /**
     91     * Build FunnelKit Stripe integration with specific configuration.
     92     *
     93     * @param StripePaymentIntegration $integration The integration instance.
     94     * @return StripePaymentIntegration
     95     */
     96    private static function build_funnelkit_integration( StripePaymentIntegration $integration ): StripePaymentIntegration {
     97        $funnelkit_mode = get_option( 'fkwcs_mode', 'test' );
     98        $environment    = 'test' === $funnelkit_mode ? 'test' : 'live';
     99
     100        if ( 'test' === $environment ) {
     101            $funnelkit_public_key = get_option( 'fkwcs_test_pub_key', '' );
     102            $funnelkit_secret_key = get_option( 'fkwcs_test_secret_key', '' );
     103
     104            $integration->set_api_key_1( $funnelkit_public_key ?? '' );
     105            $integration->set_api_key_2( $funnelkit_secret_key ?? '' );
     106            $integration->set_test_mode( true );
     107        } else {
     108            $funnelkit_public_key = get_option( 'fkwcs_pub_key', '' );
     109            $funnelkit_secret_key = get_option( 'fkwcs_secret_key', '' );
     110
     111            $integration->set_api_key_1( $funnelkit_public_key ?? '' );
     112            $integration->set_api_key_2( $funnelkit_secret_key ?? '' );
     113            $integration->set_test_mode( false );
     114        }
     115
     116        return $integration;
     117    }
     118
     119    /**
    69120     * Gets the value indicating if the payment integration is valid or not.
    70121     *
     
    86137        return true;
    87138    }
     139
     140    /**
     141     * Get order payment data for QPilot.
     142     *
     143     * @param int      $order_id The order ID.
     144     * @param WC_Order $order The order object.
     145     *
     146     * @return ?QPilotPaymentData The payment data.
     147     */
     148    public function get_order_payment_data( int $order_id, WC_Order $order ): ?QPilotPaymentData {
     149        $payment_method = $order->get_payment_method();
     150
     151        if ( 'fkwcs_stripe' === $payment_method ) {
     152            return $this->get_fkwcs_order_payment_data( $order );
     153        }
     154
     155        // Stripe version >= 4.0.0.
     156        $token_id = $order->get_meta( '_stripe_source_id' );
     157
     158        // Stripe version < 4.0.0.
     159        if ( empty( $token_id ) ) {
     160            $token_id = $order->get_meta( '_stripe_card_id' );
     161        }
     162
     163        $customer_id = $order->get_meta( '_stripe_customer_id' );
     164
     165        if ( ! empty( $token_id ) && ! empty( $customer_id ) ) {
     166
     167            $token = autoship_get_related_tokenized_id( $token_id );
     168
     169            if ( ! empty( $token ) ) {
     170
     171                $payment_data                      = new QPilotPaymentData();
     172                $payment_data->description         = $token->get_display_name();
     173                $payment_data->type                = 'Stripe';
     174                $payment_data->gateway_payment_id  = $token->get_token();
     175                $payment_data->gateway_customer_id = $customer_id;
     176
     177                if ( method_exists( $token, 'get_last4' ) && method_exists( $token, 'get_expiry_month' ) && method_exists( $token, 'get_expiry_year' ) ) {
     178                    $payment_data->last_four  = $token->get_last4();
     179                    $payment_data->expiration = $token->get_expiry_month() . substr( $token->get_expiry_year(), -2 );
     180                }
     181
     182                // Stripe Link.
     183                if ( 'link' === $token->get_type() ) {
     184                    $payment_data->gateway_payment_type = 32;
     185                } else {
     186                    $payment_data->gateway_payment_type = 7;
     187                }
     188
     189                return $payment_data;
     190            } else {
     191                // Try to fetch payment data from Stripe API.
     192                return $this->get_payment_method_data_by_id( $token_id, $customer_id );
     193            }
     194        }
     195
     196        return null;
     197    }
     198
     199    /**
     200     * Get FunnelKit Stripe order payment data.
     201     *
     202     * @param WC_Order $order The order object.
     203     * @return ?QPilotPaymentData The payment data.
     204     */
     205    private function get_fkwcs_order_payment_data( WC_Order $order ): ?QPilotPaymentData {
     206        $token_id    = $order->get_meta( '_fkwcs_source_id' );
     207        $customer_id = $order->get_meta( '_fkwcs_customer_id' );
     208
     209        if ( ! empty( $token_id ) && ! empty( $customer_id ) ) {
     210            $token = autoship_get_related_tokenized_id( $token_id );
     211
     212            if ( ! empty( $token ) ) {
     213                $expiration   = $token->get_expiry_month() . substr( $token->get_expiry_year(), -2 );
     214                $payment_data = new QPilotPaymentData();
     215
     216                $payment_data->description          = $token->get_display_name();
     217                $payment_data->type                 = 'Stripe';
     218                $payment_data->gateway_payment_id   = $token->get_token();
     219                $payment_data->gateway_customer_id  = $customer_id;
     220                $payment_data->last_four            = $token->get_last4();
     221                $payment_data->expiration           = $expiration;
     222                $payment_data->gateway_payment_type = 7;
     223
     224                return $payment_data;
     225            }
     226        }
     227
     228        return null;
     229    }
     230
     231    /**
     232     * Get payment method data by ID from Stripe API.
     233     *
     234     * @param string $payment_method_id The payment method ID.
     235     * @param string $customer_id The Stripe customer ID.
     236     * @return ?QPilotPaymentData The payment data.
     237     */
     238    public function get_payment_method_data_by_id( string $payment_method_id, string $customer_id ): ?QPilotPaymentData {
     239        if ( class_exists( 'WC_Stripe_API' ) ) {
     240            $response = WC_Stripe_API::get_payment_method( $payment_method_id );
     241            if ( ! empty( $response->error ) || is_wp_error( $response ) ) {
     242                return null;
     243            }
     244            if ( empty( $response->type ) || 'card' !== $response->type ) {
     245                return null;
     246            }
     247            $exp_year                          = substr( $response->card->exp_year, - 2 );
     248            $expiration                        = $response->card->exp_month . $exp_year;
     249            $description                       = sprintf( /* translators: 1: credit card type 2: last 4 digits 3: expiry month 4: expiry year */ __( '%1$s ending in %2$s (expires %3$s/%4$s)', 'woocommerce' ), wc_get_credit_card_type_label( $response->card->brand ), $response->card->last4, $response->card->exp_month, $exp_year );
     250            $payment_data                      = new QPilotPaymentData();
     251            $payment_data->description         = $description;
     252            $payment_data->type                = 'Stripe';
     253            $payment_data->gateway_payment_id  = $response->id;
     254            $payment_data->gateway_customer_id = $customer_id;
     255            $payment_data->last_four           = $response->card->last4;
     256            $payment_data->expiration          = $expiration;
     257
     258            return $payment_data;
     259        }
     260
     261        return null;
     262    }
     263
     264    /**
     265     * Add payment method data for QPilot.
     266     *
     267     * @param array            $payment_method_data The payment method data.
     268     * @param string           $type The payment method type.
     269     * @param WC_Payment_Token $token The payment token.
     270     * @return array The modified payment method data.
     271     */
     272    public function add_payment_method( array $payment_method_data, string $type, WC_Payment_Token $token ): array {
     273        $user_id = $token->get_user_id();
     274
     275        // FunnelKit Stripe payment gateway.
     276        if ( 'fkwcs_stripe' === $token->get_gateway_id() ) {
     277            $payment_method_data['gatewayCustomerId'] = get_user_option( '_fkwcs_customer_id', $user_id );
     278        } else {
     279            // Stripe uses the customer id from user meta as Gateway Customer ID.
     280            $payment_method_data['gatewayCustomerId'] = get_user_option( '_stripe_customer_id', $user_id );
     281
     282            if ( 'stripe_sepa' === $token->get_gateway_id() ) {
     283                $payment_method_data['gatewayPaymentType'] = 21;
     284            }
     285        }
     286
     287        return $payment_method_data;
     288    }
     289
     290    /**
     291     * Delete payment method validation.
     292     *
     293     * @param bool             $valid Current validation status.
     294     * @param string           $type The payment method type.
     295     * @param WC_Payment_Token $token The payment token.
     296     * @param object           $method The QPilot payment method.
     297     * @return bool Whether the deletion is valid.
     298     */
     299    public function delete_payment_method( bool $valid, string $type, WC_Payment_Token $token, object $method ): bool {
     300        if ( 'Stripe' === $type ) {
     301            $customer_id = get_user_option( '_stripe_customer_id', $token->get_user_id() );
     302
     303            return ( $method->gatewayCustomerId === $customer_id && $method->gatewayPaymentId === $token->get_token() ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     304        }
     305
     306        return $valid;
     307    }
     308
     309    /**
     310     * Add refund metadata for Stripe.
     311     *
     312     * @param array    $payment_meta The payment metadata.
     313     * @param WC_Order $order The order object.
     314     * @return void
     315     */
     316    public function add_adjusted_gateway_metadata( array $payment_meta, WC_Order $order ): void {
     317        $metadata = array();
     318
     319        if ( isset( $payment_meta['CustomerId'] ) ) {
     320            $metadata = array(
     321                '_stripe_customer_id'     => $payment_meta['CustomerId'],
     322                '_stripe_source_id'       => $payment_meta['Source']['Id'],
     323                '_stripe_charge_captured' => 1 === $payment_meta['Captured'] ? 'yes' : 'no',
     324                '_stripe_currency'        => strtoupper( $payment_meta['Currency'] ),
     325            );
     326            $order->set_transaction_id( $payment_meta['Id'] );
     327        } elseif ( isset( $payment_meta['charges'] ) && isset( $payment_meta['charges']['data'] ) ) {
     328            $data     = $payment_meta['charges']['data'][0];
     329            $metadata = array(
     330                '_stripe_customer_id'     => $data['customer'],
     331                '_stripe_source_id'       => $data['balance_transaction']['source'],
     332                '_stripe_charge_captured' => 1 === $data['captured'] ? 'yes' : 'no',
     333                '_stripe_currency'        => strtoupper( $data['balance_transaction']['currency'] ),
     334            );
     335            $order->set_transaction_id( $data['id'] );
     336        }
     337
     338        foreach ( $metadata as $key => $value ) {
     339            $order->update_meta_data( $key, $value );
     340        }
     341
     342        $order->save();
     343    }
     344
     345    /**
     346     * Add fee metadata for Stripe.
     347     *
     348     * @param array    $payment_meta The payment metadata.
     349     * @param WC_Order $order The order object.
     350     * @return void
     351     */
     352    public function add_adjusted_gateway_metadata_fees( array $payment_meta, WC_Order $order ): void {
     353        if ( ! class_exists( 'WC_Stripe_Helper' ) ) {
     354            return;
     355        }
     356
     357        $fee_refund = null;
     358        $net_refund = null;
     359
     360        if ( ! isset( $payment_meta['BalanceTransaction'] ) && isset( $payment_meta['charges'] ) ) {
     361            if ( isset( $payment_meta['charges']['data'] ) && ! empty( $payment_meta['charges']['data'] ) ) {
     362                $data = $payment_meta['charges']['data'][0];
     363
     364                $fee_refund = isset( $data['balance_transaction']['fee'] ) ? $payment_meta['charges']['data'][0]['balance_transaction']['fee'] : 0;
     365                $net_refund = isset( $data['balance_transaction']['net'] ) ? $payment_meta['charges']['data'][0]['balance_transaction']['net'] : 0;
     366
     367                // Use Stripes Helper Function to format currency.
     368                if ( ! in_array( strtolower( $data['balance_transaction']['currency'] ), WC_Stripe_Helper::no_decimal_currencies(), true ) ) {
     369                    $fee_refund = number_format( $fee_refund / 100, 2, '.', '' );
     370                    $net_refund = number_format( $net_refund / 100, 2, '.', '' );
     371                }
     372            }
     373        } elseif ( isset( $payment_meta['BalanceTransaction'] ) && ! empty( $payment_meta['BalanceTransaction'] ) ) {
     374            // Fees and Net needs to both come from Stripe to be accurate as the returned
     375            // values are in the local currency of the Stripe account, not from WC.
     376            $fee_refund = isset( $payment_meta['BalanceTransaction']['Fee'] ) ? $payment_meta['BalanceTransaction']['Fee'] : 0;
     377            $net_refund = isset( $payment_meta['BalanceTransaction']['Net'] ) ? $payment_meta['BalanceTransaction']['Net'] : 0;
     378
     379            // Use Stripes Helper Function to format currency.
     380            if ( ! in_array( strtolower( $payment_meta['BalanceTransaction']['Currency'] ), WC_Stripe_Helper::no_decimal_currencies(), true ) ) {
     381                $fee_refund = number_format( $fee_refund / 100, 2, '.', '' );
     382                $net_refund = number_format( $net_refund / 100, 2, '.', '' );
     383            }
     384        }
     385
     386        if ( isset( $fee_refund ) && isset( $net_refund ) ) {
     387            // Current data fee & net.
     388            $fee_current = WC_Stripe_Helper::get_stripe_fee( $order );
     389            $net_current = WC_Stripe_Helper::get_stripe_net( $order );
     390
     391            // Calculation.
     392            $fee = (float) $fee_current + (float) $fee_refund;
     393            $net = (float) $net_current + (float) $net_refund;
     394
     395            // Retrieve the current fees.
     396            $current_fee = WC_Stripe_Helper::get_stripe_fee( $order );
     397            $current_net = WC_Stripe_Helper::get_stripe_net( $order );
     398
     399            // if the fee or net doesn't exist update it.
     400            if ( empty( $current_fee ) || empty( $current_net ) ) {
     401                WC_Stripe_Helper::update_stripe_fee( $order, $fee );
     402                WC_Stripe_Helper::update_stripe_net( $order, $net );
     403            }
     404        }
     405    }
     406
     407    /**
     408     * Static helper method for Stripe force save compatibility.
     409     *
     410     * @param bool $val The current value.
     411     * @return bool
     412     */
     413    public static function force_save_source( bool $val ): bool {
     414        if ( autoship_cart_has_valid_autoship_items() ) {
     415            return true;
     416        }
     417        return $val;
     418    }
     419
     420    /**
     421     * Static helper method for Stripe intent request filtering.
     422     *
     423     * @param array    $request The Stripe intent request.
     424     * @param WC_Order $order The WC Order object.
     425     * @return array
     426     */
     427    public static function filter_intent_request( array $request, WC_Order $order ): array {
     428        if ( autoship_order_total_scheduled_items( $order ) > 0 ) {
     429            // Setup card for both on and off-session payments.
     430            $request['setup_future_usage'] = 'off_session';
     431            // Flag as a recurring payment.
     432            $request['metadata']['payment_type'] = 'recurring';
     433        }
     434        return $request;
     435    }
     436
     437    /**
     438     * Static helper method for Stripe Link mandate data.
     439     *
     440     * @param array $order_data The order data.
     441     * @param int   $order_id The order ID.
     442     * @return array
     443     */
     444    public static function add_link_mandate_data( array $order_data, int $order_id ): array {
     445        if ( isset( $order_data['paymentMethod']['type'] ) && isset( $order_data['paymentMethod']['gatewayPaymentType'] ) ) {
     446            if ( 'Stripe' === $order_data['paymentMethod']['type'] && 32 === $order_data['paymentMethod']['gatewayPaymentType'] ) {
     447                $order = wc_get_order( $order_id );
     448                if ( $order ) {
     449                    $ip_address = $order->get_customer_ip_address();
     450                    $user_agent = $order->get_customer_user_agent();
     451
     452                    $order_data['metadata']['stripe_mandate_data'] = array(
     453                        'customer_acceptance' => array(
     454                            'type'   => 'online',
     455                            'online' => array(
     456                                'ip_address' => $ip_address,
     457                                'user_agent' => $user_agent,
     458                            ),
     459                        ),
     460                    );
     461                }
     462            }
     463        }
     464        return $order_data;
     465    }
     466
     467    /**
     468     * Modifies the Payment Gateway ID for gateways treated like other gateways
     469     *
     470     * @param string              $gateway_id The current Gateway ID.
     471     * @param WC_Payment_Token_CC $token The payment Token being deleted.
     472     *
     473     * @return array The modified Payment Method Data to Send to QPilot
     474     */
     475    public static function autoship_filter_deleted_payment_method_gateway_ids( $gateway_id, $token ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
     476        return 'stripe_sepa' === $gateway_id ? 'stripe' : $gateway_id;
     477    }
    88478}
  • autoship-cloud/trunk/app/Services/QPilot/QPilotServiceClient.php

    r3352156 r3462779  
    3131use Autoship\Services\QPilot\Implementations\CouponManagement;
    3232use Autoship\Services\QPilot\Implementations\CustomerManagement;
     33use Autoship\Services\QPilot\Implementations\FeatureFlagManagement;
    3334use Autoship\Services\QPilot\Implementations\IntegrationManagement;
    3435use Autoship\Services\QPilot\Implementations\OrderManagement;
     
    138139
    139140    /**
     141     * Feature flag management implementation.
     142     *
     143     * @var FeatureFlagManagement
     144     */
     145    private FeatureFlagManagement $feature_flag_management;
     146
     147    /**
    140148     * Constructor.
    141149     *
     
    146154
    147155        // Initialize all implementation classes.
    148         $this->access_management      = new Implementations\AccessManagement( $this->api_client );
    149         $this->customer_management    = new Implementations\CustomerManagement( $this->api_client );
    150         $this->product_management     = new Implementations\ProductManagement( $this->api_client );
    151         $this->order_management       = new Implementations\OrderManagement( $this->api_client );
    152         $this->payment_management     = new Implementations\PaymentManagement( $this->api_client );
    153         $this->coupon_management      = new Implementations\CouponManagement( $this->api_client );
    154         $this->integration_management = new Implementations\IntegrationManagement( $this->api_client );
    155         $this->site_management        = new Implementations\SiteManagement( $this->api_client );
     156        $this->access_management       = new Implementations\AccessManagement( $this->api_client );
     157        $this->customer_management     = new Implementations\CustomerManagement( $this->api_client );
     158        $this->product_management      = new Implementations\ProductManagement( $this->api_client );
     159        $this->order_management        = new Implementations\OrderManagement( $this->api_client );
     160        $this->payment_management      = new Implementations\PaymentManagement( $this->api_client );
     161        $this->coupon_management       = new Implementations\CouponManagement( $this->api_client );
     162        $this->integration_management  = new Implementations\IntegrationManagement( $this->api_client );
     163        $this->site_management         = new Implementations\SiteManagement( $this->api_client );
     164        $this->feature_flag_management = new Implementations\FeatureFlagManagement( $this->api_client );
    156165    }
    157166
     
    780789
    781790    /**
     791     * Get all globally enabled feature flags.
     792     *
     793     * @return array<string> List of enabled feature flag names.
     794     */
     795    public function get_feature_flags(): array {
     796        return $this->feature_flag_management->get_feature_flags();
     797    }
     798
     799    /**
     800     * Get feature flags enabled for the current site.
     801     *
     802     * @return array<string> List of enabled feature flag names.
     803     */
     804    public function get_site_feature_flags(): array {
     805        return $this->feature_flag_management->get_site_feature_flags();
     806    }
     807
     808    /**
    782809     * Get the authentication token.
    783810     *
  • autoship-cloud/trunk/app/Services/QPilot/QPilotServiceInterface.php

    r3352156 r3462779  
    1616use Autoship\Services\QPilot\Interfaces\PaymentManagementInterface;
    1717use Autoship\Services\QPilot\Interfaces\CouponManagementInterface;
     18use Autoship\Services\QPilot\Interfaces\FeatureFlagManagementInterface;
    1819use Autoship\Services\QPilot\Interfaces\IntegrationManagementInterface;
    1920use Autoship\Services\QPilot\Interfaces\SiteManagementInterface;
     
    3839    CouponManagementInterface,
    3940    IntegrationManagementInterface,
    40     SiteManagementInterface {
     41    SiteManagementInterface,
     42    FeatureFlagManagementInterface {
    4143    /**
    4244     * Get the authentication token.
  • autoship-cloud/trunk/autoship.php

    r3453277 r3462779  
    88 * Plugin URI: https://autoship.cloud
    99 * Description: Autoship Cloud for WooCommerce
    10  * Version: 2.11.0
     10 * Version: 2.12.0
    1111 * Author: Patterns In the Cloud LLC
    1212 * Author URI: https://qpilot.cloud
     
    1717 */
    1818
    19 define( 'Autoship_Version', '2.11.0' ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase
     19define( 'Autoship_Version', '2.12.0' ); // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase
    2020
    2121if ( ! defined( 'Autoship_Plugin_Dir' ) ) {
  • autoship-cloud/trunk/languages/autoship.pot

    r3453277 r3462779  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Autoship Cloud powered by QPilot 2.11.0\n"
     5"Project-Id-Version: Autoship Cloud powered by QPilot 2.12.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/autoship-cloud\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
  • autoship-cloud/trunk/readme.txt

    r3453277 r3462779  
    1010WC tested up to: 10.4.3
    1111Requires PHP: 7.4
    12 Stable tag: 2.11.0
     12Stable tag: 2.12.0
    1313License: GPLv2 or later
    1414License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    291291
    292292== Changelog ==
     293
     294= 2.12.0 - 2026-02-16 =
     295
     296  - New! Payment gateway integrations are now managed through a centralized module with support for Stripe, PayPal and Authorize.NET. This provides a unified, modern architecture for handling payment method synchronization with QPilot.
     297
     298  - New! Added a payment compatibility layer that bridges the new modular payment system with existing WooCommerce payment hooks, ensuring a seamless transition with no disruption to existing store configurations.
     299
     300  - New! Payment gateways can now be remotely enabled or disabled via QPilot feature flags, giving merchants and support teams centralized control over which payment methods are active without requiring code changes.
    293301
    294302= 2.11.0 - 2026-02-03 =
  • autoship-cloud/trunk/src/payments.php

    r3387754 r3462779  
    7171// - 5. Upsert The Method          // autoship_update_payment_method() -- ** Same b.
    7272
     73use Autoship\Core\Plugin;
     74use Autoship\Modules\Payments\Services\PaymentMethodService;
    7375use Autoship\Services\Logging\Logger;
    7476
     
    23552357 */
    23562358function autoship_delete_payment_method( $method_id ) {
    2357     // Get Autoship Default client.
    2358     $client = autoship_get_default_client();
    2359 
    23602359    try {
    2361         // Try to delete payment method with method id.
    2362         $client->delete_payment_method( $method_id );
    2363 
     2360        $container = Plugin::get_service_container();
     2361        $service   = $container->get( PaymentMethodService::class );
     2362
     2363        return $service->delete( $method_id );
    23642364    } catch ( Exception $e ) {
    2365         $notice = autoship_expand_http_code( $e->getCode() );
    2366         autoship_log_entry( __( 'Autoship Payment Methods', 'autoship' ), sprintf( 'Error deleting QPilot Payment Method. Additional Details: %s - %s', $e->getCode(), $e->getMessage() ) );
    2367 
    2368         return new WP_Error( 'Deleting QPilot Payment Method Failed', __( $notice['desc'], 'autoship' ) ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
    2369     }
    2370 
    2371     return true;
     2365        // Fallback: service container unavailable, use legacy logic.
     2366        $client = autoship_get_default_client();
     2367
     2368        try {
     2369            $client->delete_payment_method( $method_id );
     2370        } catch ( Exception $ex ) {
     2371            $notice = autoship_expand_http_code( $ex->getCode() );
     2372            autoship_log_entry( __( 'Autoship Payment Methods', 'autoship' ), sprintf( 'Error deleting QPilot Payment Method. Additional Details: %s - %s', $ex->getCode(), $ex->getMessage() ) );
     2373
     2374            return new WP_Error( 'Deleting QPilot Payment Method Failed', __( $notice['desc'], 'autoship' ) ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
     2375        }
     2376
     2377        return true;
     2378    }
    23722379}
    23732380
     
    23992406 */
    24002407function autoship_add_payment_method( $payment_method_data ) {
    2401 
    2402     $client = autoship_get_default_client();
    2403 
    24042408    try {
    2405         $method = $client->upsert_payment_method( $payment_method_data );
     2409        $container = Plugin::get_service_container();
     2410        $service   = $container->get( PaymentMethodService::class );
     2411
     2412        return $service->upsert( $payment_method_data );
    24062413    } catch ( Exception $e ) {
    2407         $notice = autoship_expand_http_code( $e->getCode() );
    2408         autoship_log_entry( __( 'Autoship Payment Methods', 'autoship' ), sprintf( 'Error creating QPilot Payment Method. Additional Details: %s - %s', $e->getCode(), $e->getMessage() ) );
    2409 
    2410         return new WP_Error( 'Creating QPilot Payment Method Failed', __( $notice['desc'], 'autoship' ) ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
    2411     }
    2412 
    2413     return $method;
     2414        // Fallback: service container unavailable, use legacy logic.
     2415        $client = autoship_get_default_client();
     2416
     2417        try {
     2418            $method = $client->upsert_payment_method( $payment_method_data );
     2419        } catch ( Exception $ex ) {
     2420            $notice = autoship_expand_http_code( $ex->getCode() );
     2421            autoship_log_entry( __( 'Autoship Payment Methods', 'autoship' ), sprintf( 'Error creating QPilot Payment Method. Additional Details: %s - %s', $ex->getCode(), $ex->getMessage() ) );
     2422
     2423            return new WP_Error( 'Creating QPilot Payment Method Failed', __( $notice['desc'], 'autoship' ) ); // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText
     2424        }
     2425
     2426        return $method;
     2427    }
    24142428}
    24152429
     
    26302644 */
    26312645function autoship_add_tokenized_payment_method( $token_id ) {
    2632     // Get the token.
    2633     $token = WC_Payment_Tokens::get( $token_id );
    2634 
    2635     Logger::trace( 'Autoship', 'Adding Tokenized Payment Method', array( 'token_id' => $token_id ) );
    2636 
    2637     // Do not upsert payment methods at checkout.
    2638     if ( apply_filters( 'autoship_add_tokenized_payment_method', false, $token ) ) {
    2639 
    2640         Logger::trace( 'Autoship', 'Attempting to add payment method at checkout' );
    2641 
    2642         return;
    2643     }
    2644 
    2645     // Get the Type of Gateway ( standard tokenized or non-standard ).
    2646     $gateway_type = autoship_get_payment_method_gateway_type( $token->get_gateway_id() );
    2647 
    2648     // Apply filters for non-standard gateways to tokenize the method data.
    2649     $token = apply_filters( 'autoship_payment_method_tokenization', $token, $token_id, $gateway_type );
    2650 
    2651     // Allow users to override the gateway id.
    2652     $gateway_id = apply_filters( 'autoship_add_tokenized_payment_method_gateway_id', $token->get_gateway_id(), $token_id, $token );
    2653 
    2654     // Get the current gateway id types. & Allow users to extend based on id & token.
    2655     $gateway_id_types = apply_filters( 'autoship_add_tokenized_payment_method_extend_gateway_types', autoship_standard_gateway_id_types(), $gateway_id, $token );
    2656 
    2657     if ( ! array_key_exists( $gateway_id, $gateway_id_types ) ) {
    2658         return false;
    2659     }
    2660 
    2661     /**
    2662      * Add an action for Customers to call their own payment gateway add method.
    2663      */
    2664     do_action( 'autoship_add_tokenized_payment_method_extend_gateway', $gateway_id, $gateway_id_types, $token );
    2665 
    2666     // Call the Autoship general add payment function.
    2667     return autoship_add_general_payment_method( $token );
     2646    try {
     2647        $container = Plugin::get_service_container();
     2648        $service   = $container->get( PaymentMethodService::class );
     2649
     2650        return $service->add_tokenized( $token_id );
     2651    } catch ( Exception $e ) {
     2652        // Fallback: service container unavailable, use legacy logic.
     2653        $token = WC_Payment_Tokens::get( $token_id );
     2654
     2655        Logger::trace( 'Autoship', 'Adding Tokenized Payment Method', array( 'token_id' => $token_id ) );
     2656
     2657        if ( apply_filters( 'autoship_add_tokenized_payment_method', false, $token ) ) {
     2658            Logger::trace( 'Autoship', 'Attempting to add payment method at checkout' );
     2659
     2660            return;
     2661        }
     2662
     2663        $gateway_type     = autoship_get_payment_method_gateway_type( $token->get_gateway_id() );
     2664        $token            = apply_filters( 'autoship_payment_method_tokenization', $token, $token_id, $gateway_type );
     2665        $gateway_id       = apply_filters( 'autoship_add_tokenized_payment_method_gateway_id', $token->get_gateway_id(), $token_id, $token );
     2666        $gateway_id_types = apply_filters( 'autoship_add_tokenized_payment_method_extend_gateway_types', autoship_standard_gateway_id_types(), $gateway_id, $token );
     2667
     2668        if ( ! array_key_exists( $gateway_id, $gateway_id_types ) ) {
     2669            return false;
     2670        }
     2671
     2672        do_action( 'autoship_add_tokenized_payment_method_extend_gateway', $gateway_id, $gateway_id_types, $token );
     2673
     2674        return autoship_add_general_payment_method( $token );
     2675    }
    26682676}
    26692677
     
    26782686 */
    26792687function autoship_delete_tokenized_payment_method( $token_id, $token ) {
    2680 
    2681     // The id for the token being removed.
    2682     $gateway_id = apply_filters( 'autoship_delete_tokenized_payment_method_gateway_id', $token->get_gateway_id(), $token );
    2683 
    2684     // Get the current gateway id types. & Allow users to extend based on id & token.
    2685     $gateway_id_types = apply_filters( 'autoship_delete_tokenized_payment_method_extend_gateway_types', autoship_standard_gateway_id_types(), $gateway_id, $token );
    2686 
    2687     if ( ! array_key_exists( $gateway_id, $gateway_id_types ) ) {
    2688         return false;
    2689     }
    2690 
    2691     // Add an action for Customers to call their own payment gateway deletion method.
    2692     do_action( 'autoship_delete_tokenized_payment_method_extend_gateway', $gateway_id, $gateway_id_types, $token );
    2693 
    2694     // Call the Autoship general delete payment function.
    2695     return autoship_delete_general_payment_method( $token_id, $token, $gateway_id_types[ $gateway_id ] );
     2688    try {
     2689        $container = Plugin::get_service_container();
     2690        $service   = $container->get( PaymentMethodService::class );
     2691
     2692        return $service->delete_tokenized( $token_id, $token );
     2693    } catch ( Exception $e ) {
     2694        // Fallback: service container unavailable, use legacy logic.
     2695        $gateway_id       = apply_filters( 'autoship_delete_tokenized_payment_method_gateway_id', $token->get_gateway_id(), $token );
     2696        $gateway_id_types = apply_filters( 'autoship_delete_tokenized_payment_method_extend_gateway_types', autoship_standard_gateway_id_types(), $gateway_id, $token );
     2697
     2698        if ( ! array_key_exists( $gateway_id, $gateway_id_types ) ) {
     2699            return false;
     2700        }
     2701
     2702        do_action( 'autoship_delete_tokenized_payment_method_extend_gateway', $gateway_id, $gateway_id_types, $token );
     2703
     2704        return autoship_delete_general_payment_method( $token_id, $token, $gateway_id_types[ $gateway_id ] );
     2705    }
    26962706}
    26972707
     
    27072717 */
    27082718function autoship_add_non_wc_token_payment_method( $response, $order, $autoship_method_type ) {
    2709 
    2710     $token              = $response->get_payment_token();
    2711     $autoship_method_id = $token->get_id();
    2712 
    2713     // Apply filters for non-standard gateways to tokenize the method data.
    2714     $token = apply_filters( 'autoship_payment_method_tokenization', WC_Payment_Tokens::get( $autoship_method_id ), $autoship_method_id, $autoship_method_type );
    2715     if ( empty( $token ) ) {
    2716         return $token;
    2717     }
    2718 
    2719     $type = autoship_get_valid_payment_method_type( $token->get_gateway_id() );
    2720 
    2721     // Get the payment method data.
    2722     $autoship_method_data = autoship_add_general_payment_method( $token, true );
    2723 
    2724     return autoship_add_payment_method( $autoship_method_data );
     2719    try {
     2720        $container = Plugin::get_service_container();
     2721        $service   = $container->get( PaymentMethodService::class );
     2722
     2723        return $service->add_from_gateway_response( $response, $order, $autoship_method_type );
     2724    } catch ( Exception $e ) {
     2725        // Fallback: service container unavailable, use legacy logic.
     2726        $token              = $response->get_payment_token();
     2727        $autoship_method_id = $token->get_id();
     2728
     2729        $token = apply_filters( 'autoship_payment_method_tokenization', WC_Payment_Tokens::get( $autoship_method_id ), $autoship_method_id, $autoship_method_type );
     2730        if ( empty( $token ) ) {
     2731            return $token;
     2732        }
     2733
     2734        $autoship_method_data = autoship_add_general_payment_method( $token, true );
     2735
     2736        return autoship_add_payment_method( $autoship_method_data );
     2737    }
    27252738}
    27262739
     
    27372750 */
    27382751function autoship_delete_non_wc_token_payment_method( $gateway_payment_method_id, $gateway_customer_id, $type, $wc_customer_id = null ) {
    2739     if ( empty( $wc_customer_id ) || ! isset( $wc_customer_id ) ) {
    2740         $wc_customer_id = get_current_user_id();
    2741     }
    2742 
    2743     // Check if the customer exists and if not create it.
    2744     $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_delete_non_wc_token_payment' );
    2745 
    2746     if ( ! $customer ) {
    2747         return false;
    2748     }
    2749 
    2750     // Get this users payment methods from QPilot.
    2751     $payment_methods = $customer->paymentMethods; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
    2752 
    2753     $result = false;
    2754     foreach ( $payment_methods as $method ) {
    2755 
    2756         // Valid Check Since some gateways don't have a user id at deletion.
    2757         $valid = ! empty( $gateway_customer_id ) ? ( $method->type === $type && $method->gatewayPaymentId === $gateway_customer_id && $method->gatewayPaymentId === $gateway_payment_method_id ) : ( $method->type === $type && $method->gatewayPaymentId === $gateway_payment_method_id ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
    2758 
    2759         if ( $valid ) {
    2760             // Delete the Payment Method.
    2761             $result = autoship_delete_payment_method( $method->id );
    2762             break;
    2763         }
    2764     }
    2765 
    2766     return $result;
     2752    try {
     2753        $container = Plugin::get_service_container();
     2754        $service   = $container->get( PaymentMethodService::class );
     2755
     2756        return $service->delete_non_wc_token( $gateway_payment_method_id, $gateway_customer_id, $type, $wc_customer_id );
     2757    } catch ( Exception $e ) {
     2758        // Fallback: service container unavailable, use legacy logic.
     2759        if ( empty( $wc_customer_id ) || ! isset( $wc_customer_id ) ) {
     2760            $wc_customer_id = get_current_user_id();
     2761        }
     2762
     2763        $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_delete_non_wc_token_payment' );
     2764
     2765        if ( ! $customer ) {
     2766            return false;
     2767        }
     2768
     2769        $payment_methods = $customer->paymentMethods; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     2770
     2771        $result = false;
     2772        foreach ( $payment_methods as $method ) {
     2773            $valid = ! empty( $gateway_customer_id ) ? ( $method->type === $type && $method->gatewayPaymentId === $gateway_customer_id && $method->gatewayPaymentId === $gateway_payment_method_id ) : ( $method->type === $type && $method->gatewayPaymentId === $gateway_payment_method_id ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     2774
     2775            if ( $valid ) {
     2776                $result = autoship_delete_payment_method( $method->id );
     2777                break;
     2778            }
     2779        }
     2780
     2781        return $result;
     2782    }
    27672783}
    27682784
     
    27772793 */
    27782794function autoship_add_general_payment_method( $token, $return_or_added = false ) {
    2779 
    27802795    if ( is_wp_error( $token ) || empty( $token ) || ! $token ) {
    27812796        return;
    27822797    }
    27832798
    2784     // Get the User associated with the token.
    2785     $wc_customer_id = $token->get_user_id();
    2786 
    2787     // CHeck if the customer is already in Qpilot
    2788     // This will upsert them if they aren't already there.
    2789     $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_add_general_payment' );
    2790 
    2791     // Don't process if not a Autoship.
    2792     if ( ! $customer ) {
    2793         return false;
    2794     }
    2795 
    2796     // Create the payment method and gather the info.
    2797     $payment_method_id = $token->get_token();
    2798 
    2799     /**
    2800      * Apply filters for non-standard gateways to tokenize the method data.
    2801      */
    2802     $gateway     = $token->get_gateway_id();
    2803     $description = apply_filters( 'autoship_add_general_payment_method_description', $token->get_display_name(), $token );
    2804     $type        = autoship_get_valid_payment_method_type( $gateway );
    2805     $expiration  = null;
    2806 
    2807     // Catch Scenarios where the token class doesn't use expirations ( i.e. SEPA ).
    2808     if ( is_callable( array( $token, 'get_expiry_month' ) ) && is_callable( array( $token, 'get_expiry_year' ) ) ) {
    2809         $expiration = $token->get_expiry_month() . substr( $token->get_expiry_year(), -2 );
    2810     }
    2811 
    2812     // Verify if the method exists within the token.
    2813     $last_four_digits = null;
    2814     if ( method_exists( $token, 'get_last4' ) && is_callable( array( $token, 'get_last4' ) ) && 'ppcp-gateway' !== $gateway ) {
    2815         $last_four_digits = $token->get_last4();
    2816     }
    2817 
    2818     $data = array(
    2819         'type'              => $type,
    2820         'expiration'        => $expiration,
    2821         'lastFourDigits'    => $last_four_digits,
    2822         'gatewayCustomerId' => $wc_customer_id,
    2823         'gatewayPaymentId'  => $payment_method_id,
    2824         'description'       => $description,
    2825     );
    2826 
    2827     // PayPal Payments adjustments.
    2828     if ( 'PayPalV3' == $type ) { //phpcs:ignore
    2829         if ( 'ppcp-credit-card-gateway' == $gateway ) { //phpcs:ignore
    2830             $data['gatewayPaymentType'] = 26;
    2831         }
    2832 
    2833         if ( 'ppcp-gateway' == $gateway ) { //phpcs:ignore
    2834             $data['gatewayPaymentType'] = 25;
    2835             $data['description']        = 'PayPal Payments';
    2836         }
    2837 
    2838         $gateway_customer_id = get_user_meta( $wc_customer_id, '_ppcp_target_customer_id', true );
    2839         if ( ! $gateway_customer_id ) {
    2840             $gateway_customer_id = get_user_meta( $wc_customer_id, 'ppcp_customer_id', true );
    2841         }
    2842 
    2843         if ( $gateway_customer_id ) {
    2844             $data['gatewayCustomerId'] = $gateway_customer_id;
    2845         }
    2846     }
    2847 
    2848     // Stripe Link Adjustments.
    2849     if ( is_a( $token, 'WC_Payment_Token_Link' ) && 'Stripe' == $type && 'stripe' == $gateway ) { //phpcs:ignore
    2850         $data['gatewayPaymentType'] = 32;
    2851     }
    2852 
    2853     $payment_method_data = autoship_get_general_payment_method_customer_data( $wc_customer_id, $data );
    2854 
    2855     // Apply the Payment method filter for customers to override.
    2856     $payment_method_data = apply_filters( 'autoship_add_general_payment_method', $payment_method_data, $type, $token );
    2857     $payment_method_data = apply_filters( "autoship_add_{$type}_payment_method", $payment_method_data, $type, $token );
    2858     $payment_method_data = apply_filters( 'autoship_api_create_payment_method_data', $payment_method_data );
    2859 
    2860     if ( Logger::is_tracing_enabled() ) {
    2861         $export_payment_data = var_export( $payment_method_data, true ); // phpcs:ignore
    2862 
    2863         Logger::trace( 'Autoship Payment Methods', "Sending Payment Method Data to QPilot. Type: '{$type}' Gateway: '{$gateway}' Data: {$export_payment_data}." );
    2864     }
    2865 
    2866     // Add the Method.
    2867     return $return_or_added ? $payment_method_data : autoship_add_payment_method( $payment_method_data );
     2799    try {
     2800        $container = Plugin::get_service_container();
     2801        $service   = $container->get( PaymentMethodService::class );
     2802
     2803        return $service->add_from_token( $token, $return_or_added );
     2804    } catch ( Exception $e ) {
     2805        // Fallback: service container unavailable, use legacy logic.
     2806        $wc_customer_id = $token->get_user_id();
     2807
     2808        $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_add_general_payment' );
     2809
     2810        if ( ! $customer ) {
     2811            return false;
     2812        }
     2813
     2814        $payment_method_id = $token->get_token();
     2815        $gateway           = $token->get_gateway_id();
     2816        $description       = apply_filters( 'autoship_add_general_payment_method_description', $token->get_display_name(), $token );
     2817        $type              = autoship_get_valid_payment_method_type( $gateway );
     2818        $expiration        = null;
     2819
     2820        if ( is_callable( array( $token, 'get_expiry_month' ) ) && is_callable( array( $token, 'get_expiry_year' ) ) ) {
     2821            $expiration = $token->get_expiry_month() . substr( $token->get_expiry_year(), -2 );
     2822        }
     2823
     2824        $last_four_digits = null;
     2825        if ( method_exists( $token, 'get_last4' ) && is_callable( array( $token, 'get_last4' ) ) && 'ppcp-gateway' !== $gateway ) {
     2826            $last_four_digits = $token->get_last4();
     2827        }
     2828
     2829        $data = array(
     2830            'type'              => $type,
     2831            'expiration'        => $expiration,
     2832            'lastFourDigits'    => $last_four_digits,
     2833            'gatewayCustomerId' => $wc_customer_id,
     2834            'gatewayPaymentId'  => $payment_method_id,
     2835            'description'       => $description,
     2836        );
     2837
     2838        if ( 'PayPalV3' == $type ) { //phpcs:ignore
     2839            if ( 'ppcp-credit-card-gateway' == $gateway ) { //phpcs:ignore
     2840                $data['gatewayPaymentType'] = 26;
     2841            }
     2842
     2843            if ( 'ppcp-gateway' == $gateway ) { //phpcs:ignore
     2844                $data['gatewayPaymentType'] = 25;
     2845                $data['description']        = 'PayPal Payments';
     2846            }
     2847
     2848            $gateway_customer_id = get_user_meta( $wc_customer_id, '_ppcp_target_customer_id', true );
     2849            if ( ! $gateway_customer_id ) {
     2850                $gateway_customer_id = get_user_meta( $wc_customer_id, 'ppcp_customer_id', true );
     2851            }
     2852
     2853            if ( $gateway_customer_id ) {
     2854                $data['gatewayCustomerId'] = $gateway_customer_id;
     2855            }
     2856        }
     2857
     2858        if ( is_a( $token, 'WC_Payment_Token_Link' ) && 'Stripe' == $type && 'stripe' == $gateway ) { //phpcs:ignore
     2859            $data['gatewayPaymentType'] = 32;
     2860        }
     2861
     2862        $payment_method_data = autoship_get_general_payment_method_customer_data( $wc_customer_id, $data );
     2863
     2864        $payment_method_data = apply_filters( 'autoship_add_general_payment_method', $payment_method_data, $type, $token );
     2865        $payment_method_data = apply_filters( "autoship_add_{$type}_payment_method", $payment_method_data, $type, $token );
     2866        $payment_method_data = apply_filters( 'autoship_api_create_payment_method_data', $payment_method_data );
     2867
     2868        if ( Logger::is_tracing_enabled() ) {
     2869            $export_payment_data = var_export( $payment_method_data, true ); // phpcs:ignore
     2870
     2871            Logger::trace( 'Autoship Payment Methods', "Sending Payment Method Data to QPilot. Type: '{$type}' Gateway: '{$gateway}' Data: {$export_payment_data}." );
     2872        }
     2873
     2874        return $return_or_added ? $payment_method_data : autoship_add_payment_method( $payment_method_data );
     2875    }
    28682876}
    28692877
     
    28782886 */
    28792887function autoship_delete_general_payment_method( $token_id, $token, $type = '' ) {
    2880 
    2881     // Get the WC Customer from the Token.
    2882     $wc_customer_id = $token->get_user_id();
    2883 
    2884     // Get the Autoship Customer ID.
    2885     $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_delete_general_payment' );
    2886 
    2887     // If not autoship bypass.
    2888     if ( ! $customer || empty( $type ) ) {
    2889         return false;
    2890     }
    2891 
    2892     // Get the method id from the token.
    2893     $payment_method_id = $token->get_token();
    2894 
    2895     // Get the customer's payment methods from QPilot.
    2896     $payment_methods = $customer->paymentMethods; // phpcs:ignore
    2897 
    2898     // Check if there are any payment methods.
    2899     if ( empty( $payment_methods ) ) {
    2900         return true;
    2901     }
    2902 
    2903     $result = false;
    2904 
    2905     // Iterate through the payment methods and delete the one that matches this type.
    2906     foreach ( $payment_methods as $method ) {
    2907 
    2908         // If the token being deleted matches a token in QPilot Delete it.
    2909         $valid = ( $method->type === $type && $method->gatewayCustomerId === $wc_customer_id && $method->gatewayPaymentId === $payment_method_id ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
    2910 
    2911         // Added a filter to allow for custom matches between QPilot and Autoship.
    2912         // Takes the current matching boolean, the supplied QPilot Method Type, The current Token and the QPilot Payment Method object.
    2913         $valid = apply_filters( 'autoship_delete_general_payment_method_qpilot_match', $valid, $type, $token, $method );
    2914         $valid = apply_filters( "autoship_delete_{$type}_payment_method_qpilot_match", $valid, $type, $token, $method );
    2915 
    2916         if ( $valid ) {
    2917 
    2918             // Delete the Payment Method.
    2919             $result = autoship_delete_payment_method( $method->id );
    2920             break;
    2921         }
    2922     }
    2923 
    2924     return $result;
     2888    try {
     2889        $container = Plugin::get_service_container();
     2890        $service   = $container->get( PaymentMethodService::class );
     2891
     2892        return $service->delete_for_token( $token_id, $token, $type );
     2893    } catch ( Exception $e ) {
     2894        // Fallback: service container unavailable, use legacy logic.
     2895        $wc_customer_id = $token->get_user_id();
     2896
     2897        $customer = autoship_check_autoship_customer( $wc_customer_id, 'autoship_delete_general_payment' );
     2898
     2899        if ( ! $customer || empty( $type ) ) {
     2900            return false;
     2901        }
     2902
     2903        $payment_method_id = $token->get_token();
     2904        $payment_methods   = $customer->paymentMethods; // phpcs:ignore
     2905
     2906        if ( empty( $payment_methods ) ) {
     2907            return true;
     2908        }
     2909
     2910        $result = false;
     2911
     2912        foreach ( $payment_methods as $method ) {
     2913            $valid = ( $method->type === $type && $method->gatewayCustomerId === $wc_customer_id && $method->gatewayPaymentId === $payment_method_id ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
     2914
     2915            $valid = apply_filters( 'autoship_delete_general_payment_method_qpilot_match', $valid, $type, $token, $method );
     2916            $valid = apply_filters( "autoship_delete_{$type}_payment_method_qpilot_match", $valid, $type, $token, $method );
     2917
     2918            if ( $valid ) {
     2919                $result = autoship_delete_payment_method( $method->id );
     2920                break;
     2921            }
     2922        }
     2923
     2924        return $result;
     2925    }
    29252926}
    29262927
  • autoship-cloud/trunk/vendor/autoload.php

    r3362678 r3462779  
    1515        }
    1616    }
    17     throw new RuntimeException($err);
     17    trigger_error(
     18        $err,
     19        E_USER_ERROR
     20    );
    1821}
    1922
  • autoship-cloud/trunk/vendor/composer/InstalledVersions.php

    r3362678 r3462779  
    2727class InstalledVersions
    2828{
    29     /**
    30      * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
    31      * @internal
    32      */
    33     private static $selfDir = null;
    34 
    3529    /**
    3630     * @var mixed[]|null
     
    330324
    331325    /**
    332      * @return string
    333      */
    334     private static function getSelfDir()
    335     {
    336         if (self::$selfDir === null) {
    337             self::$selfDir = strtr(__DIR__, '\\', '/');
    338         }
    339 
    340         return self::$selfDir;
    341     }
    342 
    343     /**
    344326     * @return array[]
    345327     * @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
     
    355337
    356338        if (self::$canGetVendors) {
    357             $selfDir = self::getSelfDir();
     339            $selfDir = strtr(__DIR__, '\\', '/');
    358340            foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
    359341                $vendorDir = strtr($vendorDir, '\\', '/');
  • autoship-cloud/trunk/vendor/composer/autoload_classmap.php

    r3453277 r3462779  
    3939    'Autoship\\Core\\SettingsInterface' => $baseDir . '/app/Core/SettingsInterface.php',
    4040    'Autoship\\Core\\SitesManagerInterface' => $baseDir . '/app/Core/SitesManagerInterface.php',
     41    'Autoship\\Domain\\AbstractPaymentGateway' => $baseDir . '/app/Domain/AbstractPaymentGateway.php',
    4142    'Autoship\\Domain\\Nextime\\DeliveryDate' => $baseDir . '/app/Domain/Nextime/DeliveryDate.php',
    4243    'Autoship\\Domain\\Nextime\\NextimeShippingCalculator' => $baseDir . '/app/Domain/Nextime/NextimeShippingCalculator.php',
     
    7172    'Autoship\\Modules\\Nextime\\NextimeModule' => $baseDir . '/app/Modules/Nextime/NextimeModule.php',
    7273    'Autoship\\Modules\\Nextime\\NextimeService' => $baseDir . '/app/Modules/Nextime/NextimeService.php',
     74    'Autoship\\Modules\\Payments\\Compatibility\\AbstractPaymentCompatibility' => $baseDir . '/app/Modules/Payments/Compatibility/AbstractPaymentCompatibility.php',
     75    'Autoship\\Modules\\Payments\\Compatibility\\AuthorizeNetPaymentCompatibility' => $baseDir . '/app/Modules/Payments/Compatibility/AuthorizeNetPaymentCompatibility.php',
     76    'Autoship\\Modules\\Payments\\Compatibility\\PayPalPaymentCompatibility' => $baseDir . '/app/Modules/Payments/Compatibility/PayPalPaymentCompatibility.php',
     77    'Autoship\\Modules\\Payments\\Compatibility\\PaymentsCompatibilityManager' => $baseDir . '/app/Modules/Payments/Compatibility/PaymentsCompatibilityManager.php',
     78    'Autoship\\Modules\\Payments\\Compatibility\\SquarePaymentCompatibility' => $baseDir . '/app/Modules/Payments/Compatibility/SquarePaymentCompatibility.php',
     79    'Autoship\\Modules\\Payments\\Compatibility\\StripePaymentCompatibility' => $baseDir . '/app/Modules/Payments/Compatibility/StripePaymentCompatibility.php',
     80    'Autoship\\Modules\\Payments\\PaymentsModule' => $baseDir . '/app/Modules/Payments/PaymentsModule.php',
     81    'Autoship\\Modules\\Payments\\Services\\PaymentGatewayRegistry' => $baseDir . '/app/Modules/Payments/Services/PaymentGatewayRegistry.php',
     82    'Autoship\\Modules\\Payments\\Services\\PaymentGatewayService' => $baseDir . '/app/Modules/Payments/Services/PaymentGatewayService.php',
     83    'Autoship\\Modules\\Payments\\Services\\PaymentMethodDataBuilder' => $baseDir . '/app/Modules/Payments/Services/PaymentMethodDataBuilder.php',
     84    'Autoship\\Modules\\Payments\\Services\\PaymentMethodService' => $baseDir . '/app/Modules/Payments/Services/PaymentMethodService.php',
     85    'Autoship\\Modules\\Payments\\Services\\PaymentsCompatibilityService' => $baseDir . '/app/Modules/Payments/Services/PaymentsCompatibilityService.php',
    7386    'Autoship\\Modules\\QuickLinks\\Controllers\\QuickLinkController' => $baseDir . '/app/Modules/QuickLinks/Controllers/QuickLinkController.php',
    7487    'Autoship\\Modules\\QuickLinks\\Controllers\\QuickLinksSettingsController' => $baseDir . '/app/Modules/QuickLinks/Controllers/QuickLinksSettingsController.php',
     
    134147    'Autoship\\Services\\QPilot\\Implementations\\CouponManagement' => $baseDir . '/app/Services/QPilot/Implementations/CouponManagement.php',
    135148    'Autoship\\Services\\QPilot\\Implementations\\CustomerManagement' => $baseDir . '/app/Services/QPilot/Implementations/CustomerManagement.php',
     149    'Autoship\\Services\\QPilot\\Implementations\\FeatureFlagManagement' => $baseDir . '/app/Services/QPilot/Implementations/FeatureFlagManagement.php',
    136150    'Autoship\\Services\\QPilot\\Implementations\\IntegrationManagement' => $baseDir . '/app/Services/QPilot/Implementations/IntegrationManagement.php',
    137151    'Autoship\\Services\\QPilot\\Implementations\\OrderManagement' => $baseDir . '/app/Services/QPilot/Implementations/OrderManagement.php',
     
    146160    'Autoship\\Services\\QPilot\\Interfaces\\CouponManagementInterface' => $baseDir . '/app/Services/QPilot/Interfaces/CouponManagementInterface.php',
    147161    'Autoship\\Services\\QPilot\\Interfaces\\CustomerManagementInterface' => $baseDir . '/app/Services/QPilot/Interfaces/CustomerManagementInterface.php',
     162    'Autoship\\Services\\QPilot\\Interfaces\\FeatureFlagManagementInterface' => $baseDir . '/app/Services/QPilot/Interfaces/FeatureFlagManagementInterface.php',
    148163    'Autoship\\Services\\QPilot\\Interfaces\\IntegrationManagementInterface' => $baseDir . '/app/Services/QPilot/Interfaces/IntegrationManagementInterface.php',
    149164    'Autoship\\Services\\QPilot\\Interfaces\\OrderManagementInterface' => $baseDir . '/app/Services/QPilot/Interfaces/OrderManagementInterface.php',
  • autoship-cloud/trunk/vendor/composer/autoload_static.php

    r3453277 r3462779  
    88{
    99    public static $prefixLengthsPsr4 = array (
    10         'A' =>
     10        'A' => 
    1111        array (
    1212            'Autoship\\' => 9,
     
    1515
    1616    public static $prefixDirsPsr4 = array (
    17         'Autoship\\' =>
     17        'Autoship\\' => 
    1818        array (
    1919            0 => __DIR__ . '/../..' . '/app',
     
    5454        'Autoship\\Core\\SettingsInterface' => __DIR__ . '/../..' . '/app/Core/SettingsInterface.php',
    5555        'Autoship\\Core\\SitesManagerInterface' => __DIR__ . '/../..' . '/app/Core/SitesManagerInterface.php',
     56        'Autoship\\Domain\\AbstractPaymentGateway' => __DIR__ . '/../..' . '/app/Domain/AbstractPaymentGateway.php',
    5657        'Autoship\\Domain\\Nextime\\DeliveryDate' => __DIR__ . '/../..' . '/app/Domain/Nextime/DeliveryDate.php',
    5758        'Autoship\\Domain\\Nextime\\NextimeShippingCalculator' => __DIR__ . '/../..' . '/app/Domain/Nextime/NextimeShippingCalculator.php',
     
    8687        'Autoship\\Modules\\Nextime\\NextimeModule' => __DIR__ . '/../..' . '/app/Modules/Nextime/NextimeModule.php',
    8788        'Autoship\\Modules\\Nextime\\NextimeService' => __DIR__ . '/../..' . '/app/Modules/Nextime/NextimeService.php',
     89        'Autoship\\Modules\\Payments\\Compatibility\\AbstractPaymentCompatibility' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/AbstractPaymentCompatibility.php',
     90        'Autoship\\Modules\\Payments\\Compatibility\\AuthorizeNetPaymentCompatibility' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/AuthorizeNetPaymentCompatibility.php',
     91        'Autoship\\Modules\\Payments\\Compatibility\\PayPalPaymentCompatibility' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/PayPalPaymentCompatibility.php',
     92        'Autoship\\Modules\\Payments\\Compatibility\\PaymentsCompatibilityManager' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/PaymentsCompatibilityManager.php',
     93        'Autoship\\Modules\\Payments\\Compatibility\\SquarePaymentCompatibility' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/SquarePaymentCompatibility.php',
     94        'Autoship\\Modules\\Payments\\Compatibility\\StripePaymentCompatibility' => __DIR__ . '/../..' . '/app/Modules/Payments/Compatibility/StripePaymentCompatibility.php',
     95        'Autoship\\Modules\\Payments\\PaymentsModule' => __DIR__ . '/../..' . '/app/Modules/Payments/PaymentsModule.php',
     96        'Autoship\\Modules\\Payments\\Services\\PaymentGatewayRegistry' => __DIR__ . '/../..' . '/app/Modules/Payments/Services/PaymentGatewayRegistry.php',
     97        'Autoship\\Modules\\Payments\\Services\\PaymentGatewayService' => __DIR__ . '/../..' . '/app/Modules/Payments/Services/PaymentGatewayService.php',
     98        'Autoship\\Modules\\Payments\\Services\\PaymentMethodDataBuilder' => __DIR__ . '/../..' . '/app/Modules/Payments/Services/PaymentMethodDataBuilder.php',
     99        'Autoship\\Modules\\Payments\\Services\\PaymentMethodService' => __DIR__ . '/../..' . '/app/Modules/Payments/Services/PaymentMethodService.php',
     100        'Autoship\\Modules\\Payments\\Services\\PaymentsCompatibilityService' => __DIR__ . '/../..' . '/app/Modules/Payments/Services/PaymentsCompatibilityService.php',
    88101        'Autoship\\Modules\\QuickLinks\\Controllers\\QuickLinkController' => __DIR__ . '/../..' . '/app/Modules/QuickLinks/Controllers/QuickLinkController.php',
    89102        'Autoship\\Modules\\QuickLinks\\Controllers\\QuickLinksSettingsController' => __DIR__ . '/../..' . '/app/Modules/QuickLinks/Controllers/QuickLinksSettingsController.php',
     
    149162        'Autoship\\Services\\QPilot\\Implementations\\CouponManagement' => __DIR__ . '/../..' . '/app/Services/QPilot/Implementations/CouponManagement.php',
    150163        'Autoship\\Services\\QPilot\\Implementations\\CustomerManagement' => __DIR__ . '/../..' . '/app/Services/QPilot/Implementations/CustomerManagement.php',
     164        'Autoship\\Services\\QPilot\\Implementations\\FeatureFlagManagement' => __DIR__ . '/../..' . '/app/Services/QPilot/Implementations/FeatureFlagManagement.php',
    151165        'Autoship\\Services\\QPilot\\Implementations\\IntegrationManagement' => __DIR__ . '/../..' . '/app/Services/QPilot/Implementations/IntegrationManagement.php',
    152166        'Autoship\\Services\\QPilot\\Implementations\\OrderManagement' => __DIR__ . '/../..' . '/app/Services/QPilot/Implementations/OrderManagement.php',
     
    161175        'Autoship\\Services\\QPilot\\Interfaces\\CouponManagementInterface' => __DIR__ . '/../..' . '/app/Services/QPilot/Interfaces/CouponManagementInterface.php',
    162176        'Autoship\\Services\\QPilot\\Interfaces\\CustomerManagementInterface' => __DIR__ . '/../..' . '/app/Services/QPilot/Interfaces/CustomerManagementInterface.php',
     177        'Autoship\\Services\\QPilot\\Interfaces\\FeatureFlagManagementInterface' => __DIR__ . '/../..' . '/app/Services/QPilot/Interfaces/FeatureFlagManagementInterface.php',
    163178        'Autoship\\Services\\QPilot\\Interfaces\\IntegrationManagementInterface' => __DIR__ . '/../..' . '/app/Services/QPilot/Interfaces/IntegrationManagementInterface.php',
    164179        'Autoship\\Services\\QPilot\\Interfaces\\OrderManagementInterface' => __DIR__ . '/../..' . '/app/Services/QPilot/Interfaces/OrderManagementInterface.php',
  • autoship-cloud/trunk/vendor/composer/platform_check.php

    r3453277 r3462779  
    2020        }
    2121    }
    22     throw new \RuntimeException(
    23         'Composer detected issues in your platform: ' . implode(' ', $issues)
     22    trigger_error(
     23        'Composer detected issues in your platform: ' . implode(' ', $issues),
     24        E_USER_ERROR
    2425    );
    2526}
Note: See TracChangeset for help on using the changeset viewer.