Fix recording playback authentication and phone number display
Recording Playback Fixes: - Added proxy_recording_audio endpoint to handle authenticated playback - Recordings now play through WordPress proxy like voicemails - Updated Play/Download buttons to use proxy URLs instead of direct Twilio URLs - No more Twilio login prompts when playing recordings Phone Number Display Fixes: - Fixed outbound call recording display to show customer numbers properly - For browser phone outbound calls, customer number now shows in "From" field - Added logic to detect client: calls and swap number display appropriately - Enhanced logging for debugging outbound call number handling The recording system now works consistently with the voicemail system, providing seamless authenticated playback without exposing Twilio credentials. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
		@@ -2217,8 +2217,9 @@ class TWP_Admin {
 | 
			
		||||
                html += '<td>' + formatDuration(recording.duration) + '</td>';
 | 
			
		||||
                html += '<td>';
 | 
			
		||||
                if (recording.has_recording) {
 | 
			
		||||
                    html += '<button class="button button-small" onclick="playRecording(\'' + recording.recording_url + '\')">Play</button> ';
 | 
			
		||||
                    html += '<a href="' + recording.recording_url + '" class="button button-small" download>Download</a>';
 | 
			
		||||
                    var proxyUrl = '<?php echo home_url('/wp-json/twilio-webhook/v1/recording-audio/'); ?>' + recording.id;
 | 
			
		||||
                    html += '<button class="button button-small" onclick="playRecording(\'' + proxyUrl + '\')">Play</button> ';
 | 
			
		||||
                    html += '<a href="' + proxyUrl + '" class="button button-small" download>Download</a>';
 | 
			
		||||
                    <?php if (current_user_can('manage_options')): ?>
 | 
			
		||||
                    html += ' <button class="button button-small button-link-delete" onclick="deleteRecording(' + recording.id + ')">Delete</button>';
 | 
			
		||||
                    <?php endif; ?>
 | 
			
		||||
@@ -7415,11 +7416,24 @@ class TWP_Admin {
 | 
			
		||||
            global $wpdb;
 | 
			
		||||
            $recordings_table = $wpdb->prefix . 'twp_call_recordings';
 | 
			
		||||
            
 | 
			
		||||
            // For outbound calls from browser phone, swap the numbers for better display
 | 
			
		||||
            $from_number = $call->from;
 | 
			
		||||
            $to_number = $call->to;
 | 
			
		||||
            
 | 
			
		||||
            // If this is a browser phone call (from contains 'client:'), then the customer is the 'to' number
 | 
			
		||||
            if (strpos($call->from, 'client:') === 0) {
 | 
			
		||||
                // This is an outbound call from browser phone
 | 
			
		||||
                // Store the customer number as 'from' for display consistency
 | 
			
		||||
                $from_number = $call->to; // Customer number
 | 
			
		||||
                $to_number = $call->from; // Browser phone client
 | 
			
		||||
                error_log("TWP: Outbound browser call - Customer: {$call->to}, Agent: {$call->from}");
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            $insert_result = $wpdb->insert($recordings_table, [
 | 
			
		||||
                'call_sid' => $call_sid,
 | 
			
		||||
                'recording_sid' => $recording->sid,
 | 
			
		||||
                'from_number' => $call->from,
 | 
			
		||||
                'to_number' => $call->to,
 | 
			
		||||
                'from_number' => $from_number,
 | 
			
		||||
                'to_number' => $to_number,
 | 
			
		||||
                'agent_id' => $user_id,
 | 
			
		||||
                'status' => 'recording',
 | 
			
		||||
                'started_at' => current_time('mysql')
 | 
			
		||||
 
 | 
			
		||||
@@ -159,6 +159,23 @@ class TWP_Webhooks {
 | 
			
		||||
                ),
 | 
			
		||||
            ));
 | 
			
		||||
            
 | 
			
		||||
            // Call recording audio proxy endpoint
 | 
			
		||||
            register_rest_route('twilio-webhook/v1', '/recording-audio/(?P<id>\d+)', array(
 | 
			
		||||
                'methods' => 'GET',
 | 
			
		||||
                'callback' => array($this, 'proxy_recording_audio'),
 | 
			
		||||
                'permission_callback' => function() {
 | 
			
		||||
                    // Check if user is logged in with proper permissions
 | 
			
		||||
                    return is_user_logged_in() && current_user_can('manage_options');
 | 
			
		||||
                },
 | 
			
		||||
                'args' => array(
 | 
			
		||||
                    'id' => array(
 | 
			
		||||
                        'validate_callback' => function($param, $request, $key) {
 | 
			
		||||
                            return is_numeric($param);
 | 
			
		||||
                        }
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ));
 | 
			
		||||
            
 | 
			
		||||
            // Transcription webhook
 | 
			
		||||
            register_rest_route('twilio-webhook/v1', '/transcription', array(
 | 
			
		||||
                'methods' => 'POST',
 | 
			
		||||
@@ -1175,6 +1192,60 @@ class TWP_Webhooks {
 | 
			
		||||
        exit;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Proxy call recording audio through WordPress
 | 
			
		||||
     */
 | 
			
		||||
    public function proxy_recording_audio($request) {
 | 
			
		||||
        // Permission already checked by REST API permission_callback
 | 
			
		||||
        $recording_id = intval($request->get_param('id'));
 | 
			
		||||
        
 | 
			
		||||
        global $wpdb;
 | 
			
		||||
        $table_name = $wpdb->prefix . 'twp_call_recordings';
 | 
			
		||||
        
 | 
			
		||||
        $recording = $wpdb->get_row($wpdb->prepare(
 | 
			
		||||
            "SELECT recording_url FROM $table_name WHERE id = %d",
 | 
			
		||||
            $recording_id
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        if (!$recording || !$recording->recording_url) {
 | 
			
		||||
            header('HTTP/1.0 404 Not Found');
 | 
			
		||||
            exit('Recording not found');
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Fetch the audio from Twilio using authenticated request
 | 
			
		||||
        $twilio = new TWP_Twilio_API();
 | 
			
		||||
        $account_sid = get_option('twp_twilio_account_sid');
 | 
			
		||||
        $auth_token = get_option('twp_twilio_auth_token');
 | 
			
		||||
        
 | 
			
		||||
        // Add .mp3 to the URL if not present
 | 
			
		||||
        $audio_url = $recording->recording_url;
 | 
			
		||||
        if (strpos($audio_url, '.mp3') === false && strpos($audio_url, '.wav') === false) {
 | 
			
		||||
            $audio_url .= '.mp3';
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Fetch audio with authentication
 | 
			
		||||
        $response = wp_remote_get($audio_url, array(
 | 
			
		||||
            'headers' => array(
 | 
			
		||||
                'Authorization' => 'Basic ' . base64_encode($account_sid . ':' . $auth_token)
 | 
			
		||||
            ),
 | 
			
		||||
            'timeout' => 30
 | 
			
		||||
        ));
 | 
			
		||||
        
 | 
			
		||||
        if (is_wp_error($response)) {
 | 
			
		||||
            return new WP_Error('fetch_error', 'Unable to fetch recording', array('status' => 500));
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        $body = wp_remote_retrieve_body($response);
 | 
			
		||||
        $content_type = wp_remote_retrieve_header($response, 'content-type') ?: 'audio/mpeg';
 | 
			
		||||
        
 | 
			
		||||
        // Return audio with proper headers
 | 
			
		||||
        header('Content-Type: ' . $content_type);
 | 
			
		||||
        header('Content-Length: ' . strlen($body));
 | 
			
		||||
        header('Cache-Control: private, max-age=3600');
 | 
			
		||||
        echo $body;
 | 
			
		||||
        exit;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle voicemail callback
 | 
			
		||||
     */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user