payola-payments 1.0.7 → 1.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/app/assets/javascripts/payola/checkout_button.js +12 -8
- data/app/assets/javascripts/payola/form.js +70 -0
- data/app/controllers/payola/transactions_controller.rb +2 -6
- data/app/helpers/payola/price_helper.rb +2 -2
- data/app/models/payola/sale.rb +28 -9
- data/app/services/payola/charge_card.rb +5 -5
- data/app/services/payola/create_sale.rb +4 -3
- data/app/views/payola/transactions/_checkout.html.erb +5 -1
- data/app/views/payola/transactions/_stripe_header.html.erb +4 -0
- data/db/migrate/20141026101628_add_custom_fields_to_payola_sale.rb +5 -0
- data/db/migrate/20141026144800_rename_custom_fields_to_signed_custom_fields_on_payola_sale.rb +5 -0
- data/lib/payola/version.rb +1 -1
- data/spec/controllers/payola/transactions_controller_spec.rb +66 -0
- data/spec/dummy/app/controllers/buy_controller.rb +1 -1
- data/spec/dummy/app/views/buy/index.html.erb +18 -1
- data/spec/dummy/app/views/layouts/application.html.erb +2 -1
- data/spec/dummy/config/application.rb +1 -0
- data/spec/dummy/config/locales/de.yml +203 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +2 -1
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +2116 -0
- data/spec/dummy/log/test.log +18776 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/0e585aeb88c1555ae8f5292f7c7a9068 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/1b54952339cbc96811097cee5f8923b4 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/5afa4bc970e0bc781b3500c696ba25ff +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/664efeb51d1b486dcfc30d142db56f3c +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/68a3bbdc64a0729b5896fa1e33fce4b1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/96b1ccdc193a02b2bbea195310262fa5 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/aea159adbc0944b120d91d5b578612ba +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b20dab973e971c61bc7e334a38453ae0 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/b83da90cb58011313e13a97a4a41cb6e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c0b954b40f2881a6d286061bc069643e +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/c5b3877d49b39932a7557611cefca9a6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ca491e325ff6948be9292f701aa4ce64 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d3f4b67cc032016dfeebc71f2148b9dc +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/d9856638a3c7b9cfd9d1b884e3b33982 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ddba6e841f28d3005e828fbfffd18b74 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/df935e399019055729aa968bd7062096 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/ef82c31948061751bb026663b54484e1 +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f0b5d4bea0c6cdd1904e1e9a0a45532f +0 -0
- data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
- data/spec/helpers/payola/price_helper_spec.rb +17 -0
- data/spec/models/payola/sale_spec.rb +15 -0
- data/spec/services/payola/create_sale_spec.rb +1 -1
- metadata +58 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2E1ZTQyZDI1ZGM1YjczYmQzOGQxMTRiODIyMWUxNGFiZGNjMDdlOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ODgxMDljMjc2ODcyMzM0ZjdiODM4NGUzYWE5Zjg2NWExYmZmN2QyOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDJjZmNiMWRiYTY0YzZjYjllMDRiYzAyM2E1MTE2MmM2ZWQ5ZjMyZWNkYmE1
|
10
|
+
YmIxYjk2NWYxZWQ3OWUzODY2MGUyMmY4NmU4NTZmM2Y0Yjc4YTg3MGY2MTJl
|
11
|
+
YThmMDZlOWIyZWRlYmIwMDBkZTg2OWE5ZTNhOGM3OTJhM2UzOGQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MTFhYWNhZGU5MTExMTY2ODc1NmI1ZGE4ZjA2NmMzNjFhYjFjMGMwMjlkZGJl
|
14
|
+
MWI1NWQ5MWZjYTM0OWY2ZTAwNjNkYjUwMTNmM2NiNzA4YWI0NDg2ZWJjOWEw
|
15
|
+
NWFlNDUwNGI5ZGYxZDM0YWVkYTkwZWM3N2Q5MjA3YjMwMzAzNjA=
|
@@ -1,16 +1,16 @@
|
|
1
|
-
var
|
1
|
+
var PayolaCheckout = {
|
2
2
|
setUpStripeCheckoutButton: function(options) {
|
3
3
|
var handler = StripeCheckout.configure({
|
4
4
|
key: options.publishable_key,
|
5
5
|
image: options.product_image_path,
|
6
|
-
token: function(token) {
|
6
|
+
token: function(token) { PayolaCheckout.tokenHandler(token, options) }
|
7
7
|
});
|
8
8
|
|
9
9
|
document.getElementById(options.button_id).addEventListener('click', function(e) {
|
10
10
|
handler.open({
|
11
11
|
name: options.name,
|
12
12
|
description: options.description,
|
13
|
-
amount: options.
|
13
|
+
amount: options.price,
|
14
14
|
panelLabel: options.panel_label,
|
15
15
|
allowRememberMe: options.allow_remember_me,
|
16
16
|
zipCode: options.verify_zip_code,
|
@@ -25,6 +25,10 @@ var Payola = {
|
|
25
25
|
console.log(options.form_id);
|
26
26
|
form.append($('<input type="hidden" name="stripeToken">').val(token.id));
|
27
27
|
form.append($('<input type="hidden" name="stripeEmail">').val(token.email));
|
28
|
+
if (options.signed_custom_fields) {
|
29
|
+
form.append($('<input type="hidden" name="signed_custom_fields">').val(options.signed_custom_fields));
|
30
|
+
}
|
31
|
+
|
28
32
|
$(".payola-checkout-button").prop("disabled", true);
|
29
33
|
$(".payola-checkout-button-text").hide();
|
30
34
|
$(".payola-checkout-button-spinner").show();
|
@@ -32,8 +36,8 @@ var Payola = {
|
|
32
36
|
type: "POST",
|
33
37
|
url: options.base_path + "/buy/" + options.product_class + "/" + options.product_permalink,
|
34
38
|
data: form.serialize(),
|
35
|
-
success: function(data) {
|
36
|
-
error: function(data) {
|
39
|
+
success: function(data) { PayolaCheckout.poll(data.guid, 60, options) },
|
40
|
+
error: function(data) { PayolaCheckout.showError(data.responseJSON.error, options) }
|
37
41
|
});
|
38
42
|
},
|
39
43
|
|
@@ -48,7 +52,7 @@ var Payola = {
|
|
48
52
|
|
49
53
|
poll: function(guid, num_retries_left, options) {
|
50
54
|
if (num_retries_left == 0) {
|
51
|
-
|
55
|
+
PayolaCheckout.showError("This seems to be taking too long. Please contact support and give them transaction ID: " + guid, options);
|
52
56
|
return;
|
53
57
|
}
|
54
58
|
|
@@ -56,9 +60,9 @@ var Payola = {
|
|
56
60
|
if (data.status === "finished") {
|
57
61
|
window.location = options.base_path + "/confirm/" + guid;
|
58
62
|
} else if (data.status === "errored") {
|
59
|
-
|
63
|
+
PayolaCheckout.showError(data.error, options);
|
60
64
|
} else {
|
61
|
-
setTimeout(function() {
|
65
|
+
setTimeout(function() { PayolaCheckout.poll(guid, num_retries_left - 1, options) }, 500);
|
62
66
|
}
|
63
67
|
});
|
64
68
|
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
var PayolaPaymentForm = {
|
2
|
+
initialize: function() {
|
3
|
+
$(document).on('submit', '.payola-payment-form', function() {
|
4
|
+
return PayolaPaymentForm.handleSubmit($(this));
|
5
|
+
});
|
6
|
+
},
|
7
|
+
|
8
|
+
handleSubmit: function(form) {
|
9
|
+
form.find(':submit').prop('disabled', true);
|
10
|
+
$('.payola-spinner').show();
|
11
|
+
Stripe.card.createToken(form, function(status, response) {
|
12
|
+
PayolaPaymentForm.stripeResponseHandler(form, status, response);
|
13
|
+
});
|
14
|
+
return false;
|
15
|
+
},
|
16
|
+
|
17
|
+
stripeResponseHandler: function(form, status, response) {
|
18
|
+
if (response.error) {
|
19
|
+
PayolaPaymentForm.showError(form, response.error.message);
|
20
|
+
form.find(':submit').prop('disabled', false);
|
21
|
+
} else {
|
22
|
+
var email = form.find("[data-payola='email']").val();
|
23
|
+
|
24
|
+
var base_path = form.data('payola-base-path');
|
25
|
+
var product = form.data('payola-product');
|
26
|
+
var permalink = form.data('payola-permalink');
|
27
|
+
|
28
|
+
var data_form = $('<form></form>');
|
29
|
+
data_form.append($('<input type="hidden" name="stripeToken">').val(response.id));
|
30
|
+
data_form.append($('<input type="hidden" name="stripeEmail">').val(email));
|
31
|
+
data_form.append(form.find('input[name="authenticity_token"]'));
|
32
|
+
|
33
|
+
$.ajax({
|
34
|
+
type: "POST",
|
35
|
+
url: base_path + "/buy/" + product + "/" + permalink,
|
36
|
+
data: data_form.serialize(),
|
37
|
+
success: function(data) { PayolaPaymentForm.poll(form, 60, data.guid, base_path) },
|
38
|
+
error: function(data) { PayolaPaymentForm.showError(form, data.responseJSON.error) }
|
39
|
+
});
|
40
|
+
}
|
41
|
+
},
|
42
|
+
|
43
|
+
poll: function(form, num_retries_left, guid, base_path) {
|
44
|
+
if (num_retries_left == 0) {
|
45
|
+
PayolaPaymentForm.showError(form, "This seems to be taking too long. Please contact support and give them transaction ID: " + guid)
|
46
|
+
}
|
47
|
+
$.get(base_path + '/status/' + guid, function(data) {
|
48
|
+
if (data.status === "finished") {
|
49
|
+
form.append($('<input type="hidden" name="payola_sale_guid"></input>').val(guid));
|
50
|
+
form.get(0).submit();
|
51
|
+
} else if (data.status === "errored") {
|
52
|
+
PayolaPaymentForm.showError(form, data.error);
|
53
|
+
} else {
|
54
|
+
setTimeout(function() { PayolaPaymentForm.poll(form, num_retries_left - 1, guid, base_path) }, 500);
|
55
|
+
}
|
56
|
+
});
|
57
|
+
},
|
58
|
+
|
59
|
+
showError: function(form, message) {
|
60
|
+
$('.payola-spinner').hide();
|
61
|
+
var error_selector = form.data('payola-error-selector');
|
62
|
+
if (error_selector) {
|
63
|
+
$(error_selector).text(message);
|
64
|
+
} else {
|
65
|
+
form.find('.payola-payment-error').text(message);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
};
|
69
|
+
|
70
|
+
$(function() { PayolaPaymentForm.initialize() } );
|
@@ -17,11 +17,11 @@ module Payola
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def create
|
20
|
-
create_params =
|
20
|
+
create_params = params.permit!.merge(
|
21
21
|
product: @product,
|
22
22
|
coupon: @coupon,
|
23
23
|
affiliate: @affiliate
|
24
|
-
|
24
|
+
)
|
25
25
|
|
26
26
|
@sale = CreateSale.call(create_params)
|
27
27
|
|
@@ -58,9 +58,5 @@ module Payola
|
|
58
58
|
|
59
59
|
end
|
60
60
|
|
61
|
-
def sale_params
|
62
|
-
params.permit(:stripeToken, :stripe_token, :stripeTokenType, :stripeEmail)
|
63
|
-
end
|
64
|
-
|
65
61
|
end
|
66
62
|
end
|
data/app/models/payola/sale.rb
CHANGED
@@ -44,6 +44,34 @@ module Payola
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
def verifier
|
48
|
+
@verifier ||= ActiveSupport::MessageVerifier.new(Payola.secret_key_for_sale(self), digest: 'SHA256')
|
49
|
+
end
|
50
|
+
|
51
|
+
def verify_charge
|
52
|
+
begin
|
53
|
+
self.verify_charge!
|
54
|
+
rescue RuntimeError => e
|
55
|
+
self.error = e.message
|
56
|
+
self.fail!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def verify_charge!
|
61
|
+
if Payola.charge_verifier.arity > 1
|
62
|
+
Payola.charge_verifier.call(self, custom_fields)
|
63
|
+
else
|
64
|
+
Payola.charge_verifier.call(self)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def custom_fields
|
69
|
+
if self.signed_custom_fields.present?
|
70
|
+
verifier.verify(self.signed_custom_fields)
|
71
|
+
else
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
47
75
|
|
48
76
|
private
|
49
77
|
|
@@ -63,15 +91,6 @@ module Payola
|
|
63
91
|
Payola.instrument(instrument_key('refunded'), self)
|
64
92
|
end
|
65
93
|
|
66
|
-
def verify_charge
|
67
|
-
begin
|
68
|
-
Payola.charge_verifier.call(self)
|
69
|
-
rescue RuntimeError => e
|
70
|
-
self.error = e.message
|
71
|
-
self.fail!
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
94
|
def product_class
|
76
95
|
product.product_class
|
77
96
|
end
|
@@ -5,27 +5,27 @@ module Payola
|
|
5
5
|
secret_key = Payola.secret_key_for_sale(sale)
|
6
6
|
|
7
7
|
begin
|
8
|
-
|
9
|
-
|
8
|
+
sale.verify_charge!
|
9
|
+
|
10
10
|
customer = Stripe::Customer.create({
|
11
11
|
card: sale.stripe_token,
|
12
12
|
email: sale.email
|
13
13
|
}, secret_key)
|
14
|
-
|
14
|
+
|
15
15
|
charge = Stripe::Charge.create({
|
16
16
|
amount: sale.amount,
|
17
17
|
currency: sale.currency,
|
18
18
|
customer: customer.id,
|
19
19
|
description: sale.guid,
|
20
20
|
}, secret_key)
|
21
|
-
|
21
|
+
|
22
22
|
if charge.respond_to?(:fee)
|
23
23
|
fee = charge.fee
|
24
24
|
else
|
25
25
|
balance = Stripe::BalanceTransaction.retrieve(charge.balance_transaction, secret_key)
|
26
26
|
fee = balance.fee
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
sale.update_attributes(
|
30
30
|
stripe_id: charge.id,
|
31
31
|
stripe_customer_id: customer.id,
|
@@ -7,11 +7,12 @@ module Payola
|
|
7
7
|
|
8
8
|
Payola::Sale.new do |s|
|
9
9
|
s.product = product
|
10
|
-
s.email = params[:
|
11
|
-
s.stripe_token = params[:stripeToken]
|
10
|
+
s.email = params[:stripeEmail]
|
11
|
+
s.stripe_token = params[:stripeToken]
|
12
12
|
s.affiliate_id = affiliate.try(:id)
|
13
13
|
s.currency = product.respond_to?(:currency) ? product.currency : 'usd'
|
14
|
-
|
14
|
+
s.signed_custom_fields = params[:signed_custom_fields]
|
15
|
+
|
15
16
|
if coupon
|
16
17
|
s.coupon = coupon
|
17
18
|
s.amount = product.price * (1 - s.coupon.percent_off / 100.0)
|
@@ -9,6 +9,7 @@
|
|
9
9
|
allow_remember_me = local_assigns.fetch :allow_remember_me, true
|
10
10
|
email = local_assigns.fetch :email, ''
|
11
11
|
verify_zip_code = local_assigns.fetch :verify_zip_code, false
|
12
|
+
custom_fields = local_assigns.fetch :custom_fields, nil
|
12
13
|
|
13
14
|
sale = Payola::Sale.new(product: sellable)
|
14
15
|
|
@@ -38,7 +39,7 @@
|
|
38
39
|
<% end %>
|
39
40
|
<% end %>
|
40
41
|
<script type="text/javascript">
|
41
|
-
|
42
|
+
PayolaCheckout.setUpStripeCheckoutButton({
|
42
43
|
base_path: "<%= main_app.payola_path %>",
|
43
44
|
form_id: "<%= form_id %>",
|
44
45
|
button_id: "<%= button_id %>",
|
@@ -54,5 +55,8 @@
|
|
54
55
|
allow_remember_me: <%= allow_remember_me %>,
|
55
56
|
email: "<%= email %>",
|
56
57
|
verify_zip_code: <%= verify_zip_code %>
|
58
|
+
<% if custom_fields %>
|
59
|
+
,signed_custom_fields: "<%= sale.verifier.generate(custom_fields) %>"
|
60
|
+
<% end %>
|
57
61
|
});
|
58
62
|
</script>
|
data/lib/payola/version.rb
CHANGED
@@ -2,6 +2,72 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Payola
|
4
4
|
describe TransactionsController do
|
5
|
+
before do
|
6
|
+
@product = create(:product)
|
7
|
+
Payola.register_sellable(@product.class)
|
8
|
+
end
|
5
9
|
|
10
|
+
describe '#create' do
|
11
|
+
it "should pass args to CreateSale and queue the job" do
|
12
|
+
sale = double
|
13
|
+
sale.should_receive(:save).and_return(true)
|
14
|
+
sale.should_receive(:guid).at_least(1).times.and_return('blah')
|
15
|
+
|
16
|
+
CreateSale.should_receive(:call).and_return(sale)
|
17
|
+
Payola.should_receive(:queue!)
|
18
|
+
post :create, product_class: @product.product_class, permalink: @product.permalink, use_route: :payola
|
19
|
+
|
20
|
+
expect(response.status).to eq 200
|
21
|
+
parsed_body = JSON.load(response.body)
|
22
|
+
expect(parsed_body['guid']).to eq 'blah'
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "with an error" do
|
26
|
+
it "should return an error in json" do
|
27
|
+
sale = double
|
28
|
+
sale.should_receive(:save).and_return(false)
|
29
|
+
error = double
|
30
|
+
error.should_receive(:full_messages).and_return(['done did broke'])
|
31
|
+
sale.should_receive(:errors).and_return(error)
|
32
|
+
|
33
|
+
CreateSale.should_receive(:call).and_return(sale)
|
34
|
+
Payola.should_not_receive(:queue!)
|
35
|
+
|
36
|
+
post :create, product_class: @product.product_class, permalink: @product.permalink, use_route: :payola
|
37
|
+
|
38
|
+
expect(response.status).to eq 400
|
39
|
+
parsed_body = JSON.load(response.body)
|
40
|
+
expect(parsed_body['error']).to eq 'done did broke'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#status' do
|
46
|
+
it "should return 404 if it can't find the sale" do
|
47
|
+
get :status, guid: 'doesnotexist', use_route: :payola
|
48
|
+
expect(response.status).to eq 404
|
49
|
+
end
|
50
|
+
it "should return json with properties" do
|
51
|
+
sale = create(:sale)
|
52
|
+
get :status, guid: sale.guid, use_route: :payola
|
53
|
+
|
54
|
+
expect(response.status).to eq 200
|
55
|
+
|
56
|
+
parsed_body = JSON.load(response.body)
|
57
|
+
|
58
|
+
expect(parsed_body['guid']).to eq sale.guid
|
59
|
+
expect(parsed_body['status']).to eq sale.state
|
60
|
+
expect(parsed_body['error']).to be_nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#show' do
|
65
|
+
it "should redirect to the product's redirect path" do
|
66
|
+
sale = create(:sale)
|
67
|
+
get :show, guid: sale.guid, use_route: :payola
|
68
|
+
|
69
|
+
expect(response).to redirect_to '/'
|
70
|
+
end
|
71
|
+
end
|
6
72
|
end
|
7
73
|
end
|
@@ -1 +1,18 @@
|
|
1
|
-
<%= render 'payola/transactions/checkout',
|
1
|
+
<%= render 'payola/transactions/checkout', sellable: @product, button_class: 'not-a-class' %>
|
2
|
+
|
3
|
+
<hr>
|
4
|
+
|
5
|
+
<%= form_for @product, url: '/', method: :post, html: { class: 'payola-payment-form', 'data-payola-base-path' => '/subdir/payola', 'data-payola-product' => @product.product_class, 'data-payola-permalink' => @product.permalink } do |f| %>
|
6
|
+
<span class="payola-payment-error"></span>
|
7
|
+
Email:<br>
|
8
|
+
<input type="email" name="stripeEmail" data-payola="email"></input><br>
|
9
|
+
Card Number<br>
|
10
|
+
<input type="text" data-stripe="number"></input><br>
|
11
|
+
Exp Month<br>
|
12
|
+
<input type="text" data-stripe="exp_month"></input><br>
|
13
|
+
Exp Year<br>
|
14
|
+
<input type="text" data-stripe="exp_year"></input><br>
|
15
|
+
CVC<br>
|
16
|
+
<input type="text" data-stripe="cvc"></input><br>
|
17
|
+
<input type="submit"></input>
|
18
|
+
<% end %>
|