2025-08-06 15:25:47 -07:00
< ? php
/**
2025-08-07 15:24:29 -07:00
* Twilio API integration class using official Twilio PHP SDK
2025-08-06 15:25:47 -07:00
*/
class TWP_Twilio_API {
2025-08-07 15:24:29 -07:00
private $client ;
2025-08-06 15:25:47 -07:00
private $phone_number ;
/**
* Constructor
*/
public function __construct () {
2025-08-07 15:24:29 -07:00
$this -> init_sdk_client ();
2025-08-11 20:31:48 -07:00
// Try to get the SMS notification number first, or get the first available Twilio number
$this -> phone_number = get_option ( 'twp_sms_notification_number' );
// If no SMS number configured, try to get the first phone number from the account
if ( empty ( $this -> phone_number )) {
$this -> phone_number = $this -> get_default_phone_number ();
}
}
/**
* Get the default phone number from the account
*/
private function get_default_phone_number () {
try {
// Get the first phone number from the account
$numbers = $this -> client -> incomingPhoneNumbers -> read ([], 1 );
if ( ! empty ( $numbers )) {
return $numbers [ 0 ] -> phoneNumber ;
}
} catch ( \Exception $e ) {
error_log ( 'TWP: Unable to get default phone number: ' . $e -> getMessage ());
}
return null ;
2025-08-06 15:25:47 -07:00
}
/**
2025-08-07 15:24:29 -07:00
* Initialize Twilio SDK client
2026-05-05 08:48:45 -07:00
*
* Loader priority :
* 1. Internal bundled vendor / autoload . php ( shipped with release zip — primary )
* 2. External wp - content / twilio - sdk / autoload . php ( legacy / manual install fallback )
2025-08-06 15:25:47 -07:00
*/
2025-08-07 15:24:29 -07:00
private function init_sdk_client () {
2026-01-23 18:03:38 -08:00
$autoloader_path = null ;
2026-05-05 08:48:45 -07:00
// Priority 1: Internal bundled vendor (shipped via release zip)
$internal_autoloader = TWP_PLUGIN_DIR . 'vendor/autoload.php' ;
if ( file_exists ( $internal_autoloader )) {
$autoloader_path = $internal_autoloader ;
2025-08-07 15:24:29 -07:00
}
2026-01-23 18:03:38 -08:00
2026-05-05 08:48:45 -07:00
// Priority 2: External legacy SDK location
2026-01-23 18:03:38 -08:00
if ( ! $autoloader_path ) {
2026-05-05 08:48:45 -07:00
$external_autoloader = TWP_EXTERNAL_SDK_DIR . 'autoload.php' ;
if ( file_exists ( $external_autoloader )) {
$autoloader_path = $external_autoloader ;
2026-01-23 18:03:38 -08:00
}
}
if ( ! $autoloader_path ) {
2026-05-05 08:48:45 -07:00
error_log ( 'TWP Plugin: Autoloader not found. Checked: ' . TWP_PLUGIN_DIR . 'vendor/autoload.php and ' . TWP_EXTERNAL_SDK_DIR . 'autoload.php' );
throw new Exception ( 'Twilio SDK not found. Reinstall or update the plugin (release zips bundle the SDK), or run ./install-twilio-sdk-external.sh as a fallback.' );
2026-01-23 18:03:38 -08:00
}
2025-08-07 15:24:29 -07:00
// Load the autoloader
require_once $autoloader_path ;
// Give more detailed error information
if ( ! class_exists ( 'Twilio\Rest\Client' )) {
$sdk_path = TWP_PLUGIN_DIR . 'vendor/twilio/sdk' ;
$client_file = $sdk_path . '/Rest/Client.php' ;
error_log ( 'TWP Plugin: Twilio SDK classes not found.' );
error_log ( 'TWP Plugin: Looking for SDK at: ' . $sdk_path );
error_log ( 'TWP Plugin: Client.php exists: ' . ( file_exists ( $client_file ) ? 'YES' : 'NO' ));
error_log ( 'TWP Plugin: SDK directory contents: ' . print_r ( scandir ( $sdk_path ), true ));
throw new Exception ( 'Twilio SDK classes not available. Please reinstall with: ./install-twilio-sdk.sh' );
}
2025-08-06 15:25:47 -07:00
2025-08-07 15:24:29 -07:00
$account_sid = get_option ( 'twp_twilio_account_sid' );
$auth_token = get_option ( 'twp_twilio_auth_token' );
if ( empty ( $account_sid ) || empty ( $auth_token )) {
throw new Exception ( 'Twilio credentials not configured. Please check your WordPress admin settings.' );
2025-08-06 15:25:47 -07:00
}
2025-08-07 15:24:29 -07:00
try {
$this -> client = new \Twilio\Rest\Client ( $account_sid , $auth_token );
error_log ( 'TWP Plugin: Twilio SDK initialized successfully' );
} catch ( Exception $e ) {
error_log ( 'TWP Plugin: Failed to initialize Twilio client: ' . $e -> getMessage ());
throw new Exception ( 'Failed to initialize Twilio SDK: ' . $e -> getMessage ());
}
}
/**
* Make a phone call
*/
public function make_call ( $to_number , $twiml_url , $status_callback = null , $from_number = null ) {
try {
$params = [
'url' => $twiml_url ,
'from' => $from_number ? : $this -> phone_number ,
'to' => $to_number
];
if ( $status_callback ) {
$params [ 'statusCallback' ] = $status_callback ;
$params [ 'statusCallbackEvent' ] = [ 'initiated' , 'ringing' , 'answered' , 'completed' ];
2025-08-11 20:31:48 -07:00
$params [ 'statusCallbackMethod' ] = 'POST' ;
$params [ 'timeout' ] = 20 ; // Ring for 20 seconds before giving up
$params [ 'machineDetection' ] = 'Enable' ; // Detect if voicemail answers
$params [ 'machineDetectionTimeout' ] = 30 ; // Wait 30 seconds to detect machine
2025-08-07 15:24:29 -07:00
}
$call = $this -> client -> calls -> create (
$to_number ,
$from_number ? : $this -> phone_number ,
$params
);
return [
'success' => true ,
'data' => [
'sid' => $call -> sid ,
'status' => $call -> status ,
'from' => $call -> from ,
'to' => $call -> to ,
'direction' => $call -> direction ,
'price' => $call -> price ,
'priceUnit' => $call -> priceUnit
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
/**
* Forward a call
*/
public function forward_call ( $call_sid , $to_number ) {
2025-08-07 15:24:29 -07:00
try {
$twiml = new \Twilio\TwiML\VoiceResponse ();
$twiml -> dial ( $to_number );
$call = $this -> client -> calls ( $call_sid ) -> update ([
'twiml' => $twiml -> asXML ()
]);
return [
'success' => true ,
'data' => [
'sid' => $call -> sid ,
'status' => $call -> status
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
/**
* Update an active call
*/
public function update_call ( $call_sid , $params ) {
2025-08-07 15:24:29 -07:00
try {
$call = $this -> client -> calls ( $call_sid ) -> update ( $params );
return [
'success' => true ,
'data' => [
'sid' => $call -> sid ,
'status' => $call -> status ,
'from' => $call -> from ,
'to' => $call -> to
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
/**
* Get call details
*/
public function get_call ( $call_sid ) {
2025-08-07 15:24:29 -07:00
try {
$call = $this -> client -> calls ( $call_sid ) -> fetch ();
return [
'success' => true ,
'data' => [
'sid' => $call -> sid ,
'status' => $call -> status ,
'from' => $call -> from ,
'to' => $call -> to ,
'direction' => $call -> direction ,
'duration' => $call -> duration ,
'price' => $call -> price ,
'priceUnit' => $call -> priceUnit
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
2025-09-02 11:03:33 -07:00
/**
* Get call status only
*/
public function get_call_status ( $call_sid ) {
$call_result = $this -> get_call ( $call_sid );
if ( $call_result [ 'success' ]) {
return $call_result [ 'data' ][ 'status' ];
}
throw new Exception ( 'Could not retrieve call status: ' . ( $call_result [ 'error' ] ? ? 'Unknown error' ));
}
2025-08-06 15:25:47 -07:00
/**
* Create TwiML for queue
*/
public function create_queue_twiml ( $queue_name , $wait_url = null , $wait_message = null ) {
2025-08-07 15:24:29 -07:00
try {
$response = new \Twilio\TwiML\VoiceResponse ();
if ( $wait_message ) {
$response -> say ( $wait_message , [ 'voice' => 'alice' ]);
}
2025-09-02 11:03:33 -07:00
// Pass waitUrl as an option to enqueue if provided
$enqueue_options = [];
2025-08-07 15:24:29 -07:00
if ( $wait_url ) {
2025-09-02 11:03:33 -07:00
$enqueue_options [ 'waitUrl' ] = $wait_url ;
2025-08-07 15:24:29 -07:00
}
2025-09-02 11:03:33 -07:00
$response -> enqueue ( $queue_name , $enqueue_options );
2025-08-07 15:24:29 -07:00
return $response -> asXML ();
} catch ( Exception $e ) {
error_log ( 'TWP Plugin: Failed to create queue TwiML: ' . $e -> getMessage ());
throw $e ;
2025-08-06 15:25:47 -07:00
}
}
/**
* Create TwiML for IVR menu
*/
public function create_ivr_twiml ( $message , $options = array ()) {
2025-08-07 15:24:29 -07:00
try {
$response = new \Twilio\TwiML\VoiceResponse ();
$gather = $response -> gather ([
'numDigits' => 1 ,
'timeout' => 10 ,
'action' => isset ( $options [ 'action_url' ]) ? $options [ 'action_url' ] : null
]);
$gather -> say ( $message , [ 'voice' => 'alice' ]);
if ( ! empty ( $options [ 'no_input_message' ])) {
$response -> say ( $options [ 'no_input_message' ], [ 'voice' => 'alice' ]);
}
return $response -> asXML ();
} catch ( Exception $e ) {
error_log ( 'TWP Plugin: Failed to create IVR TwiML: ' . $e -> getMessage ());
throw $e ;
2025-08-06 15:25:47 -07:00
}
}
/**
2025-10-21 11:13:54 -07:00
* Send SMS ( uses configured SMS provider - Twilio or Amazon SNS )
2025-08-06 15:25:47 -07:00
*/
2025-08-07 15:24:29 -07:00
public function send_sms ( $to_number , $message , $from_number = null ) {
2025-10-21 11:13:54 -07:00
// Use SMS Manager to handle provider abstraction
require_once dirname ( __FILE__ ) . '/class-twp-sms-manager.php' ;
return TWP_SMS_Manager :: send_sms ( $to_number , $message , $from_number );
2025-08-06 15:25:47 -07:00
}
/**
* Get available phone numbers
*/
public function get_phone_numbers () {
2025-08-07 15:24:29 -07:00
try {
$numbers = $this -> client -> incomingPhoneNumbers -> read ([], 50 );
$numbers_data = [];
foreach ( $numbers as $number ) {
$numbers_data [] = [
'sid' => $number -> sid ? : '' ,
2025-08-07 16:46:51 -07:00
'phone_number' => $number -> phoneNumber ? : '' ,
'friendly_name' => $number -> friendlyName ? : $number -> phoneNumber ? : 'Unknown' ,
'voice_url' => $number -> voiceUrl ? : '' ,
'sms_url' => $number -> smsUrl ? : '' ,
2025-08-11 20:31:48 -07:00
'status_callback_url' => $number -> statusCallback ? : '' ,
2025-08-07 15:24:29 -07:00
'capabilities' => [
'voice' => $number -> capabilities ? ( bool ) $number -> capabilities -> getVoice () : false ,
'sms' => $number -> capabilities ? ( bool ) $number -> capabilities -> getSms () : false ,
'mms' => $number -> capabilities ? ( bool ) $number -> capabilities -> getMms () : false
]
];
}
return [
'success' => true ,
'data' => [
'incoming_phone_numbers' => $numbers_data
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
/**
* Search for available phone numbers
*/
public function search_available_numbers ( $country_code = 'US' , $area_code = null , $contains = null , $limit = 20 ) {
2025-08-07 15:24:29 -07:00
try {
$params = [ 'limit' => $limit ];
if ( $area_code ) {
$params [ 'areaCode' ] = $area_code ;
}
if ( $contains ) {
$params [ 'contains' ] = $contains ;
}
$numbers = $this -> client -> availablePhoneNumbers ( $country_code )
-> local
-> read ( $params , $limit );
$numbers_data = [];
foreach ( $numbers as $number ) {
$numbers_data [] = [
2025-08-07 16:46:51 -07:00
'phone_number' => $number -> phoneNumber ? : '' ,
'friendly_name' => $number -> friendlyName ? : $number -> phoneNumber ? : 'Available Number' ,
2025-08-07 15:24:29 -07:00
'locality' => $number -> locality ? : '' ,
'region' => $number -> region ? : '' ,
2025-08-07 16:46:51 -07:00
'postal_code' => $number -> postalCode ? : '' ,
2025-08-07 15:24:29 -07:00
'capabilities' => [
'voice' => $number -> capabilities ? ( bool ) $number -> capabilities -> getVoice () : false ,
'sms' => $number -> capabilities ? ( bool ) $number -> capabilities -> getSms () : false ,
'mms' => $number -> capabilities ? ( bool ) $number -> capabilities -> getMms () : false
]
];
}
return [
'success' => true ,
'data' => [
'available_phone_numbers' => $numbers_data
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
2025-08-06 15:25:47 -07:00
}
}
/**
* Purchase a phone number
*/
public function purchase_phone_number ( $phone_number , $voice_url = null , $sms_url = null ) {
2025-08-07 15:24:29 -07:00
try {
$params = [ 'phoneNumber' => $phone_number ];
if ( $voice_url ) {
$params [ 'voiceUrl' ] = $voice_url ;
$params [ 'voiceMethod' ] = 'POST' ;
}
if ( $sms_url ) {
$params [ 'smsUrl' ] = $sms_url ;
$params [ 'smsMethod' ] = 'POST' ;
}
2025-08-11 20:31:48 -07:00
// Add status callback for real-time call state tracking
$status_callback_url = home_url ( '/wp-json/twilio-webhook/v1/status' );
$params [ 'statusCallback' ] = $status_callback_url ;
$params [ 'statusCallbackMethod' ] = 'POST' ;
2025-08-07 15:24:29 -07:00
$number = $this -> client -> incomingPhoneNumbers -> create ( $params );
return [
'success' => true ,
'data' => [
'sid' => $number -> sid ,
2025-08-07 16:46:51 -07:00
'phone_number' => $number -> phoneNumber ,
'friendly_name' => $number -> friendlyName ,
'voice_url' => $number -> voiceUrl ,
'sms_url' => $number -> smsUrl
2025-08-07 15:24:29 -07:00
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
2025-08-06 15:25:47 -07:00
}
}
/**
* Release a phone number
*/
public function release_phone_number ( $phone_number_sid ) {
2025-08-07 15:24:29 -07:00
try {
$this -> client -> incomingPhoneNumbers ( $phone_number_sid ) -> delete ();
return [
'success' => true ,
'data' => [
'message' => 'Phone number released successfully'
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
}
2025-08-06 15:25:47 -07:00
}
/**
* Configure phone number webhook
*/
public function configure_phone_number ( $phone_sid , $voice_url , $sms_url = null ) {
2025-08-07 15:24:29 -07:00
try {
$params = [
'voiceUrl' => $voice_url ,
'voiceMethod' => 'POST'
];
if ( $sms_url ) {
$params [ 'smsUrl' ] = $sms_url ;
$params [ 'smsMethod' ] = 'POST' ;
}
2025-08-11 20:31:48 -07:00
// Add status callback for real-time call state tracking
$status_callback_url = home_url ( '/wp-json/twilio-webhook/v1/status' );
$params [ 'statusCallback' ] = $status_callback_url ;
$params [ 'statusCallbackMethod' ] = 'POST' ;
2025-08-07 15:24:29 -07:00
$number = $this -> client -> incomingPhoneNumbers ( $phone_sid ) -> update ( $params );
return [
'success' => true ,
'data' => [
'sid' => $number -> sid ,
2025-08-07 16:46:51 -07:00
'phone_number' => $number -> phoneNumber ,
'voice_url' => $number -> voiceUrl ,
'sms_url' => $number -> smsUrl
2025-08-07 15:24:29 -07:00
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage (),
'code' => $e -> getCode ()
];
2025-08-06 15:25:47 -07:00
}
}
/**
2025-08-07 15:24:29 -07:00
* Create TwiML helper - returns SDK VoiceResponse
2025-08-06 15:25:47 -07:00
*/
2025-08-07 15:24:29 -07:00
public function create_twiml () {
return new \Twilio\TwiML\VoiceResponse ();
}
/**
* Get the Twilio client instance
*/
public function get_client () {
return $this -> client ;
2025-08-06 15:25:47 -07:00
}
2025-08-11 20:31:48 -07:00
/**
* Get SMS from number with proper priority
*/
public static function get_sms_from_number ( $workflow_id = null ) {
// Priority 1: If we have a workflow_id, get the workflow's phone number
if ( $workflow_id ) {
$workflow = TWP_Workflow :: get_workflow ( $workflow_id );
if ( $workflow && ! empty ( $workflow -> phone_number )) {
return $workflow -> phone_number ;
}
}
// Priority 2: Use default SMS number setting
$default_sms_number = get_option ( 'twp_default_sms_number' );
if ( ! empty ( $default_sms_number )) {
return $default_sms_number ;
}
// Priority 3: Fall back to first available Twilio number
$twilio = new self ();
$phone_numbers = $twilio -> get_phone_numbers ();
if ( $phone_numbers [ 'success' ] && ! empty ( $phone_numbers [ 'data' ][ 'incoming_phone_numbers' ])) {
return $phone_numbers [ 'data' ][ 'incoming_phone_numbers' ][ 0 ][ 'phone_number' ];
}
return null ;
}
2025-08-06 15:25:47 -07:00
/**
* Validate webhook signature
*/
public function validate_webhook_signature ( $url , $params , $signature ) {
2025-08-07 15:24:29 -07:00
$validator = new \Twilio\Security\RequestValidator ( get_option ( 'twp_twilio_auth_token' ));
return $validator -> validate ( $signature , $url , $params );
2025-08-06 15:25:47 -07:00
}
2025-08-11 20:31:48 -07:00
/**
* Get call information from Twilio
*/
public function get_call_info ( $call_sid ) {
try {
$call = $this -> client -> calls ( $call_sid ) -> fetch ();
return [
'sid' => $call -> sid ,
'status' => $call -> status ,
'from' => $call -> from ,
'to' => $call -> to ,
'duration' => $call -> duration ,
'start_time' => $call -> startTime ? $call -> startTime -> format ( 'Y-m-d H:i:s' ) : null ,
'end_time' => $call -> endTime ? $call -> endTime -> format ( 'Y-m-d H:i:s' ) : null ,
'direction' => $call -> direction ,
'price' => $call -> price ,
'priceUnit' => $call -> priceUnit
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
error_log ( 'TWP: Error fetching call info for ' . $call_sid . ': ' . $e -> getMessage ());
return null ;
}
}
/**
* Toggle status callback for a specific phone number
*/
public function toggle_number_status_callback ( $phone_sid , $enable = true ) {
try {
$params = [];
if ( $enable ) {
$params [ 'statusCallback' ] = home_url ( '/wp-json/twilio-webhook/v1/status' );
$params [ 'statusCallbackMethod' ] = 'POST' ;
} else {
// Clear the status callback
$params [ 'statusCallback' ] = '' ;
}
$number = $this -> client -> incomingPhoneNumbers ( $phone_sid ) -> update ( $params );
return [
'success' => true ,
'data' => [
'sid' => $number -> sid ,
'phone_number' => $number -> phoneNumber ,
'status_callback' => $number -> statusCallback ,
'enabled' => ! empty ( $number -> statusCallback )
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage ()
];
}
}
/**
* Update all existing phone numbers to include status callbacks
*/
public function enable_status_callbacks_for_all_numbers () {
try {
$numbers = $this -> get_phone_numbers ();
if ( ! $numbers [ 'success' ]) {
return [
'success' => false ,
'error' => 'Failed to retrieve phone numbers: ' . $numbers [ 'error' ]
];
}
$status_callback_url = home_url ( '/wp-json/twilio-webhook/v1/status' );
$updated_count = 0 ;
$errors = [];
foreach ( $numbers [ 'data' ][ 'incoming_phone_numbers' ] as $number ) {
try {
$this -> client -> incomingPhoneNumbers ( $number [ 'sid' ]) -> update ([
'statusCallback' => $status_callback_url ,
'statusCallbackMethod' => 'POST'
]);
$updated_count ++ ;
error_log ( 'TWP: Added status callback to phone number: ' . $number [ 'phone_number' ]);
} catch ( \Twilio\Exceptions\TwilioException $e ) {
$errors [] = 'Failed to update ' . $number [ 'phone_number' ] . ': ' . $e -> getMessage ();
error_log ( 'TWP: Error updating phone number ' . $number [ 'phone_number' ] . ': ' . $e -> getMessage ());
}
}
return [
'success' => true ,
'data' => [
'updated_count' => $updated_count ,
'total_numbers' => count ( $numbers [ 'data' ][ 'incoming_phone_numbers' ]),
'errors' => $errors
]
];
} catch ( \Twilio\Exceptions\TwilioException $e ) {
return [
'success' => false ,
'error' => $e -> getMessage ()
];
}
}
2025-08-12 07:05:47 -07:00
/**
* Generate capability token for Browser Phone
*/
public function generate_capability_token ( $client_name = null ) {
$account_sid = get_option ( 'twp_twilio_account_sid' );
$auth_token = get_option ( 'twp_twilio_auth_token' );
$twiml_app_sid = get_option ( 'twp_twiml_app_sid' );
if ( empty ( $account_sid ) || empty ( $auth_token )) {
return [
'success' => false ,
'error' => 'Twilio credentials not configured'
];
}
if ( empty ( $twiml_app_sid )) {
return [
'success' => false ,
'error' => 'TwiML App SID not configured. Please set up a TwiML App in your Twilio Console.'
];
}
try {
// Create client name if not provided
if ( ! $client_name ) {
$current_user = wp_get_current_user ();
2025-08-13 10:15:39 -07:00
// Twilio requires alphanumeric characters only - remove all non-alphanumeric
2025-09-02 11:03:33 -07:00
// Use user_login for consistency across all client name generation
$clean_name = preg_replace ( '/[^a-zA-Z0-9]/' , '' , $current_user -> user_login );
2025-08-13 10:15:39 -07:00
if ( empty ( $clean_name )) {
$clean_name = 'user' ;
}
$client_name = 'agent' . $current_user -> ID . $clean_name ;
2025-08-12 07:05:47 -07:00
}
$capability = new \Twilio\Jwt\ClientToken ( $account_sid , $auth_token );
$capability -> allowClientOutgoing ( $twiml_app_sid );
$capability -> allowClientIncoming ( $client_name );
$token = $capability -> generateToken ( 3600 ); // Valid for 1 hour
return [
'success' => true ,
'data' => [
'token' => $token ,
'client_name' => $client_name ,
'expires_in' => 3600
]
];
} catch ( \Exception $e ) {
return [
'success' => false ,
'error' => 'Failed to generate capability token: ' . $e -> getMessage ()
];
}
}
2025-08-06 15:25:47 -07:00
}