【问题标题】:custom Bitcoin Stripe Rails payments自定义比特币 Stripe Rails 付款
【发布时间】:2016-08-05 20:08:21
【问题描述】:

我收到一封来自客户的电子邮件,说他们在支付比特币/Stripe 后从未收到过他们的产品。问题是,我找不到在沙盒中彻底测试比特币的方法,所以不确定我的实现是否正常工作。沙箱自动填充接收器,所以我收到付款通知,一切正常。但是,在实时运行时,我不确定我的轮询是否正常工作。我在下面发布了所有相关代码,任何人都可以看到代码中的任何缺陷吗?

*我的商店在 Rails (4.2.6) 上运行 Ruby(2.3.1p112)

我的payment_bitcoin.html.erb

<%= render :partial => "offsite_checkout_summary" %>

<p>Your license key, along with your purchase receipt, will be sent to <strong><%= @order.licensee_name %></strong> at <strong><%= @order.email %></strong> once payment has been confirmed.</p>

<div id="extra_purchase_notes">
    <em><strong>Note:</strong> The bitcoin link and price provided is only valid for <span id="countdown_time">10:00</span> minutes.</em>
</div>

<div class="d cl"></div>
<%= render :partial => "items_checkout_summary", :locals => { order: @order } %>
  <div id="bitcoin_payment_address">
    <script src="https://js.stripe.com/v2/stripe.js"></script>
    <script type="text/javascript">
      Stripe.setPublishableKey('<%= $STRIPE_PREFS['stripe_publishable_key'] %>');
      function populateBitcoinCheckout(status, response) {
        if (status === 200) {
          document.getElementById("bitcoin_total").innerHTML = " (" + response.bitcoin_amount/100000000 + " BTC)";
          document.getElementById("bitcoin_payment_string").innerHTML = 'Please send: <strong>' + response.bitcoin_amount/100000000 + ' BTC</strong> to <strong>' + '<a href="' + response.bitcoin_uri + '&label=Software+Purchase">' + response.inbound_address + '</a></strong>';
          document.getElementById("btc-button").href = response.bitcoin_uri + '&label=Software+Purchase';

                    //poll reciever
                    Stripe.bitcoinReceiver.pollReceiver(response.id, filledReceiverHandler(response));

          //configure timer
          function startTimer(duration, countdown) {
              var timer = duration,minutes,seconds;
              var t = setInterval(function () {
                  minutes = parseInt(timer / 60, 10)
                  seconds = parseInt(timer % 60, 10);

                  minutes = minutes < 10 ? "0" + minutes : minutes;
                  seconds = seconds < 10 ? "0" + seconds : seconds;

                  countdown.textContent = minutes + ":" + seconds;

                  if (--timer < 0) {
                      clearInterval(t);
                      document.getElementById("bitcoin_total").innerHTML = "";
                      document.getElementById("bitcoin_payment_string").innerHTML = "";
                      document.getElementById("bitcoin_button_text").innerHTML = "Refresh Order"
                      document.getElementById("btc-button").href = "javascript:history.back()"
                      document.getElementById("extra_purchase_notes").innerHTML = "<em><strong>Oops...</strong> This order has expired, use the Refresh Order button to retry.</em>"
                      Stripe.bitcoinReceiver.cancelReceiverPoll(response.id);
                  }
              }, 1000);
          }

          //start timer
          var countdown = document.getElementById('countdown_time');
          startTimer(600, countdown);

        } else {
          document.getElementById("bitcoin_uri_string").innerHTML = JSON.stringify(response);
          Stripe.bitcoinReceiver.cancelReceiverPoll(response.id);
        }
      }

      Stripe.bitcoinReceiver.createReceiver({
        amount: "<%= (@order.total * 100).round %>",
        currency: 'usd',
        description: 'Software purchase',
        email: "<%= @order.email %>"
      }, populateBitcoinCheckout);

            function filledReceiverHandler(response)
            {
                if (response.filled === true) {
                    function post(path, parameters) {
                var form = $('<form></form>');
                form.attr("method", "post");
                form.attr("action", path);

                $.each(parameters, function(key, value) {
            if ( typeof value == 'object' || typeof value == 'array' ){
                $.each(value, function(subkey, subvalue) {
                    var field = $('<input />');
                    field.attr("type", "hidden");
                    field.attr("name", key+'[]');
                    field.attr("value", subvalue);
                    form.append(field);
                });
            } else {
                var field = $('<input />');
                field.attr("type", "hidden");
                field.attr("name", key);
                field.attr("value", value);
                form.append(field);
            }
            });
            $(document.body).append(form);
            form.submit();
            }
                post('purchase_bitcoin', response);
            }
        }
    </script>
    </div>
