is_form_editor() || $this->is_entry_detail() ) ? "field_{$this->formId}_{$this->id}_creditcard_warning_message" : ''; foreach ( $this->inputs as $input ) { $input_id = str_replace( $this->id . '.', '', $input['id'] ); $input_value = GFForms::get( $input['id'], $values ); if ( ! empty( $_POST[ 'is_submit_' . $this->formId ] ) && $this->isRequired && in_array( $input_id, $required_inputs_ids ) && empty( $input_value ) ) { $describedby_attributes[ $input_id ] = "aria-describedby='validation_message_{$this->formId}_{$this->id} {$warning_container_id}'"; } else { $describedby_attributes[ $input_id ] = empty( $warning_container_id ) ? '' : "aria-describedby='{$warning_container_id}'"; } } return $describedby_attributes; } public function validate( $value, $form ) { $card_number = rgpost( 'input_' . $this->id . '_1' ); $expiration_date = rgpost( 'input_' . $this->id . '_2' ); $security_code = rgpost( 'input_' . $this->id . '_3' ); if ( $this->isRequired && ( empty( $card_number ) || empty( $security_code ) || empty( $expiration_date[0] ) || empty( $expiration_date[1] ) ) ) { $this->failed_validation = true; $this->validation_message = empty( $this->errorMessage ) ? esc_html__( 'Please enter your credit card information.', 'gravityforms' ) : $this->errorMessage; } elseif ( ! empty( $card_number ) ) { $card_type = GFCommon::get_card_type( $card_number ); if ( empty( $security_code ) ) { $this->failed_validation = true; $this->validation_message = esc_html__( "Please enter your card's security code.", 'gravityforms' ); } elseif ( ! $card_type ) { $this->failed_validation = true; $this->validation_message = esc_html__( 'Invalid credit card number.', 'gravityforms' ); } elseif ( ! $this->is_card_supported( $card_type['slug'] ) ) { $this->failed_validation = true; $this->validation_message = $card_type['name'] . ' ' . esc_html__( 'is not supported. Please enter one of the supported credit cards.', 'gravityforms' ); } } } public function is_card_supported( $card_slug ) { $supported_cards = $this->creditCards; $default_cards = array( 'amex', 'discover', 'mastercard', 'visa' ); if ( ! empty( $supported_cards ) && in_array( $card_slug, $supported_cards ) ) { return true; } elseif ( empty( $supported_cards ) && in_array( $card_slug, $default_cards ) ) { return true; } return false; } public function get_value_submission( $field_values, $get_from_post_global_var = true ) { if ( $get_from_post_global_var ) { $value[ $this->id . '.1' ] = $this->get_input_value_submission( 'input_' . $this->id . '_1', rgar( $this->inputs[0], 'name' ), $field_values, true ); $value[ $this->id . '.2' ] = $this->get_input_value_submission( 'input_' . $this->id . '_2', rgar( $this->inputs[1], 'name' ), $field_values, true ); $value[ $this->id . '.3' ] = $this->get_input_value_submission( 'input_' . $this->id . '_3', rgar( $this->inputs[3], 'name' ), $field_values, true ); $value[ $this->id . '.4' ] = $this->get_input_value_submission( 'input_' . $this->id . '_4', rgar( $this->inputs[4], 'name' ), $field_values, true ); $value[ $this->id . '.5' ] = $this->get_input_value_submission( 'input_' . $this->id . '_5', rgar( $this->inputs[5], 'name' ), $field_values, true ); } else { $value = $this->get_input_value_submission( 'input_' . $this->id, $this->inputName, $field_values, $get_from_post_global_var ); } return $value; } /** * Returns the HTML tag for the field container. * * @since 2.5 * * @param array $form The current Form object. * * @return string */ public function get_field_container_tag( $form ) { if ( GFCommon::is_legacy_markup_enabled( $form ) ) { return parent::get_field_container_tag( $form ); } return 'fieldset'; } /** * Displays an insecure page warning below the field content. * * @since 2.5 * * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission. * @param bool $force_frontend_label Should the frontend label be displayed in the admin even if an admin label is configured. * @param array $form The Form Object currently being processed. * * @return string */ public function get_field_content( $value, $force_frontend_label, $form ) { $is_form_editor = GFCommon::is_form_editor(); $is_entry_detail = GFCommon::is_entry_detail(); $is_admin = $is_form_editor || $is_entry_detail; // Get existing field content. $field_content = parent::get_field_content( $value, $force_frontend_label, $form ); // If SSL is not used, display warning message. if ( ! GFCommon::is_ssl() && ! $is_admin ) { $field_content = "
id}_creditcard_warning_message'>" . esc_html__( 'This page is unsecured. Do not enter a real credit card number! Use this field only for testing purposes. ', 'gravityforms' ) . '
' . $field_content; } return $field_content; } public function get_field_input( $form, $value = '', $entry = null ) { $is_entry_detail = $this->is_entry_detail(); $is_form_editor = $this->is_form_editor(); $form_id = $form['id']; $id = intval( $this->id ); $field_id = $is_entry_detail || $is_form_editor || $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id"; $form_id = ( $is_entry_detail || $is_form_editor ) && empty( $form_id ) ? rgget( 'id' ) : $form_id; $disabled_text = $is_form_editor ? "disabled='disabled'" : ''; $class_suffix = $is_entry_detail ? '_admin' : ''; $form_sub_label_placement = rgar( $form, 'subLabelPlacement' ); $field_sub_label_placement = $this->subLabelPlacement; $is_sub_label_above = $field_sub_label_placement == 'above' || ( empty( $field_sub_label_placement ) && $form_sub_label_placement == 'above' ); $sub_label_class_attribute = $field_sub_label_placement == 'hidden_label' ? "class='hidden_sub_label screen-reader-text'" : ''; $card_number = ''; $card_name = ''; $expiration_month = ''; $expiration_year = ''; $security_code = ''; $autocomplete = RGFormsModel::is_html5_enabled() ? "autocomplete='off'" : ''; if ( is_array( $value ) ) { $card_number = esc_attr( rgget( $this->id . '.1', $value ) ); $card_name = esc_attr( rgget( $this->id . '.5', $value ) ); $expiration_date = rgget( $this->id . '.2', $value ); if ( ! is_array( $expiration_date ) && ! empty( $expiration_date ) ) { $expiration_date = explode( '/', $expiration_date ); } if ( is_array( $expiration_date ) && count( $expiration_date ) == 2 ) { $expiration_month = $expiration_date[0]; $expiration_year = $expiration_date[1]; } $security_code = esc_attr( rgget( $this->id . '.3', $value ) ); } $action = ! ( $is_entry_detail || $is_form_editor ) ? "gformMatchCard(\"{$field_id}_1\");" : ''; $onchange = "onchange='{$action}'"; $onkeyup = "onkeyup='{$action}'"; $card_icons = ''; $cards = GFCommon::get_card_types(); $enabled_card_names = array(); foreach ( $cards as $card ) { $style = ''; if ( $this->is_card_supported( $card['slug'] ) ) { $print_card = true; $enabled_card_names[] = rgar( $card, 'name' ); } elseif ( $is_form_editor || $is_entry_detail ) { $print_card = true; $style = "style='display:none;'"; } else { $print_card = false; } if ( $print_card ) { $card_icons .= "
{$card['name']}
"; } } $payment_methods = apply_filters( 'gform_payment_methods', array(), $this, $form_id ); $payment_options = ''; if ( is_array( $payment_methods ) ) { foreach ( $payment_methods as $payment_method ) { $checked = rgpost( 'gform_payment_method' ) == $payment_method['key'] ? "checked='checked'" : ''; $payment_options .= "
{$payment_method['label']}
"; } } $checked = rgpost( 'gform_payment_method' ) == 'creditcard' || rgempty( 'gform_payment_method' ) ? "checked='checked'" : ''; $card_radio_button = empty( $payment_options ) ? '' : ""; $card_describer = sprintf( "%s %s", $form_id, $this->id, esc_html__( 'Supported Credit Cards:', 'gravityforms' ), implode( ', ', $enabled_card_names ) ); $card_icons = "{$payment_options}
{$card_radio_button}{$card_icons}{$card_describer}
"; // Aria attributes. $number_aria_attributes = $this->get_aria_attributes( $value, '1' ); $expiration_month_aria_attributes = $this->get_aria_attributes( $value, '2_month' ); $expiration_year_aria_attributes = $this->get_aria_attributes( $value, '2_year' ); $security_aria_attributes = $this->get_aria_attributes( $value, '3' ); $name_aria_attributes = $this->get_aria_attributes( $value, '5' ); //card number fields $tabindex = $this->get_tabindex(); $card_number_field_input = GFFormsModel::get_input( $this, $this->id . '.1' ); $html5_output = ! is_admin() && GFFormsModel::is_html5_enabled() ? "pattern='[0-9]*' title='" . esc_attr__( 'Only digits are allowed', 'gravityforms' ) . "'" : ''; $card_number_label = rgar( $card_number_field_input, 'customLabel' ) != '' ? $card_number_field_input['customLabel'] : esc_html__( 'Card Number', 'gravityforms' ); $card_number_label = gf_apply_filters( array( 'gform_card_number', $form_id ), $card_number_label, $form_id ); $card_number_placeholder = $this->get_input_placeholder_attribute( $card_number_field_input ); if ( $is_sub_label_above ) { $card_field = " {$card_icons} "; } else { $card_field = " {$card_icons} "; } // Expiration Date Inputs $expiration_wrapper_tag = $this->get_field_container_tag( $form ) === 'fieldset' ? 'fieldset' : 'span'; $expiration_label_tag = ( $expiration_wrapper_tag === 'fieldset' && $is_sub_label_above ) ? 'legend' : 'label'; $expiration_label_for = $expiration_wrapper_tag === 'label' ? " for='{$field_id}_2_month'" : ''; $expiration_month_input = GFFormsModel::get_input( $this, $this->id . '.2_month' ); $expiration_label = rgar( $expiration_month_input, 'customLabel' ) != '' ? esc_html( $expiration_month_input['customLabel'] ) : esc_html__( 'Expiration Date', 'gravityforms' ); $expiration_label = gf_apply_filters( array( 'gform_card_expiration', $form_id ), $expiration_label, $form_id ); // Expiration Date: Month $expiration_month_label = $expiration_wrapper_tag === 'fieldset' ? "" : ''; $expiration_month_tab_index = $this->get_tabindex(); $expiration_month_placeholder = $this->get_input_placeholder_value( $expiration_month_input ); $expiration_months = $this->get_expiration_months( $expiration_month, $expiration_month_placeholder ); // Expiration Date: Year $expiration_year_input = GFFormsModel::get_input( $this, $this->id . '.2_year' ); $expiration_year_tab_index = $this->get_tabindex(); $expiration_year_placeholder = $this->get_input_placeholder_value( $expiration_year_input ); $expiration_years = $this->get_expiration_years( $expiration_year, $expiration_year_placeholder ); if ( $expiration_wrapper_tag === 'fieldset' ) { $expiration_year_label_for = $is_form_editor ? '' : $field_id . '_2_year'; $expiration_year_label = ""; } else { $expiration_year_label = ''; } if ( $is_sub_label_above ) { $expiration_field = " <{$expiration_wrapper_tag} class='ginput_cardinfo_left{$class_suffix}' id='{$field_id}_2_cardinfo_left'> <{$expiration_label_tag}{$expiration_label_for} {$sub_label_class_attribute}>{$expiration_label} " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " {$expiration_month_label} " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " {$expiration_year_label} " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " "; } else { $expiration_field = " <{$expiration_wrapper_tag} class='ginput_cardinfo_left{$class_suffix}' id='{$field_id}_2_cardinfo_left'> " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " {$expiration_month_label} " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " {$expiration_year_label} " . ( $expiration_wrapper_tag === 'fieldset' ? "" : "" ) . " <{$expiration_label_tag}{$expiration_label_for} {$sub_label_class_attribute}>{$expiration_label} "; } //security code field $tabindex = $this->get_tabindex(); $security_code_field_input = GFFormsModel::get_input( $this, $this->id . '.3' ); $security_code_label = rgar( $security_code_field_input, 'customLabel' ) != '' ? $security_code_field_input['customLabel'] : esc_html__( 'Security Code', 'gravityforms' ); $security_code_label = gf_apply_filters( array( 'gform_card_security_code', $form_id ), $security_code_label, $form_id ); $html5_output = GFFormsModel::is_html5_enabled() ? "pattern='[0-9]*' title='" . esc_attr__( 'Only digits are allowed', 'gravityforms' ) . "'" : ''; $security_code_placeholder = $this->get_input_placeholder_attribute( $security_code_field_input ); if ( $is_sub_label_above ) { $security_field = "   "; } else { $security_field = "   "; } $tabindex = $this->get_tabindex(); $card_name_field_input = GFFormsModel::get_input( $this, $this->id . '.5' ); $card_name_label = rgar( $card_name_field_input, 'customLabel' ) != '' ? $card_name_field_input['customLabel'] : esc_html__( 'Cardholder Name', 'gravityforms' ); $card_name_label = gf_apply_filters( array( 'gform_card_name', $form_id ), $card_name_label, $form_id ); $card_name_placeholder = $this->get_input_placeholder_attribute( $card_name_field_input ); if ( $is_sub_label_above ) { $card_name_field = " "; } else { $card_name_field = " "; } return "
" . $card_field . $expiration_field . $security_field . $card_name_field . '
'; } /** * Get the options for the Expiration Date Month drop down. * * @since Unknown * @since 2.5 Added $included_placeholder parameter. * * @param string $selected_month The currently selected month. * @param string $placeholder Placeholder text. * * @return string */ private function get_expiration_months( $selected_month, $placeholder ) { $str = ''; if ( empty( $placeholder ) ) { $placeholder = esc_html__( 'Month', 'gravityforms' ); } $str .= ""; for ( $i = 1; $i < 13; $i ++ ) { $selected = intval( $selected_month ) == $i ? "selected='selected'" : ''; $month = str_pad( $i, 2, '0', STR_PAD_LEFT ); $str .= ""; } return $str; } /** * Get the options for the Expiration Date Year drop down. * * @since Unknown * @since 2.5 Added $included_placeholder parameter. * * @param string $selected_year The currently selected year. * @param string $placeholder Placeholder text. * * @return string */ private function get_expiration_years( $selected_year, $placeholder ) { $str = ''; if ( empty( $placeholder ) ) { $placeholder = esc_html__( 'Year', 'gravityforms' ); } $str .= ""; $year = intval( date( 'Y' ) ); for ( $i = $year; $i < ( $year + 20 ); $i ++ ) { $selected = intval( $selected_year ) == $i ? "selected='selected'" : ''; $str .= ""; } return $str; } public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) { if ( is_array( $value ) ) { $card_number = trim( rgget( $this->id . '.1', $value ) ); $card_type = trim( rgget( $this->id . '.4', $value ) ); $separator = $format == 'html' ? '
' : "\n"; return empty( $card_number ) ? '' : $card_type . $separator . $card_number; } else { return ''; } } public function get_form_inline_script_on_page_render( $form ) { $field_id = "input_{$form['id']}_{$this->id}"; if ( $this->forceSSL && ! GFCommon::is_ssl() && ! GFCommon::is_preview() ) { $script = "document.location.href='" . esc_js( RGFormsModel::get_current_page_url( true ) ) . "';"; } else { $script = "jQuery(document).ready(function(){ { gformMatchCard(\"{$field_id}_1\"); } } );"; } $card_rules = $this->get_credit_card_rules(); $script = "if(!window['gf_cc_rules']){window['gf_cc_rules'] = new Array(); } window['gf_cc_rules'] = " . GFCommon::json_encode( $card_rules ) . "; $script"; return $script; } public function get_credit_card_rules() { $cards = GFCommon::get_card_types(); //$supported_cards = //TODO: Only include enabled cards $rules = array(); foreach ( $cards as $card ) { $prefixes = explode( ',', $card['prefixes'] ); foreach ( $prefixes as $prefix ) { $rules[ $card['slug'] ][] = $prefix; } } return $rules; } public function get_entry_inputs() { $inputs = array(); // only store month and card number input values foreach ( $this->inputs as $input ) { if ( in_array( $input['id'], array( $this->id . '.1', $this->id . '.4' ) ) ) { $inputs[] = $input; } } return $inputs; } public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) { //saving last 4 digits of credit card list( $input_token, $field_id_token, $input_id ) = rgexplode( '_', $input_name, 3 ); if ( $input_id == '1' ) { $value = str_replace( ' ', '', $value ); $card_number_length = strlen( $value ); $value = substr( $value, - 4, 4 ); $value = str_pad( $value, $card_number_length, 'X', STR_PAD_LEFT ); } elseif ( $input_id == '4' ) { $value = rgpost( "input_{$field_id_token}_4" ); if ( ! $value ) { $card_number = rgpost( "input_{$field_id_token}_1" ); $card_type = GFCommon::get_card_type( $card_number ); $value = $card_type ? $card_type['name'] : ''; } } else { $value = ''; } return $this->sanitize_entry_value( $value, $form['id'] ); } /** * Upgrades inputs, if needed. * * @since 2.1.2.7 * @access public * @see GF_Field::post_convert_field() * * @uses GF_Field::post_convert_field() * @uses GF_Field_CreditCard::maybe_upgrade_inputs() * * @return void */ public function post_convert_field() { parent::post_convert_field(); $this->maybe_upgrade_inputs(); } /** * GF1.8 and earlier used 5 inputs (1 input for the expiration date); GF1.9 changed to 6 inputs (the expiration month and year now separate); upgrade those fields still using the older configuration. */ public function maybe_upgrade_inputs() { $inputs = $this->inputs; $exp_input = $inputs[1]; $exp_id = $this->id . '.2'; if ( count( $inputs ) == 5 && $exp_input['id'] == $exp_id ) { $new_inputs = array( array( 'id' => $exp_id . '_month', 'label' => esc_html__( 'Expiration Month', 'gravityforms' ), 'defaultLabel' => $exp_input['label'] ), array( 'id' => $exp_id . '_year', 'label' => esc_html__( 'Expiration Year', 'gravityforms' ), ) ); array_splice( $inputs, 1, 1, $new_inputs ); $this->inputs = $inputs; } } } GF_Fields::register( new GF_Field_CreditCard() );