当用户尝试在 laravel 中使用 nexmo 呼叫 phone 号码时验证用户

Validate user when they try to call phone number with nexmo in laravel

我有客户服务应用程序使用 nexmo 和 laravel 作为框架在应用程序浏览器调用中使用。我有一些用户,每个用户在 table 中都有他们的列表 phone 号码,每一行都有联系操作按钮来调用。 phone 数字的目标是这样放在按钮中的:

<a href="#" class="btn btn-sm btn-info call-button" data-toggle="modal" data-number="6281299054899">Contact</a>

但用户可以检查元素并编辑这些按钮,然后输入他们想要的另一个 phone 数字。我如何防止这种作弊并在示例中提醒用户“您未授权呼叫此 phone 号码。”他们什么时候做那个动作?

这是我在单击 .call-button 时的处理程序:

dataTableAdminLeader.on('click', '.call-button', function(){
    let id                  = $(this).data('id');
    let master_number_id    = $(this).data('master_numbers_id');
    let target_number       = $(this).data('number');
    const trashButton       = document.querySelector('.btn-trash');
    const updateButton      = document.querySelector(".btn-update");

    trashButton.setAttribute('disabled', 'disabled');
    updateButton.setAttribute('disabled', 'disabled');

    $('#phone-call-modal').modal('show');
    // input reset
    document.querySelector('#target-phone').value = target_number;
    document.querySelector('#id-edit').value = id;
    document.querySelector('#master-numbers-id-edit').value = master_number_id;
    document.querySelector('#member-id').value = $(this).data('id-member');
    document.querySelector('#member-name').value = $(this).data('name');

    trashButton.removeAttribute('disabled');
    updateButton.removeAttribute('disabled');

    // all constant needed for call
    const USER_JWT          = "{{ $jwt_token }}";
    const phoneNumberInput  = document.querySelector("#cs-phone");
    const statusElement     = document.querySelector("#status-call");
    // button object selector
    const callButton        = document.querySelector(".btn-call");
    callButton.setAttribute('disabled', 'disabled');
    const hangupButton      = document.querySelector(".btn-hangup");
    const closeButton       = document.querySelector('.btn-close');

    closeButton.style.removeProperty('display');
    const statusButton      = document.querySelector('.btn-status');
    // input object selector
    let campaign_result     = document.querySelector('#campaignresult');
    let note_contacted      = document.querySelector('#note_contacted');
    let note_container      = document.querySelector('.note_container');
    let nameContainer       = document.querySelector('.name-container');
    let campaignContainer   = document.querySelector('.campaign-container');
    let waContainer         = document.querySelector('.wa-container');
    let inputCallStatus     = document.querySelector('#call-status');
    // call status check
    let callStatusCompleted = false;
    let callStatusAnswered  = false;
    // reset property
    campaign_result.value   = "";
    note_container.style    = 'display: none';
    nameContainer.style     = 'display: none';

    // sound object
    var sndAnswered = new Audio("{{ asset('storage/sounds/answered.wav') }}");

    // listening to event
    campaign_result.addEventListener('change', function(){
      if(campaign_result.value != ''){
        note_container.style.removeProperty('display');
        note_contacted.setAttribute('required', 'required');
      }else{
        note_container.style = 'display: none';
        note_contacted.removeAttribute('required');
      }
    });
    // nexmo status reset
    statusElement.innerText = '';
    inputCallStatus.value = '';
    // nexmo call button reset
    callButton.style.display = "inline";
    hangupButton.style.display = "none";
    // timeouts set
    setTimeout(() => {
      callButton.removeAttribute('disabled');
    }, 5000);
    // nexmo object start
    new NexmoClient({ debug: true }).login(USER_JWT).then(app => {
      callButton.addEventListener("click", event => {
        event.preventDefault();
        let number = String(target_number);
        console.log(number);
        if (number !== ""){
          app.callServer(number).catch(function(error){
            console.log('debug: ',error);
          });
        } else {
          statusElement.innerText = 'Please enter your phone number.';
        }
      });
      app.on("member:call", (member, call) => {
        // object selector reset
        callButton.style.display = 'none';
        closeButton.style.display = 'none';
        hangupButton.style.removeProperty('display');
        statusButton.style.removeProperty('display');
        $('#wa-valid').removeAttr('checked');
        // event when hangup button clicked
        hangupButton.addEventListener("click", () => {
          call.hangUp();
        });
      });
      app.on("call:status:changed",(call) => {
        console.log('Periodik : ', call.status);
        // animation call
        let statusAnimation = `<p class="saving">Call status: ${call.status}<span>.</span><span>.</span><span>.</span></p>`;
        // assign call animation to nexmo status display
        statusElement.innerHTML = statusAnimation;
        // filter nexmo status condition
        switch(call.status) {
          case call.CALL_STATUS.STARTED:
            console.log('Case call status: ', call.status);
            break;
          case call.CALL_STATUS.RINGING:
            console.log('Case call status: ', call.status);
            break;
          case call.CALL_STATUS.FAILED:
            inputCallStatus.value = call.status;
            callStatusAnswered = false;
            callButton.style.display = 'none';
            hangupButton.style.display = 'none';
            statusButton.style.removeProperty('display');
            updateButton.style.removeProperty('display');
            trashButton.style.removeProperty('display');
            waContainer.style.removeProperty('display');
            closeButton.style.removeProperty('dispaly');
            nameContainer.style.removeProperty('display');
            console.log('Case call status: ', call.status);
            break;
          case call.CALL_STATUS.CANCELLED:
            inputCallStatus.value = call.status;
            callStatusAnswered = false;
            callButton.style.removeProperty('display');
            hangupButton.style.display = 'none';
            statusButton.style.display = 'none';
            nameContainer.style.removeProperty('display');
            updateButton.style.removeProperty('display');
            trashButton.style.removeProperty('display');
            waContainer.style.removeProperty('display');
            closeButton.style.removeProperty('dispaly');
            console.log('Case call status: ', call.status);
            break;
          case call.CALL_STATUS.COMPLETED:
            callStatusCompleted = true;
            callButton.style.display = 'none';
            hangupButton.style.display = 'none';
            updateButton.style.removeProperty('display');
            closeButton.style.display = 'none';
            statusButton.style.display = 'none';
            campaign_result.setAttribute('required', 'required');
            nameContainer.style.removeProperty('display');
            campaignContainer.style.removeProperty('display');
            dataTableAdminLeader.ajax.reload();
            console.log('Case call status: ', call.status);
            break;
          case call.CALL_STATUS.ANSWERED:
            // play sound
            sndAnswered.play();
            inputCallStatus.value = call.status;
            callStatusAnswered = true;
            callButton.style.display = 'none';
            hangupButton.style.removeProperty('display');
            nameContainer.style.removeProperty('display');
            closeButton.style.display = 'none';
            statusButton.style.display = 'none';
            console.log('Case call status: ', call.status);
            break;
          default:
            // BUSY
            // REJECTED
            // TIMEOUT
            // UNANSWERED
            inputCallStatus.value = call.status;
            callStatusAnswered = false;
            callButton.style.display = 'none';
            hangupButton.style.display = 'none';
            updateButton.style.removeProperty('display');
            trashButton.style.removeProperty('display');
            statusButton.style.display = 'none';
            nameContainer.style.removeProperty('display');
            waContainer.style.removeProperty('display');
            closeButton.style.removeProperty('dispaly');
            console.log('Case call status: ', call.status);
            console.log('Case call status default: ', call.status);
            break;
        }
      });
    }).catch(function(){
      alert('Network Problem, refresh page and try again later. Please contact dev team if this problem not fix in few minutes.');
      console.error;
      $('#phone-call-modal').modal('hide');
    });
  });

Vonage (Nexmo) Voice API 无法验证您的用户是否应该能够拨打他们在您的应用程序中有权访问的 phone 号码。听起来您已经为应用程序的用户提供了使用您的 Vonage API 凭据发起语音对话的能力。您的用户的验证逻辑必须在您的应用程序中。

您可以考虑的一个解决方案是,在发起新的语音通话之前,您可以检查您的数据库是否有用户访问权限。如果他们没有访问权限,您可以将他们重定向到应用程序中的另一个视图,如果他们有访问权限,您可以发起呼叫。 Laravel 有一整套 validation tooling 可能对您有所帮助。