Добавление данных банковского счета в полосу без сохранения в API или таблицу базы данных - PullRequest
0 голосов
/ 11 ноября 2018

Проблема: Данные банковского счета в форме не сохраняются в Stripe API (Stripe :: Account), возможно, из-за неправильной или успешной работы Stripe :: Account.retrieve (current_user. stripe_token)

Вопрос: Что в моем коде приводит к тому, что извлечение не работает, или это что-то еще, например, неправильно настроенные веб-хуки или неправильный JS?

Мои мысли: Я предполагаю, что это не контроллер, а способ настройки JS, поскольку я не очень хорошо разбираюсь в JS. Я нашел пример приложения на GitHub с использованием Stripe Connect так, как я хочу, чтобы он функционировал, и использовал его в качестве справочного материала для построения контроллера BankAccount, представления / формы и JS.

Это пример приложения для справки, которое я использовал, чтобы помочь мне настроить это: https://stripe -marketplace-demo.herokuapp.com /

Как это настроено: У меня есть таблица Users; пользователи регистрируются и попадают в эту таблицу пользователей. У меня тогда есть таблица StripeAccounts; пользователи (current_user) могут создавать StripeAccount, токен учетной записи Stripe сохраняется как acct_id в StripeAccount >> идентификатор_пользователя (из таблицы User) связан с StripeAccount. Токен stripe_account также сохраняется в таблице User в stripe_token. После создания и сохранения stripe_account они перенаправляются для заполнения формы BankAccount << Здесь мои проблемы. Информация о банковском счете не сохраняется и, скорее всего, из-за невозможности получить. Аргументация ниже. </p>

Вот весь контроллер банковского счета:

class BankAccountsController < ApplicationController
  before_action :authenticate_user!

  def new

    unless current_user.stripe_token
      redirect_to new_user_stripe_account_path and return
    end

    begin
      @stripe_account = Stripe::Account.retrieve(current_user.stripe_token)

    rescue Stripe::StripeError => e
      handle_error(e.message, 'new')

    rescue => e
      flash[:error] = e.message
    end
  end

  def create

    unless params[:stripeToken] && current_user.stripe_token
      redirect_to new_bank_account_path and return
    end

    begin
      stripe_account = Stripe::Account.retrieve(current_user.stripe_token)

      stripe_account.external_account = params[:stripeToken]
      stripe_account.save

      flash[:success] = "Your bank account has been added!"
      redirect_to dashboard_path

    rescue Stripe::StripeError => e
       flash[:error] = e.message


    rescue => e
       flash[:error] = e.message

    end
  end
end

У меня есть правильное сохранение stripe_accounts с токеном полосы, сохраненным в «acct_id» в таблице StripeAccounts, и таким же токеном, сохраненным как «stripe_token» в таблице Users относительно правильного идентификатора user_id.

Я новичок в rails (примерно через месяц) и очень новичок в Stripe, поэтому следующее является только предположением: я предполагаю, что "@stripe_account = Stripe :: Account.retrieve (current_user.stripe_token)" в новом методе ничего не извлекает. Это полагается на webhooks? Что я не правильно настроил? Я пытался сделать это через Ngrok, но безрезультатно. Хотя я не получаю ошибок на странице в HTML. Тогда я бы предположил, что застрял в коде метода create:

  unless params[:stripeToken] && current_user.stripe_token
      redirect_to new_bank_account_path and return
    end

Потому что, когда я нажимаю "Отправить", страница просто перезагружается и ничего не сохраняется в API.

Вот представление / форма, которую я отправляю вместе с JS:

