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.
Files changed (52) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/payola/checkout_button.js +12 -8
  3. data/app/assets/javascripts/payola/form.js +70 -0
  4. data/app/controllers/payola/transactions_controller.rb +2 -6
  5. data/app/helpers/payola/price_helper.rb +2 -2
  6. data/app/models/payola/sale.rb +28 -9
  7. data/app/services/payola/charge_card.rb +5 -5
  8. data/app/services/payola/create_sale.rb +4 -3
  9. data/app/views/payola/transactions/_checkout.html.erb +5 -1
  10. data/app/views/payola/transactions/_stripe_header.html.erb +4 -0
  11. data/db/migrate/20141026101628_add_custom_fields_to_payola_sale.rb +5 -0
  12. data/db/migrate/20141026144800_rename_custom_fields_to_signed_custom_fields_on_payola_sale.rb +5 -0
  13. data/lib/payola/version.rb +1 -1
  14. data/spec/controllers/payola/transactions_controller_spec.rb +66 -0
  15. data/spec/dummy/app/controllers/buy_controller.rb +1 -1
  16. data/spec/dummy/app/views/buy/index.html.erb +18 -1
  17. data/spec/dummy/app/views/layouts/application.html.erb +2 -1
  18. data/spec/dummy/config/application.rb +1 -0
  19. data/spec/dummy/config/locales/de.yml +203 -0
  20. data/spec/dummy/db/development.sqlite3 +0 -0
  21. data/spec/dummy/db/schema.rb +2 -1
  22. data/spec/dummy/db/test.sqlite3 +0 -0
  23. data/spec/dummy/log/development.log +2116 -0
  24. data/spec/dummy/log/test.log +18776 -0
  25. data/spec/dummy/tmp/cache/assets/development/sprockets/0e585aeb88c1555ae8f5292f7c7a9068 +0 -0
  26. data/spec/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  27. data/spec/dummy/tmp/cache/assets/development/sprockets/1b54952339cbc96811097cee5f8923b4 +0 -0
  28. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  29. data/spec/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  30. data/spec/dummy/tmp/cache/assets/development/sprockets/5afa4bc970e0bc781b3500c696ba25ff +0 -0
  31. data/spec/dummy/tmp/cache/assets/development/sprockets/664efeb51d1b486dcfc30d142db56f3c +0 -0
  32. data/spec/dummy/tmp/cache/assets/development/sprockets/68a3bbdc64a0729b5896fa1e33fce4b1 +0 -0
  33. data/spec/dummy/tmp/cache/assets/development/sprockets/96b1ccdc193a02b2bbea195310262fa5 +0 -0
  34. data/spec/dummy/tmp/cache/assets/development/sprockets/aea159adbc0944b120d91d5b578612ba +0 -0
  35. data/spec/dummy/tmp/cache/assets/development/sprockets/b20dab973e971c61bc7e334a38453ae0 +0 -0
  36. data/spec/dummy/tmp/cache/assets/development/sprockets/b83da90cb58011313e13a97a4a41cb6e +0 -0
  37. data/spec/dummy/tmp/cache/assets/development/sprockets/c0b954b40f2881a6d286061bc069643e +0 -0
  38. data/spec/dummy/tmp/cache/assets/development/sprockets/c5b3877d49b39932a7557611cefca9a6 +0 -0
  39. data/spec/dummy/tmp/cache/assets/development/sprockets/ca491e325ff6948be9292f701aa4ce64 +0 -0
  40. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  41. data/spec/dummy/tmp/cache/assets/development/sprockets/d3f4b67cc032016dfeebc71f2148b9dc +0 -0
  42. data/spec/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  43. data/spec/dummy/tmp/cache/assets/development/sprockets/d9856638a3c7b9cfd9d1b884e3b33982 +0 -0
  44. data/spec/dummy/tmp/cache/assets/development/sprockets/ddba6e841f28d3005e828fbfffd18b74 +0 -0
  45. data/spec/dummy/tmp/cache/assets/development/sprockets/df935e399019055729aa968bd7062096 +0 -0
  46. data/spec/dummy/tmp/cache/assets/development/sprockets/ef82c31948061751bb026663b54484e1 +0 -0
  47. data/spec/dummy/tmp/cache/assets/development/sprockets/f0b5d4bea0c6cdd1904e1e9a0a45532f +0 -0
  48. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  49. data/spec/helpers/payola/price_helper_spec.rb +17 -0
  50. data/spec/models/payola/sale_spec.rb +15 -0
  51. data/spec/services/payola/create_sale_spec.rb +1 -1
  52. metadata +58 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Nzc3ZTZjYjM5M2I0MGZlMDMxYjI0Mzc2ZjU2ZjM1NjU0ZDVmZDA3Ng==
4
+ N2E1ZTQyZDI1ZGM1YjczYmQzOGQxMTRiODIyMWUxNGFiZGNjMDdlOQ==
5
5
  data.tar.gz: !binary |-
