Phone Exchange C# Code Snippet

using System;
using System.Threading.Tasks;
using GPPL2Service;

namespace geophone_plus_2_dot_net.SOAP
{
    /// <summary>
    /// Provides functionality to call the ServiceObjects GeoPhone Plus 2 SOAP service's GetPhoneInfo operation,
    /// retrieving reverse phone lookup information (e.g., provider, contacts, SMS/MMS addresses) with fallback to a backup endpoint
    /// for reliability in live mode.
    /// </summary>
    public class GetPhoneInfoValidation
    {
        private const string LiveBaseUrl = "https://sws.serviceobjects.com/GPPL2/api.svc/GeoPhonePlusSoap";
        private const string BackupBaseUrl = "https://swsbackup.serviceobjects.com/GPPL2/api.svc/GeoPhonePlusSoap";
        private const string TrialBaseUrl = "https://trial.serviceobjects.com/GPPL2/api.svc/GeoPhonePlusSoap";

        private readonly string _primaryUrl;
        private readonly string _backupUrl;
        private readonly int _timeoutMs;
        private readonly bool _isLive;

        /// <summary>
        /// Initializes URLs/timeout/IsLive.
        /// </summary>
        public GetPhoneInfoValidation(bool isLive)
        {
            _timeoutMs = 10000;
            _isLive = isLive;

            _primaryUrl = isLive ? LiveBaseUrl : TrialBaseUrl;
            _backupUrl = isLive ? BackupBaseUrl : TrialBaseUrl;

            if (string.IsNullOrWhiteSpace(_primaryUrl))
                throw new InvalidOperationException("Primary URL not set.");
            if (string.IsNullOrWhiteSpace(_backupUrl))
                throw new InvalidOperationException("Backup URL not set.");
        }

        /// <summary>
        /// This is the basic operation for finding the reverse-lookup information.Given a phone number and test type, it will consult national directory-assistance databases to find the owner and address registered.The addresses returned are validated via third party address-validation technique. They are returned to you exactly as the phone carrier releases them.If you need these addresses to be validated, using Service Objects’ AddressValidation web services is highly recommended.Both the contact’s information and the phone company’s information are returned with this operation. Two valuable bits of information are also retrieved – whether the phone line is for business or residential purposes, and whether the phone line is landline or wireless.By examining the WSDL, you may see that multiple groups of contact/exchange information are possible. Although they are possible in the XML, you will only see one exchange per output, always.It is common, however, to see multiple contacts per phone number (as people change numbers, or there may be multiple businesses at the same phone number.) It is highly recommended that you handle each of these contacts, rather than just the first contact returned.
        /// </summary>
        /// <param name="PhoneNumber">The 10 digit phone number</param>
        /// <param name="TestType">The type of validation to perform ("FULL", "BASIC", or "NORMAL").</param>
        /// <param name="LicenseKey">Your license key to use the service
        /// <returns>A <see cref="Task{PhoneInfoResponse}"/> containing a <see cref="PhoneInfoResponse"/> object with reverse phone lookup details or an error.</returns>
        /// <exception cref="Exception">Thrown if both primary and backup endpoints fail.</exception>
        public async Task<PhoneInfoResponse> GetPhoneInfo(string PhoneNumber, string TestType, string LicenseKey)
        {
            GeoPhonePlusClient clientPrimary = null;
            GeoPhonePlusClient clientBackup = null;

            try
            {
                // Attempt Primary_PLL
                clientPrimary = new GeoPhonePlusClient();
                clientPrimary.Endpoint.Address = new System.ServiceModel.EndpointAddress(_primaryUrl);
                clientPrimary.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);

                PhoneInfoResponse response = await clientPrimary.GetPhoneInfoAsync(
                    PhoneNumber, TestType, LicenseKey).ConfigureAwait(false);

                if (_isLive && !IsValid(response))
                {
                    throw new InvalidOperationException("Primary endpoint returned null or a fatal TypeCode=3 error for GetPhoneInfo");
                }
                return response;
            }
            catch (Exception primaryEx)
            {

                try
                {
                    clientBackup = new GeoPhonePlusClient();
                    clientBackup.Endpoint.Address = new System.ServiceModel.EndpointAddress(_backupUrl);
                    clientBackup.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(_timeoutMs);

                    return await clientBackup.GetPhoneInfoAsync(
                        PhoneNumber, TestType, LicenseKey).ConfigureAwait(false);
                }
                catch (Exception backupEx)
                {
                    throw new Exception(
                        $"Both primary and backup endpoints failed.\n" +
                        $"Primary error: {primaryEx.Message}\n" +
                        $"Backup error: {backupEx.Message}");
                }
                finally
                {
                    clientBackup?.Close();
                }
            }
            finally
            {
                clientPrimary?.Close();
            }
        }
        private static bool IsValid(PhoneInfoResponse response) => response?.Error == null || response.Error.TypeCode != "3";
    }
}