<%= content_for :page_title, "Add a new bank account" %>
<% content_for(:header) do %>
  <script src="https://js.stripe.com/v3/"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.payment/1.4.1/jquery.payment.js"></script>
  <script>
    // Set your Stripe publishable API key here
    // Stripe.setPublishableKey("<%= ENV['PUBLISHABLE_KEY'] %>");
    var stripe = Stripe('pk_test_3v1234567896LyWMYKE1f0B8');

    $(function() {
      var $form = $('#payment-form');
      $form.submit(function(event) {
        // Clear any errors
        $form.find('.has-error').removeClass('has-error');
        // Disable the submit button to prevent repeated clicks:
        $form.find('.submit').prop('disabled', true).html("<i class='fa fa-spinner fa-spin'></i> Adding bank account...");
        // Request a token from Stripe:
        Stripe.bankAccount.createToken($form, stripeResponseHandler);


        return false;
      });
    });
    function stripeResponseHandler(status, response) {
      var $form = $('#payment-form');
      if (response.error) {

        $form.find('.errors').text(response.error.message).addClass('alert alert-danger');
        $form.find('.' + response.error.param).parent('.form-group').addClass('has-error');
        $form.find('button').prop('disabled', false).text('Add Bank Account'); // Re-enable submission
      }
      else { // Token was created!
        $form.find('.submit').html("<i class='fa fa-check'></i> Account added");

        var token = response.id;

        $form.append($('<input type="hidden" name="stripeToken" />').val(token));

        $form.get(0).submit();
      }
    }
  </script>
<% end %>

<div class="panel panel-default">
  <div class="panel-body">
    <form action="/bank_accounts" method="POST" id="payment-form">
      <div class="errors"></div>
      <div class="row">
        <div class="col-md-8">
          <div class="form-group">
            <label>Country</label>
            <select class="form-control input-lg" id="country" data-stripe="country">
              <option value="US">United States</option>
            </select>
          </div>
        </div>
        <div class="col-md-4">
          <div class="form-group">
            <label>Currency</label>
            <select class="form-control input-lg" id="currency" data-stripe="currency">
              <option value="usd">USD</option>
            </select>
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-6" id="routing_number_div">
          <div class="form-group">
            <label id="routing_number_label">Routing Number</label>
            <input class="form-control input-lg bank_account" id="routing_number" type="tel" size="12" data-stripe="routing_number" value="110000000" autocomplete="off">
          </div>
        </div>
        <div class="col-md-6">
          <div class="form-group">
            <label id="account_number_label">Account Number</label>
            <input class="form-control input-lg bank_account" id="account_number" type="tel" size="20" data-stripe="account_number" value="000123456789" autocomplete="off">
          </div>
        </div>
      </div>
      <div class="row">
        <div class="col-md-12">
          <button class="btn btn-lg btn-block btn-primary btn-custom submit" type="submit">Add Bank Account</button>
        </div>
      </div>
      <%= hidden_field_tag :authenticity_token, form_authenticity_token -%>
    </form>
  </div>
</div>

Как вы видите в JS: // Stripe.setPublishableKey("<%= ENV['PUBLISHABLE_KEY'] %>"); Я пытался с этим, но, видимо, это уже не актуально. Просто для целей тестирования я просто включил публикуемый ключ непосредственно в JS, а не в учетные данные, пока не выясню.

Вот моя консоль при отправке формы:

Started POST "/bank_accounts" for 127.0.0.1 at 2018-11-10 14:32:11 -0500
Processing by BankAccountsController#create as HTML
  Parameters: {"authenticity_token"=>"l69UBGkzwcel7JH34+TDbsfQ9Xjkiogu+emWm+8+0iVQfKh9AIxDaXp0yMhjFkUVznHeJYwXeVmdBVSI2XjArg=="}
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ /home/bob/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
Redirected to http://localhost:3000/bank_accounts/new
Completed 302 Found in 4ms (ActiveRecord: 0.3ms)


Started GET "/bank_accounts/new" for 127.0.0.1 at 2018-11-10 14:32:11 -0500
Processing by BankAccountsController#new as HTML
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ /home/bob/.rvm/gems/ruby-2.5.1/gems/activerecord-5.2.1/lib/active_record/log_subscriber.rb:98
  StripeAccount Load (0.2ms)  SELECT  "stripe_accounts".* FROM "stripe_accounts" WHERE "stripe_accounts"."user_id" = ? LIMIT ?  [["user_id", 2], ["LIMIT", 1]]
  ↳ app/controllers/bank_accounts_controller.rb:6
  Rendering bank_accounts/new.html.erb within layouts/application
  Rendered bank_accounts/_bank_account_form.html.erb (1.0ms)
  Rendered bank_accounts/new.html.erb within layouts/application (4.2ms)
  Rendered layouts/_navbar.html.erb (3.0ms)
  Rendered layouts/_footer.html.erb (0.3ms)
