payola-payments 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 %>