Phone Exchange Python Code Snippet

from suds.client import Client
from suds import WebFault
from suds.sudsobject import Object

class GetPhoneInfoSoap:

    def __init__(self, license_key: str, is_live: bool, timeout_ms: int = 10000):
        """
        license_key: Service Objects GPPL2 license key.
        is_live: Whether to use live or trial endpoints
        timeout_ms: SOAP call timeout in milliseconds
        """

        self._timeout_s = timeout_ms / 1000.0  # Convert to seconds
        self._is_live = is_live
        self.license_key = license_key

        # WSDL URLs for primary and backup endpoints
        self._primary_wsdl = (
            "https://sws.serviceobjects.com/GPPL2/api.svc?wsdl"
            if is_live else
            "https://trial.serviceobjects.com/GPPL2/api.svc?wsdl"
        )
        self._backup_wsdl = (
            "https://swsbackup.serviceobjects.com/GPPL2/api.svc?wsdl"
            if is_live else
            "https://trial.serviceobjects.com/GPPL2/api.svc?wsdl"
        )

    def get_phone_info(self, phone_number: str, test_type: str) -> Object:
        """
        Calls the GeoPhone Plus 2 GetPhoneInfo SOAP API to retrieve the information.

        Parameters:
        phone_number (str): The 10 digit phone number.
        test_type (str): The type of validation to perform ('FULL', 'BASIC', or 'NORMAL').
        license_key: Service Objects GeoPhone license key.
        is_live: whether to use live or trial endpoints
        timeout_ms: SOAP call timeout in milliseconds

        Returns:
            Object: Parsed SOAP response with phone information or error details.
        """

        # Common kwargs for both calls
        call_kwargs = dict(
            PhoneNumber= phone_number,
            TestType= test_type,
            LicenseKey= self.license_key
        )

        # Attempt primary
        try:
            client = Client(self._primary_wsdl, timeout=self._timeout_s)
            # Override endpoint URL if needed:
            # client.set_options(location=self._primary_wsdl.replace('?wsdl','/soap'))
            response = client.service.GetPhoneInfo(**call_kwargs)

            # If response is None or fatal error code, trigger fallback
            if response is None or (hasattr(response, 'Error') and response.Error and response.Error.TypeCode == '3'):
                raise ValueError("Primary returned no result or fatal Error.TypeCode=3")

            return response

        except (WebFault, ValueError, Exception) as primary_ex:
                try:
                    client = Client(self._backup_wsdl, timeout=self._timeout_s)
                    response = client.service.GetPhoneInfo(**call_kwargs)

                    if response is None:
                        raise ValueError("Backup returned no result")

                    return response

                except (WebFault, Exception) as backup_ex:
                    # Raise a combined error if both attempts fail
                    msg = (
                        "Both primary and backup endpoints failed.\n"
                        f"Primary error: {str(primary_ex)}\n"
                        f"Backup error: {str(backup_ex)}"
                    )
                    raise RuntimeError(msg)
           

Phone Exchange NodeJS Code Snippet

import { soap } from 'strong-soap';

/**
 * <summary>
 * A class that provides functionality to call the ServiceObjects GeoPhone Plus 2 SOAP service's GetPhoneInfo endpoint,
 * retrieving reverse phone lookup information with fallback to a backup endpoint for reliability in live mode.
 * </summary>
 */