6
- MTY0MzBlYTM1NjNlYmU3OGFiMTQyN2ZiNjZjYmQzYzc3ZTI0YzEwNg==
6
+ ODgxMDljMjc2ODcyMzM0ZjdiODM4NGUzYWE5Zjg2NWExYmZmN2QyOA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ODcxYzEwYmYzYjJhMjUyMjhiYjk2ODdiMzM4NTNiZjY1M2JiMjNmNTE5YjE5
10
- YzRjZWRmNjAwZThhZTgxNGE5ZDQ4ZTFmNTQwZmYwMDg1ZjhmYzBmMzdkYWYz
11
- ZjNjZTk2MjRkYTZlN2E5NzlkNmFiYTVjZTRiMzg3YTdjYjgwNTg=
9
+ ZDJjZmNiMWRiYTY0YzZjYjllMDRiYzAyM2E1MTE2MmM2ZWQ5ZjMyZWNkYmE1
10
+ YmIxYjk2NWYxZWQ3OWUzODY2MGUyMmY4NmU4NTZmM2Y0Yjc4YTg3MGY2MTJl
11
+ YThmMDZlOWIyZWRlYmIwMDBkZTg2OWE5ZTNhOGM3OTJhM2UzOGQ=
12
12
  data.tar.gz: !binary |-
13
- N2VjMjFkMmI2YjAyYzgxNTBhZTYxOGI0ZTE1NzY2Y2M1OWFiMWM4ZTIxN2Ey
14
- NDUwYWIwY2UxNzY0ODFjNTYwOGQzNDQ3NmY5MjUxMjMwMjRiYzhlMTQ3ZDI2
15
- NjQ2YjJkMGFjNTJiMmFmY2FhNTlmN2IyNGU4NDBjN2U0MmZjNDc=
13
+ MTFhYWNhZGU5MTExMTY2ODc1NmI1ZGE4ZjA2NmMzNjFhYjFjMGMwMjlkZGJl
14
+ MWI1NWQ5MWZjYTM0OWY2ZTAwNjNkYjUwMTNmM2NiNzA4YWI0NDg2ZWJjOWEw
15
+ NWFlNDUwNGI5ZGYxZDM0YWVkYTkwZWM3N2Q5MjA3YjMwMzAzNjA=
@@ -1,16 +1,16 @@
1
- var Payola = {
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) { Payola.tokenHandler(token, options) }
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.amount,
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) { Payola.poll(data.guid, 60, options) },
36
- error: function(data) { Payola.showError(data.responseJSON.error, options) }
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
- Payola.showError("This seems to be taking too long. Please contact support and give them transaction ID: " + guid, options);
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
- Payola.showError(data.error, options);
63
+ PayolaCheckout.showError(data.error, options);
60
64
  } else {
61
- setTimeout(function() { Payola.poll(guid, num_retries_left - 1, options) }, 500);
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 = sale_params.merge(
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
@@ -1,7 +1,7 @@
1
1
  module Payola
2
2
  module PriceHelper
3
- def formatted_price(amount)
4
- sprintf("$%0.2f", (amount || 0) / 100.0)
3
+ def formatted_price(amount, opts = {})
4
+ number_to_currency((amount || 0) / 100.0, opts)
5
5
  end
6
6
  end
7
7
  end
@@ -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
- Payola.charge_verifier.call(sale)
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[:email] || params[:stripeEmail]
11
- s.stripe_token = params[:stripeToken] || params[:stripe_token]
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
- Payola.setUpStripeCheckoutButton({
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>
@@ -0,0 +1,4 @@
1
+ <script type="text/javascript" src="https://js.stripe.com/v2/"></script>
2
+ <script type="text/javascript">
3
+ Stripe.setPublishableKey("<%= Payola.publishable_key %>");
4
+ </script>
@@ -0,0 +1,5 @@
1
+ class AddCustomFieldsToPayolaSale < ActiveRecord::Migration
2
+ def change
3
+ add_column :payola_sales, :custom_fields, :text
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class RenameCustomFieldsToSignedCustomFieldsOnPayolaSale < ActiveRecord::Migration
2
+ def change
3
+ rename_column :payola_sales, :custom_fields, :signed_custom_fields
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Payola
2
- VERSION = "1.0.7"
2
+ VERSION = "1.0.8"
3
3
  end
@@ -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
@@ -2,6 +2,6 @@ class BuyController < ApplicationController
2
2
  helper Payola::PriceHelper
3
3
 
4
4
  def index
5
- @sale = Payola::Sale.new(product: Product.first)
5
+ @product = Product.first
6
6
  end
7
7
  end
@@ -1 +1,18 @@
1
- <%= render 'payola/transactions/checkout', sale: @sale, button_class: 'not-a-class' %>
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 %>