自定义比特币条纹 Rails 付款
custom Bitcoin Stripe Rails payments
我收到一封来自客户的电子邮件,称他们在 Bitcoin/Stripe 付款后从未收到他们的产品。问题是,我找不到在沙盒中彻底测试比特币的方法,所以不确定我的实现是否正常工作。沙盒自动填充接收者,所以我收到付款通知,一切正常。然而,当 运行 直播时,我不确定我的投票是否正常工作。我在下面发布了所有相关代码,有人能看出代码中的任何缺陷吗?
*我的商店是 运行 Ruby(2.3.1p112) Rails (4.2.6)
我的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
我最终将轮询逻辑添加到计时器中,这似乎可行....尽管文档中暗示我不需要手动轮询?至少我是这样读的...
这是我为其他遇到此问题的人所做的完整修改 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>
我做了更多修改,实际上从我的 .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...
希望这能帮助其他一些遇到同样问题的人。
:)
我收到一封来自客户的电子邮件,称他们在 Bitcoin/Stripe 付款后从未收到他们的产品。问题是,我找不到在沙盒中彻底测试比特币的方法,所以不确定我的实现是否正常工作。沙盒自动填充接收者,所以我收到付款通知,一切正常。然而,当 运行 直播时,我不确定我的投票是否正常工作。我在下面发布了所有相关代码,有人能看出代码中的任何缺陷吗?
*我的商店是 运行 Ruby(2.3.1p112) Rails (4.2.6)
我的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
我最终将轮询逻辑添加到计时器中,这似乎可行....尽管文档中暗示我不需要手动轮询?至少我是这样读的...
这是我为其他遇到此问题的人所做的完整修改 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>
我做了更多修改,实际上从我的 .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...
希望这能帮助其他一些遇到同样问题的人。 :)