</div>
<p id="bitcoin_payment_string"></p>
<div class="d"></div>

<p style="text-align: right;">
  <a id="btc-button"><button class="bitcoin-button" style="visibility: visible;"><span id="bitcoin_button_text">Pay with Bitcoin</span></button></a>
</p>

以及相关的控制器方法:

#bitcoin purchase
  def purchase_bitcoin
    require 'stripe'
    if check_if_order_exists() == false
      if session[:failure_reason] != nil
        render :action => 'failed'
        return
      end
      redirect_to :action => 'index'
      return
    end

    if session[:item_number] == nil
      flash[:notice] = 'Nothing to purchase'
      redirect_to :action => 'index'
      return
    end

    #create the order
    generateNewOrder("Bitcoin")

    #total in cents
    the_total = @order.total.to_f
    the_total_cents = (the_total*100).to_i

    Stripe.api_key = $STRIPE_PREFS['stripe_secret']

    #add order details
    @order.transaction_number = params[:id]

    # Create the charge on Stripe's servers - this will charge the user's card
    session[:coin_total] = (params[:bitcoin_amount].to_f/100000000).to_s

    charge = Stripe::Charge.create(
      :amount => params[:amount],
      :currency => params[:currency],
      :source => params[:id],
      :description => 'Software purchase'
    )

    #re-add completed order details
    the_id = charge["id"]
    @order.transaction_number = the_id
    @order.comment = 'Total = ' + (params[:bitcoin_amount].to_f/100000000).to_s + 'BTC; payment address = ' + params[:inbound_address]

    @order.status = 'C'
    @order.finish_and_save()
    session[:order_id] = @order.id

    #send license
    Thread.new do
      OrderMailer.thankyou(@order).deliver
    end

    #logger.info session.inspect
    render :action => 'thankyou_bitcoin'
  end

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 stripe-payments bitcoin


    【解决方案1】:

    我最终将轮询逻辑添加到计时器中,这似乎可行....尽管文档中暗示我不需要手动轮询?至少我是这么读的……

    这是我完整修改的 payment_bitcoin.html.erb,供其他遇到问题的人使用

    <%= render :partial => "offsite_checkout_summary" %>
    
    <p>Your license key, along with your purchase receipt, will be sent to <strong><%= @order.licensee_name %></strong> at <strong><%= @order.email %></strong> once payment has been confirmed.</p>
    
    <div id="extra_purchase_notes">
        <em><strong>Note:</strong> The bitcoin link and price provided is only valid for <span id="countdown_time">10:00</span> minutes.</em>
    </div>
    
    <div class="d cl"></div>
    <%= render :partial => "items_checkout_summary", :locals => { order: @order } %>
      <div id="bitcoin_payment_address">
        <script src="https://js.stripe.com/v2/stripe.js"></script>
        <script type="text/javascript">
          Stripe.setPublishableKey('<%= $STRIPE_PREFS['stripe_publishable_key'] %>');
          function populateBitcoinCheckout(status, response) {
            if (status === 200) {
              document.getElementById("bitcoin_total").innerHTML = " (" + response.bitcoin_amount/100000000 + " BTC)";
              document.getElementById("bitcoin_payment_string").innerHTML = 'Please send: <strong>' + response.bitcoin_amount/100000000 + ' BTC</strong> to <strong>' + '<a href="' + response.bitcoin_uri + '&label=Software+Purchase">' + response.inbound_address + '</a></strong>';
              document.getElementById("btc-button").href = response.bitcoin_uri + '&label=Software+Purchase';
    
              //configure timer
              function startTimer(duration, countdown) {
                  var timer = duration,minutes,seconds;
                  var t = setInterval(function () {
    
                      minutes = parseInt(timer / 60, 10)
                      seconds = parseInt(timer % 60, 10);
    
                      minutes = minutes < 10 ? "0" + minutes : minutes;
                      seconds = seconds < 10 ? "0" + seconds : seconds;
    
                      //poll reciever
                      Stripe.bitcoinReceiver.pollReceiver(response.id, filledReceiverHandler(response));
    
                      countdown.textContent = minutes + ":" + seconds;
    
                      if (--timer < 0) {
                          clearInterval(t);
                          document.getElementById("bitcoin_total").innerHTML = "";
                          document.getElementById("bitcoin_payment_string").innerHTML = "";
                          document.getElementById("bitcoin_button_text").innerHTML = "Refresh Order"
                          document.getElementById("btc-button").href = "javascript:history.back()"
                          document.getElementById("extra_purchase_notes").innerHTML = "<em><strong>Oops...</strong> This order has expired, use the Refresh Order button to retry.</em>"
                          Stripe.bitcoinReceiver.cancelReceiverPoll(response.id);
                      }
                  }, 1000);
              }
    
              //start timer
              var countdown = document.getElementById('countdown_time');
              startTimer(600, countdown);
    
            } else {
              document.getElementById("bitcoin_uri_string").innerHTML = JSON.stringify(response);
            }
          }
    
          Stripe.bitcoinReceiver.createReceiver({
            amount: "<%= (@order.total * 100).round %>",
            currency: 'usd',
            description: 'Software purchase',
            email: "<%= @order.email %>"
          }, populateBitcoinCheckout);
    
                function filledReceiverHandler(response)
                {
                    if (response.filled === true) {
                        function post(path, parameters) {
                    var form = $('<form></form>');
                    form.attr("method", "post");
                    form.attr("action", path);
    
                    $.each(parameters, function(key, value) {
                if ( typeof value == 'object' || typeof value == 'array' ){
                    $.each(value, function(subkey, subvalue) {
                        var field = $('<input />');
                        field.attr("type", "hidden");
                        field.attr("name", key+'[]');
                        field.attr("value", subvalue);
                        form.append(field);
                    });
                } else {
                    var field = $('<input />');
                    field.attr("type", "hidden");
                    field.attr("name", key);
                    field.attr("value", value);
                    form.append(field);
                }
                });
                $(document.body).append(form);
                form.submit();
                }
                    post('purchase_bitcoin', response);
                }
            }
        </script>
        </div>
    </div>
    <p id="bitcoin_payment_string"></p>
    <div class="d"></div>
    
    <p style="text-align: right;">
      <a id="btc-button"><button class="bitcoin-button" style="visibility: visible;"><span id="bitcoin_button_text">Pay with Bitcoin</span></button></a>
    </p>
    

    【讨论】:

      【解决方案2】:

      我做了更多的修改,实际上从我的 .erb 中删除了所有轮询逻辑,选择使用网络挂钩。 https://stripe.com/docs/webhooks

      require 'json'
      require 'stripe'
      
      class Store::BitcoinController < ApplicationController
      
        def payment_recieved
          type = params[:type]
          data = params[:data]
      
          if (type.blank? || type != "bitcoin.receiver.filled" || data.blank?)
            logger.warn("Got request to Bitcoin IPN with invalid receiver email from #{request.remote_addr || request.remote_ip}")
            render :nothing => true, :status => 200
            return
          end
      
      ..process order here...
      

      希望这将帮助其他一些有同样问题的人。 :)

      【讨论】:

        猜你喜欢
        • 2018-06-02
        • 2014-04-22
        • 1970-01-01
        • 1970-01-01
        • 2016-08-07
        • 2021-06-10
        • 2019-08-08
        • 2011-09-21
        • 2017-01-28
        相关资源
        最近更新 更多