class GetPhoneInfoSoap {
    /**
     * <summary>
     * Initializes a new instance of the GetPhoneInfoSoap class with the provided input parameters,
     * setting up primary and backup WSDL URLs based on the live/trial mode.
     * </summary>
     * @param {string} phoneNumber - The 10 digit phone number.
     * @param {string} testType - The type of validation to perform ('FULL', 'BASIC', or 'NORMAL').
     * @param {string} licenseKey - Your license key to use the service. 
     * @param {boolean} isLive - Value to determine whether to use the live or trial servers.
     * @param {number} timeoutSeconds - Timeout, in seconds, for the call to the service.
     * @throws {Error} Thrown if LicenseKey is empty or null.
     */
    constructor(PhoneNumber, TestType, LicenseKey, isLive = true, timeoutSeconds = 15) {

        this.args = {
            PhoneNumber,
            TestType,
            LicenseKey
        };

        this.isLive = isLive;
        this.timeoutSeconds = timeoutSeconds;

        this.liveBaseUrl = "https://sws.serviceobjects.com/GPPL2/api.svc?singlewsdl";
        this.backupBaseUrl = "https://swsbackup.serviceobjects.com/GPPL2/api.svc?singlewsdl";
        this.trialBaseUrl = "https://trial.serviceobjects.com/GPPL2/api.svc?singlewsdl";

        this._primaryWsdl = this.isLive ? this.liveBaseUrl : this.trialBaseUrl;
        this._backupWsdl = this.isLive ? this.backupBaseUrl : this.trialBaseUrl;
    }

    /**
     * <summary>
     * Asynchronously calls the GetPhoneInfo SOAP endpoint, attempting the primary endpoint
     * first and falling back to the backup if the response is invalid (Error.TypeCode == '3') in live mode
     * or if the primary call fails.
     * </summary>
     * <returns type="Promise<GPPL2Response>">A promise that resolves to a GPPL2Response object containing reverse phone lookup details or an error.</returns>
     * <exception cref="Error">Thrown if both primary and backup calls fail, with detailed error messages.</exception>
     */
    async getPhoneInfo() {
        try {
            const primaryResult = await this._callSoap(this._primaryWsdl, this.args);

            if (this.isLive && !this._isValid(primaryResult)) {
                console.warn("Primary returned Error.TypeCode == '3', falling back to backup...");
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            }

            return primaryResult;
        } catch (primaryErr) {
            try {
                const backupResult = await this._callSoap(this._backupWsdl, this.args);
                return backupResult;
            } catch (backupErr) {
                throw new Error(`Both primary and backup calls failed:\nPrimary: ${primaryErr.message}\nBackup: ${backupErr.message}`);
            }

        }
    }

    /**
     * <summary>
     * Performs a SOAP service call to the specified WSDL URL with the given arguments,
     * creating a client and processing the response into a GPPL2Response object.
     * </summary>
     * <param name="wsdlUrl" type="string">The WSDL URL of the SOAP service endpoint (primary or backup).</param>
     * <param name="args" type="Object">The arguments to pass to the GetPhoneInfo method.</param>
     * <returns type="Promise<GPPL2Response>">A promise that resolves to a GPPL2Response object containing the SOAP response data.</returns>
     * <exception cref="Error">Thrown if the SOAP client creation fails, the service call fails, or the response cannot be parsed.</exception>
     */
    _callSoap(wsdlUrl, args) {
        return new Promise((resolve, reject) => {
            soap.createClient(wsdlUrl, { timeout: this.timeoutSeconds * 1000 }, (err, client) => {
                if (err) return reject(err);

                client.GetPhoneInfo(args, (err, result) => {
                    const rawData = result?.GetPhoneInfoResult;
                    try {
                        if (!rawData) {
                            return reject(new Error("SOAP response is empty or undefined."));
                        }
                        resolve(rawData);
                    } catch (parseErr) {
                        reject(new Error(`Failed to parse SOAP response: ${parseErr.message}`));
                    }
                });
            });
        });
    }

    /**
     * <summary>
     * Checks if a SOAP response is valid by verifying that it exists and either has no Error object
     * or the Error.TypeCode is not equal to '1'.
     * </summary>
     * <param name="response" type="GPPL2Response">The GPPL2Response object to validate.</param>
     * <returns type="boolean">True if the response is valid, false otherwise.</returns>
     */
    _isValid(response) {
        return response && (!response.Error || response.Error.TypeCode !== "3");
    }
}

export { GetPhoneInfoSoap };