PK
Current Path : /home/wallqcyy/www/wp-content/plugins/checkout-plugins-stripe-woo/gateway/stripe/ |
Current File : //home/wallqcyy/www/wp-content/plugins/checkout-plugins-stripe-woo/gateway/stripe/sepa.php |
<?php /** * SEPA Gateway * * @package checkout-plugins-stripe-woo * @since 1.4.0 */ namespace CPSW\Gateway\Stripe; use CPSW\Inc\Helper; use CPSW\Inc\Logger; use CPSW\Inc\Traits\Get_Instance; use CPSW\Inc\Traits\Subscriptions; use CPSW\Gateway\Local_Gateway; use CPSW\Gateway\Stripe\Stripe_Api; use CPSW\Inc\Token; use Exception; use WP_Error; use WC_AJAX; /** * SEPA * * @since 1.4.0 */ class Sepa extends Local_Gateway { use Get_Instance; use Subscriptions; /** * Gateway id * * @var string */ public $id = 'cpsw_sepa'; /** * Payment method types * * @var string */ public $payment_method_types = 'sepa_debit'; /** * Retry interval * * @var int */ public $retry_interval = 1; /** * Constructor * * @since 1.4.0 */ public function __construct() { parent::__construct(); add_action( 'init', [ $this, 'init_gateway' ] ); add_action( 'wc_ajax_' . $this->id . '_verify_payment_intent', [ $this, 'verify_intent' ] ); add_action( 'woocommerce_payment_token_class', [ $this, 'modify_token_class' ], 15, 2 ); add_filter( 'woocommerce_payment_methods_list_item', [ $this, 'get_saved_payment_methods_list' ], 10, 2 ); add_filter( 'woocommerce_payment_successful_result', [ $this, 'modify_successful_payment_result' ], 999, 2 ); } /** * Initializes the SEPA gateway. * * Sets up the gateway's properties and settings. * * @since 1.11.0 */ public function init_gateway() { $this->method_title = __( 'SEPA', 'checkout-plugins-stripe-woo' ); $this->method_description = $this->method_description(); $this->has_fields = true; $this->init_supports(); $this->maybe_init_subscriptions(); $this->init_form_fields(); $this->init_settings(); // get_option should be called after init_form_fields(). $this->title = $this->get_option( 'title' ); $this->description = $this->get_option( 'description' ); $this->order_button_text = $this->get_option( 'order_button_text' ); $this->enable_saved_cards = $this->get_option( 'enable_saved_card' ); $this->company_name = $this->get_option( 'company_name' ); $this->statement_descriptor = $this->clean_statement_descriptor( $this->get_option( 'statement_descriptor' ) ); $this->payment_conform = true; } /** * Controls the output on the my account page. * * @since 1.4.0 * * @param array $item Individual list item from woocommerce_saved_payment_methods_list. * @param WC_Payment_Token $token The payment token associated with this method entry. * * @return array $item */ public function get_saved_payment_methods_list( $item, $token ) { if ( 'cpsw_sepa' === strtolower( $token->get_type() ) ) { $item['method']['last4'] = $token->get_last4(); $item['method']['brand'] = esc_html__( 'SEPA IBAN', 'checkout-plugins-stripe-woo' ); } return $item; } /** * Added token class * * @since 1.4.0 * * @param string $class token class name. * @param string $type gateway name. * * @return string */ public function modify_token_class( $class, $type ) { if ( 'cpsw_sepa' === $type ) { return 'CPSW\Inc\Token'; } return $class; } /** * Description for SEPA gateway * * @since 1.4.0 * * @return string */ public function method_description() { $payment_description = $this->payment_description(); return sprintf( /* translators: %1$s: Break, %2$s: HTML entities */ __( 'Accept payments using SEPA. %1$s %2$s', 'checkout-plugins-stripe-woo' ), '<br/>', $payment_description ); } /** * Returns all supported currencies for this payment method. * * @since 1.4.0 * * @return array */ public function get_supported_currency() { return apply_filters( 'cpsw_sepa_supported_currencies', [ 'EUR', ] ); } /** * Init payment supports. * * @since 1.4.0 * * @return void */ public function init_supports() { parent::init_supports(); $this->supports[] = 'tokenization'; $this->supports[] = 'add_payment_method'; $this->supports[] = 'pre-orders'; } /** * Process payment method functionality * * @return array */ public function add_payment_method() { $source_id = ''; if ( empty( $_POST['payment_method_created'] ) && empty( $_POST['stripe_token'] ) || ! is_user_logged_in() ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing $error_msg = __( 'There was a problem adding the payment method.', 'checkout-plugins-stripe-woo' ); /* translators: error msg */ Logger::error( sprintf( __( 'Add payment method Error: %1$1s', 'checkout-plugins-stripe-woo' ), $error_msg ) ); return; } $customer_id = $this->get_customer_id(); $source = ! empty( $_POST['payment_method_created'] ) ? wc_clean( wp_unslash( $_POST['payment_method_created'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing $stripe_api = new Stripe_Api(); $response = $stripe_api->payment_methods( 'retrieve', [ $source ] ); $source_object = $response['success'] ? $response['data'] : false; if ( isset( $source_object ) ) { if ( ! empty( $source_object->error ) ) { $error_msg = __( 'Invalid stripe source', 'checkout-plugins-stripe-woo' ); wc_add_notice( $error_msg, 'error' ); /* translators: error msg */ Logger::error( sprintf( __( 'Add payment method Error: %1$1s', 'checkout-plugins-stripe-woo' ), $error_msg ) ); return; } $source_id = $source_object->id; } $stripe_api = new Stripe_Api(); $response = $stripe_api->payment_methods( 'attach', [ $source_id, [ 'customer' => $customer_id ] ] ); $response = $response['success'] ? $response['data'] : false; $user = wp_get_current_user(); $user_id = ( $user->ID && $user->ID > 0 ) ? $user->ID : false; $this->create_payment_token_for_user( $user_id, $source_object ); if ( ! $response || is_wp_error( $response ) || ! empty( $response->error ) ) { $error_msg = __( 'Unble to attach payment method to customer', 'checkout-plugins-stripe-woo' ); wc_add_notice( $error_msg, 'error' ); /* translators: error msg */ Logger::error( sprintf( __( 'Add payment method Error: %1$1s', 'checkout-plugins-stripe-woo' ), $error_msg ) ); return; } do_action( 'cpsw_add_payment_method_' . ( isset( $_POST['payment_method'] ) ? wc_clean( wp_unslash( $_POST['payment_method'] ) ) : '' ) . '_success', $source_id, $source_object ); //phpcs:ignore WordPress.Security.NonceVerification.Missing Logger::info( __( 'New payment method added successfully', 'checkout-plugins-stripe-woo' ) ); return [ 'result' => 'success', 'redirect' => wc_get_endpoint_url( 'payment-methods' ), ]; } /** * Add more gateway form fields * * @since 1.4.0 * * @return array */ public function get_default_settings() { $company_name = [ 'enable_saved_card' => [ 'label' => __( 'Enable Payment via Saved IBAN', 'checkout-plugins-stripe-woo' ), 'title' => __( 'Saved IBAN', 'checkout-plugins-stripe-woo' ), 'type' => 'checkbox', 'description' => __( 'Save IBAN details for future orders', 'checkout-plugins-stripe-woo' ), 'default' => 'yes', 'desc_tip' => true, ], 'statement_descriptor' => [ 'title' => __( 'Statement Descriptor', 'checkout-plugins-stripe-woo' ), 'type' => 'text', 'description' => __( 'Statement descriptors are limited to 22 characters, cannot use the special characters >, <, ", \, *, /, (, ), {, }, and must not consist solely of numbers. This will appear on your customer\'s statement in capital letters.', 'checkout-plugins-stripe-woo' ), 'default' => get_bloginfo( 'name' ), 'desc_tip' => true, ], 'company_name' => [ 'title' => __( 'Company Name', 'checkout-plugins-stripe-woo' ), 'type' => 'text', 'default' => get_bloginfo( 'name' ), 'desc_tip' => true, 'description' => __( 'The name of your company that will appear in the SEPA mandate info.', 'checkout-plugins-stripe-woo' ), ], ]; $local_settings = parent::get_default_settings(); $local_settings['description']['default'] = __( 'Mandate Information.', 'checkout-plugins-stripe-woo' ); return array_merge( $local_settings, $company_name ); } /** * Checks whether this gateway is available. * * @since 1.4.0 * * @return boolean */ public function is_available() { if ( ! in_array( $this->get_currency(), $this->get_supported_currency(), true ) ) { return false; } if ( ! Helper::get_webhook_secret() ) { return false; } return parent::is_available(); } /** * Renders the Stripe elements form. * * @since 1.4.0 * * @return void */ public function payment_form() { // translators: %s: company name. $description = Helper::get_sepa_mandate_description(); ?> <fieldset id="<?php echo esc_attr( $this->id ); ?>-form" class="wc-payment-form cpsw_stripe_sepa_payment_form"> <?php echo wp_kses_post( wpautop( $description ) ); ?> <p class="form-row form-row-wide"> <label for="cpsw-sepa-stripe-iban-element"> <?php esc_html_e( 'IBAN.', 'checkout-plugins-stripe-woo' ); ?> <span class="required">*</span> </label> <div id="cpsw_stripe_sepa_iban_element" class="cpsw_stripe_sepa_iban_element_field"> <!-- A Stripe Element will be inserted here. --> </div> </p> <!-- Used to display form errors --> <div class="cpsw_stripe_sepa_error" role="alert"></div> <div class="clear"></div> </fieldset> <?php } /** * Creates markup for payment form for card payments * * @since 1.4.0 * * @return void */ public function payment_fields() { $total = WC()->cart->total; $display_tokenization = $this->supports( 'tokenization' ) && is_checkout() && 'yes' === $this->enable_saved_cards && is_user_logged_in(); $description = $this->get_description(); $description = ! empty( $description ) ? $description : ''; // If paying from order, we need to get total from order not cart. Frontend summary nonce verification may not be required. if ( isset( $_GET['pay_for_order'] ) && ! empty( $_GET['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended global $wp; $order = wc_get_order( wc_clean( $wp->query_vars['order-pay'] ) ); $total = $order->get_total(); } if ( is_add_payment_method_page() ) { $total = ''; } /** * Action before payment field. * * @since 1.4.0 */ do_action( $this->id . '_before_payment_field_checkout' ); echo '<div id="cpsw-sepa-payment-data" data-amount="' . esc_attr( $this->get_formatted_amount( $total ) ) . '" data-currency="' . esc_attr( strtolower( get_woocommerce_currency() ) ) . '">'; if ( $display_tokenization ) { $this->tokenization_script(); $this->saved_payment_methods(); } $this->payment_form(); // Nonce verification already done in parent WooCommerce function. if ( apply_filters( 'cpsw_sepa_display_save_payment_method_checkbox', $display_tokenization ) && ! $this->is_subscription_item_in_cart() && ! is_add_payment_method_page() && ! isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $this->save_payment_method_checkbox(); } if ( 'live' !== Helper::get_payment_mode() ) { echo '<div class="cpsw-test-description">'; /* translators: %1$1s - %6$6s: HTML Markup */ echo wp_kses_post( Helper::get_sepa_test_mode_description() ); echo '</div>'; } echo '</div>'; /** * Action after payment field. * * @since 1.4.0 */ do_action( $this->id . '_after_payment_field_checkout' ); } /** * Tokenize card payment * * @since 1.4.0 * * @param int $user_id id of current user placing . * @param object $payment_method payment method object. * * @return object token object. */ public function create_payment_token_for_user( $user_id, $payment_method ) { $token = new Token(); $token->set_last4( $payment_method->sepa_debit->last4 ); $token->set_gateway_id( $this->id ); $token->set_token( $payment_method->id ); $token->set_user_id( $user_id ); $token->save(); return $token; } /** * Process woocommerce orders after payment is done * * @since 1.4.0 * * @param int $order_id wooCommerce order id. * * @return array data to redirect after payment processing. */ public function process_payment( $order_id ) { if ( $this->maybe_change_subscription_payment_method( $order_id ) ) { return $this->process_change_subscription_payment_method( $order_id ); } $order = wc_get_order( $order_id ); if ( 0 === absint( $order->get_total() ) ) { return $this->process_zero_amount_order( $order ); } if ( $this->is_using_saved_payment_method() ) { return $this->process_payment_with_saved_payment_method( $order_id ); } try { $customer_id = $this->get_customer_id( $order ); $idempotency_key = $order->get_order_key() . time(); $data = [ 'amount' => $this->get_formatted_amount( $order->get_total() ), 'currency' => $this->get_currency(), 'description' => $this->get_order_description( $order ), 'metadata' => $this->get_metadata( $order_id ), 'payment_method_types' => [ 'sepa_debit' ], 'customer' => $customer_id, ]; if ( ! empty( trim( $this->statement_descriptor ) ) ) { $data['statement_descriptor_suffix'] = $this->statement_descriptor; } if ( $this->should_save_card( $order_id ) ) { $data['setup_future_usage'] = 'off_session'; } /* translators: %1$1s order id, %2$2s order total amount */ Logger::info( sprintf( __( 'Begin processing payment with SEPA for order %1$1s for the amount of %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $order->get_total() ) ); $intent_data = $this->get_payment_intent( $order_id, $idempotency_key, $data ); if ( $intent_data ) { $response_data = [ 'result' => 'success', 'redirect' => false, 'intent_secret' => $intent_data['client_secret'], 'save_card' => $this->should_save_card( $order_id ), ]; /** * This is used to verify nonce for payment method checkout perform by block checkout. */ if ( isset( $_POST['payment_local_nonce'] ) && wp_verify_nonce( sanitize_text_field( $_POST['payment_local_nonce'] ), 'stripe_local_nonce' ) ) { $response_data['verification_url'] = $this->get_verification_url( $order_id, $this->get_return_url( $order ), $this->should_save_card( $order_id ) ); } return $response_data; } else { return [ 'result' => 'fail', 'redirect' => '', ]; } } catch ( Exception $e ) { Logger::error( $e->getMessage(), true ); return new WP_Error( 'order-error', '<div class="woocommerce-error">' . $e->getMessage() . '</div>', [ 'status' => 200 ] ); } } /** * Prepare payment method object * * @since 1.4.5 * * @param object $payment_method payment method object from intent. * @param object $token token object used for payment. * * @return object */ public function prepare_payment_method_zero_amount( $payment_method, $token ) { return (object) apply_filters( 'cpsw_prepare_payment_method_args', [ 'token_id' => $token, 'customer' => $payment_method->customer, 'source' => $payment_method->id, 'source_object' => $payment_method, 'payment_method' => $payment_method->id, 'payment_method_object' => $payment_method, ] ); } /** * Process zero amount order * * @since 1.4.5 * * @param WC_Order $order WooCommerce order. */ public function process_zero_amount_order( $order ) { $token = ''; $prepared_payment_method = ''; // Nonce verification already done in parent woocommerce function. if ( isset( $_POST['payment_method_created'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing $method_created = ! empty( $_POST['payment_method_created'] ) ? wc_clean( wp_unslash( $_POST['payment_method_created'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing $stripe_api = new Stripe_Api(); $response = $stripe_api->payment_methods( 'retrieve', [ $method_created ] ); $payment_method = $response['success'] ? $response['data'] : false; $user = $order->get_id() ? $order->get_user() : wp_get_current_user(); $user_id = $user->ID; $token = $this->create_payment_token_for_user( $user_id, $payment_method ); $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token ); } elseif ( $this->is_using_saved_payment_method() ) { // Nonce verification already done in parent woocommerce function. $token = $this->get_token_from_request( $_POST ); //phpcs:ignore WordPress.Security.NonceVerification.Missing $stripe_api = new Stripe_Api(); $response = $stripe_api->payment_methods( 'retrieve', [ $token ] ); $payment_method = $response['success'] ? $response['data'] : false; $prepared_payment_method = $this->prepare_payment_method_zero_amount( $payment_method, $token ); } if ( ! empty( $token ) ) { $order->payment_complete(); $this->save_payment_method_to_order( $order, $prepared_payment_method ); return $this->process_change_subscription_payment_method( $order->get_id() ); } return false; } /** * Verify intent state and redirect. * * @since 1.4.0 * * @return void */ public function verify_intent() { $checkout_url = wc_get_checkout_url(); $order_id = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended // Check for empty order id. if ( empty( $order_id ) ) { wc_add_notice( __( 'No orders are found for provided order ID.', 'checkout-plugins-stripe-woo' ), 'error' ); Logger::error( __( 'Invalid order Id.', 'checkout-plugins-stripe-woo' ) ); wp_safe_redirect( $checkout_url ); exit(); } $order = wc_get_order( $order_id ); // Check for empty order object. if ( empty( $order ) ) { wc_add_notice( __( 'No valid order found.', 'checkout-plugins-stripe-woo' ), 'error' ); Logger::error( __( 'Invalid order. No order found for provided order ID.', 'checkout-plugins-stripe-woo' ) ); wp_safe_redirect( $checkout_url ); exit(); } if ( ! isset( $_GET['order_key'] ) || ! $order->key_is_valid( wc_clean( $_GET['order_key'] ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended wc_add_notice( __( 'Invalid order key received.', 'checkout-plugins-stripe-woo' ), 'error' ); Logger::error( __( 'Invalid order key.', 'checkout-plugins-stripe-woo' ) ); wp_safe_redirect( $checkout_url ); exit(); } $intent_secret = $order->get_meta( '_cpsw_intent_secret' ); $stripe_api = new Stripe_Api(); $response = $stripe_api->payment_intents( 'retrieve', [ $intent_secret['id'] ] ); $intent = $response['success'] ? $response['data'] : false; if ( ! $intent ) { return; } if ( isset( $_GET['save_card'] ) && '1' === $_GET['save_card'] ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended $user = $order->get_id() ? $order->get_user() : wp_get_current_user(); $user_id = $user->ID; $payment_method = $intent->payment_method; $response = $stripe_api->payment_methods( 'retrieve', [ $payment_method ] ); $payment_method = $response['success'] ? $response['data'] : false; $token = $this->create_payment_token_for_user( $user_id, $payment_method ); /* translators: %1$1s order id, %2$2s token id */ Logger::info( sprintf( __( 'Payment method tokenized for Order id - %1$1s with token id - %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $token->get_id() ) ); $prepared_payment_method = $this->prepare_payment_method( $payment_method, $token ); $this->save_payment_method_to_order( $order, $prepared_payment_method ); } if ( 'succeeded' === $intent->status ) { $redirect_to = $this->process_order( end( $intent->charges->data ), $order_id ); $redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order ); } elseif ( 'pending' === $intent->status || 'processing' === $intent->status ) { $order_stock_reduced = $order->get_meta( '_order_stock_reduced', true ); if ( ! $order_stock_reduced ) { wc_reduce_stock_levels( $order_id ); } $order->set_transaction_id( $intent->id ); $others_info = __( 'Payment will be completed once payment_intent.succeeded webhook received from Stripe.', 'checkout-plugins-stripe-woo' ); /* translators: transaction id, other info */ $order->update_status( 'on-hold', sprintf( __( 'Stripe charge awaiting payment: %1$s. %2$s', 'checkout-plugins-stripe-woo' ), $intent->id, $others_info ) ); $redirect_to = $this->get_return_url( $order ); $redirect_url = apply_filters( 'cpsw_redirect_order_url', $redirect_to, $order ); } elseif ( isset( $response['data']->last_payment_error ) ) { $message = isset( $response['data']->last_payment_error->message ) ? $response['data']->last_payment_error->message : ''; // translators: %s: payment fail message. wc_add_notice( sprintf( __( 'Payment failed. %s', 'checkout-plugins-stripe-woo' ), $message ), $notice_type = 'error' ); /* translators: %1$1s order id, %2$2s payment fail message. */ Logger::error( sprintf( __( 'Payment failed for Order id - %1$1s. %2$2s', 'checkout-plugins-stripe-woo' ), $order_id, $message ) ); $redirect_url = $checkout_url; } wp_safe_redirect( $redirect_url ); exit(); } /** * Modify redirect url * * @since 1.4.0 * * @param array $result redirect url array. * @param int $order_id woocommerce order id. * * @return array modified redirect url array. */ public function modify_successful_payment_result( $result, $order_id ) { if ( empty( $order_id ) ) { return $result; } $order = wc_get_order( $order_id ); if ( $this->id !== $order->get_payment_method() ) { return $result; } if ( ! isset( $result['intent_secret'] ) ) { return $result; } // Put the final thank you page redirect into the verification URL. $verification_url = add_query_arg( [ 'order' => $order_id, 'confirm_payment_nonce' => wp_create_nonce( 'cpsw_confirm_payment_intent' ), 'redirect_to' => rawurlencode( $result['redirect'] ), 'save_card' => $result['save_card'], 'order_key' => $order->get_order_key(), ], WC_AJAX::get_endpoint( $this->id . '_verify_payment_intent' ) ); // Combine into a hash. $redirect = sprintf( '#confirm-pi-%s:%s:%s', $result['intent_secret'], rawurlencode( $verification_url ), $this->id ); return [ 'result' => 'success', 'redirect' => $redirect, ]; } }