get_error_message() ); return; } $fields = array_values( array_filter( Field_Sanitizer::flatten_submittable_fields( $form['fields'] ), function ( $field ) { return ! in_array( $field['type'], Field_Sanitizer::NON_INPUT_TYPES, true ); } ) ); $this->maybe_write_header( $token, $spreadsheet_id, $sheet_name, $fields ); $row = [ current_time( 'mysql' ) ]; foreach ( $fields as $field ) { $value = $values[ $field['id'] ] ?? ''; $row[] = is_array( $value ) ? wp_json_encode( $value ) : (string) $value; } self::$pending_context = [ 'values' => $values, 'form' => $form, 'entry_id' => $entry_id, 'settings' => $settings, 'attempt' => $attempt ]; $result = Google_Sheets_Client::append_row( $token, $spreadsheet_id, $sheet_name, $row ); if ( is_wp_error( $result ) ) { $data = $result->get_error_data(); Api_Log::log( 'sheets', $form['id'], $spreadsheet_id, $data['status'] ?? null, $result->get_error_message() ); $this->maybe_retry( $attempt ); self::$pending_context = null; return; } Api_Log::log( 'sheets', $form['id'], $spreadsheet_id, 200, __( 'Row appended.', 'chargeforms' ) ); self::$pending_context = null; } /** * One extra read of row 1 per submission is the cost of "never write a * header on top of real data" — an unconditional write-on-every-call * would clobber row 1 the moment the sheet had any content there * already (including a header a human wrote by hand before connecting * the integration). Only writes when row 1 is genuinely empty. */ private function maybe_write_header( $token, $spreadsheet_id, $sheet_name, array $fields ) { $existing = Google_Sheets_Client::get_values( $token, $spreadsheet_id, $sheet_name . '!1:1' ); if ( is_wp_error( $existing ) || ! empty( $existing ) ) { return; } $headers = [ __( 'Submitted At', 'chargeforms' ) ]; foreach ( $fields as $field ) { $headers[] = $field['label'] ?? $field['id']; } Google_Sheets_Client::update_values( $token, $spreadsheet_id, $sheet_name . '!1:1', [ $headers ] ); } /** WP-Cron callback — same shape as Mailchimp_Integration::handle_retry(). */ public static function handle_retry( $values, $form, $entry_id, $settings, $attempt ) { ( new self() )->process( $values, $form, $entry_id, $settings, $attempt ); } private function maybe_retry( $attempt ) { if ( ! self::$pending_context || $attempt >= self::MAX_ATTEMPTS ) { return; } $ctx = self::$pending_context; wp_schedule_single_event( time() + self::RETRY_DELAY_SECONDS, self::RETRY_HOOK, [ $ctx['values'], $ctx['form'], $ctx['entry_id'], $ctx['settings'], $ctx['attempt'] + 1 ] ); } }