Completed 200 OK in 218ms (Views: 212.4ms | ActiveRecord: 0.5ms)

Электронное сообщение гласит:

Invalid external_account object: must be a dictionary or a non-empty string. See API docs at https://stripe.com/docs'

но e.message появляется только при перезагрузке страницы, а не при отправке формы. Только после того, как я отправляю, а затем перезагружаю страницу, он появляется.

У меня ничего не настроено в модели BankAccount, равно как и пример приложения, которое я использовал в качестве ссылки.

Дополнительная информация: Для пользователей и StripeAccount они у меня вложены, поэтому вы увидите, что new_user_stripe_account_path ... BankAccount ни к чему не вложен.

Сообщение из консоли браузера:

Error: The selector you specified (#card-element) applies to no DOM elements that are currently on the page.
Make sure the element exists on the page before calling mount(). v3:1:10186
t
https://js.stripe.com/v3/:1:10186
oi/this.mount<
https://js.stripe.com/v3/:1:79868
Gt/<
https://js.stripe.com/v3/:1:23367
<anonymous>
http://localhost:3000/assets/stripejs.self-8c2ad75855f867e5280e1a173e994f83fb5afc997847456669b8cbe0b24fae1f.js:31:1
[Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE) [nsIContentSniffer.getMIMETypeFromContent]"  nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)"  location: "JS frame :: resource:///modules/FaviconLoader.jsm :: onStopRequest :: line 181"  data: no]
onStopRequest
resource:///modules/FaviconLoader.jsm:181:16
InterpretGeneratorResume self-hosted:1257:8 next self-hosted:1212:9

1 Ответ

0 голосов
/ 12 ноября 2018

Похоже, виноват JS. Это было несколько неправильно и устарело, и некоторые вещи не были сопоставлены с моим контроллером.

Вот правильный JS для S v3 JS на странице просмотра (я считаю, что ЭТО НУЖНО БЫТЬ ПОД ФОРМОЙ; может быть неправильно):

var stripe = Stripe('pk_test_WUSo123456789PWU8kh');

  function setOutcome(result) {
  var successElement = document.querySelector('.success');
  var errorElement = document.querySelector('.error');
  successElement.classList.remove('visible');
  errorElement.classList.remove('visible');

  if (result.token) {
    // In this example, we're simply displaying the token
    successElement.querySelector('.token').textContent = result.token.id;
    successElement.classList.add('visible');

    // In a real integration, you'd submit the form with the token to your backend server
    var form = document.querySelector('form');
    form.querySelector('input[name="token"]').setAttribute('value', result.token.id);
    form.submit();

  } else {
    errorElement.textContent = result.error.message;
    errorElement.classList.add('visible');
  }
}

document.querySelector('form').addEventListener('submit', function(e) {
  e.preventDefault();

  var bankAccountParams = {
    country: document.getElementById('country').value,
    currency: document.getElementById('currency').value,
    account_number: document.getElementById('account-number').value,
    account_holder_name: document.getElementById('account-holder-name').value,
    account_holder_type: document.getElementById('account-holder-type').value,
  }
  if (document.getElementById('routing-number').value != '') {
    bankAccountParams['routing_number'] = document.getElementById('routing-number').value;
  }

  stripe.createToken('bank_account', bankAccountParams).then(setOutcome);
});

И правильный Create для контроллера BankAccount: (или, по крайней мере, это работает, возможно, я исправлю это немного позже)

def create
    unless params[:token] && current_user.stripe_token
      redirect_to new_bank_account_path and return
    end
    begin
      token = params[:token]
      stripe_account.external_account = params[:token]
      stripe_account.save
      flash[:success] = "Your bank account has been added!"
      redirect_to dashboard_path
    rescue Stripe::StripeError => e
      # handler_for_rescue(e.message, 'new')
      flash[:error] = e.message
    # Handle any other exceptions
    rescue => e
      # handle_error(e.message, 'new')
      flash[:error] = e.message
    end
  end
end

В форме я также передаю следующее, что может или не может быть необходимо, я проведу тестирование по этому вопросу позже, после того как я опубликую это .:

<%= hidden_field_tag :authenticity_token, form_authenticity_token -%>
<%= hidden_field_tag :stripeToken, current_user.stripe_token -%>

Не верю: нужен stripeToken ... но аутентификация

...