payola-payments 1.0.8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +8 -8
  2. data/app/assets/javascripts/payola/checkout_button.js +21 -12
  3. data/app/controllers/payola/transactions_controller.rb +1 -1
  4. data/app/mailers/payola/admin_mailer.rb +35 -0
  5. data/app/mailers/payola/receipt_mailer.rb +47 -0
  6. data/app/models/payola/sale.rb +10 -2
  7. data/app/services/payola/create_sale.rb +1 -1
  8. data/app/services/payola/process_sale.rb +7 -0
  9. data/app/services/payola/send_mail.rb +7 -0
  10. data/app/views/payola/admin_mailer/dispute.html.erb +10 -0
  11. data/app/views/payola/admin_mailer/failure.html.erb +11 -0
  12. data/app/views/payola/admin_mailer/receipt.html.erb +10 -0
  13. data/app/views/payola/admin_mailer/refund.html.erb +10 -0
  14. data/app/views/payola/receipt_mailer/receipt.html.erb +4 -0
  15. data/app/views/payola/receipt_mailer/receipt.text.erb +7 -0
  16. data/app/views/payola/receipt_mailer/receipt_pdf.html.erb +83 -0
  17. data/app/views/payola/receipt_mailer/refund.html.erb +8 -0
  18. data/app/views/payola/transactions/_checkout.html.erb +31 -23
  19. data/db/migrate/20141029135848_add_owner_to_payola_sale.rb +8 -0
  20. data/lib/generators/payola/install_generator.rb +1 -1
  21. data/lib/payola.rb +33 -5
  22. data/lib/payola/version.rb +1 -1
  23. data/lib/payola/worker/active_job.rb +4 -4
  24. data/lib/payola/worker/base.rb +2 -2
  25. data/lib/payola/worker/sidekiq.rb +2 -2
  26. data/lib/payola/worker/sucker_punch.rb +2 -2
  27. data/spec/dummy/app/models/owner.rb +2 -0
  28. data/spec/dummy/db/development.sqlite3 +0 -0
  29. data/spec/dummy/db/migrate/20141029140518_create_owners.rb +8 -0
  30. data/spec/dummy/db/schema.rb +9 -1
  31. data/spec/dummy/db/test.sqlite3 +0 -0
  32. data/spec/dummy/log/development.log +1946 -0
  33. data/spec/dummy/log/test.log +10516 -0
  34. data/spec/dummy/tmp/cache/assets/development/sprockets/0e585aeb88c1555ae8f5292f7c7a9068 +0 -0
  35. data/spec/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  36. data/spec/dummy/tmp/cache/assets/development/sprockets/5afa4bc970e0bc781b3500c696ba25ff +0 -0
  37. data/spec/dummy/tmp/cache/assets/development/sprockets/664efeb51d1b486dcfc30d142db56f3c +0 -0
  38. data/spec/dummy/tmp/cache/assets/development/sprockets/68a3bbdc64a0729b5896fa1e33fce4b1 +0 -0
  39. data/spec/dummy/tmp/cache/assets/development/sprockets/b83da90cb58011313e13a97a4a41cb6e +0 -0
  40. data/spec/dummy/tmp/cache/assets/development/sprockets/c0b954b40f2881a6d286061bc069643e +0 -0
  41. data/spec/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  42. data/spec/dummy/tmp/cache/assets/development/sprockets/d9856638a3c7b9cfd9d1b884e3b33982 +0 -0
  43. data/spec/dummy/tmp/cache/assets/development/sprockets/ef82c31948061751bb026663b54484e1 +0 -0
  44. data/spec/dummy/tmp/cache/assets/development/sprockets/f0b5d4bea0c6cdd1904e1e9a0a45532f +0 -0
  45. data/spec/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  46. data/spec/models/payola/sale_spec.rb +16 -0
  47. data/spec/payola_spec.rb +32 -0
  48. data/spec/worker_spec.rb +8 -6
  49. metadata +19 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- N2E1ZTQyZDI1ZGM1YjczYmQzOGQxMTRiODIyMWUxNGFiZGNjMDdlOQ==
4
+ YzAxOTU3MDMyMWI1YmEyYTA3OWM4ZDY4YzRhODllNzNjMjhhNTczMw==
5
5
  data.tar.gz: !binary |-
6
- ODgxMDljMjc2ODcyMzM0ZjdiODM4NGUzYWE5Zjg2NWExYmZmN2QyOA==
6
+ NDhkMWE5YjZkNDE4NjEwMDMyYjkyZDEwZGYzZDAxNmNiYjA0N2RhYQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZDJjZmNiMWRiYTY0YzZjYjllMDRiYzAyM2E1MTE2MmM2ZWQ5ZjMyZWNkYmE1
10
- YmIxYjk2NWYxZWQ3OWUzODY2MGUyMmY4NmU4NTZmM2Y0Yjc4YTg3MGY2MTJl
11
- YThmMDZlOWIyZWRlYmIwMDBkZTg2OWE5ZTNhOGM3OTJhM2UzOGQ=
9
+ ZTExMTljZDNmMTk4NTg5OTQ5NWEzYTA5MDE3OTc5MmNkODY2YTIzNmRiNGI1
10
+ NTUzNzI2NDlhM2NiMzViNzExY2IwNGZmNWYwYWI5NjY2MTg0OGM3NDRiOGQ4
11
+ OTBlZjU3ZDk4NDhkZjIzNTBhNTBmNjQ5MDI3ZGQ3OGUzMzYzZjc=
12
12
  data.tar.gz: !binary |-
13
- MTFhYWNhZGU5MTExMTY2ODc1NmI1ZGE4ZjA2NmMzNjFhYjFjMGMwMjlkZGJl
14
- MWI1NWQ5MWZjYTM0OWY2ZTAwNjNkYjUwMTNmM2NiNzA4YWI0NDg2ZWJjOWEw
15
- NWFlNDUwNGI5ZGYxZDM0YWVkYTkwZWM3N2Q5MjA3YjMwMzAzNjA=
13
+ NjkzMTUwMzVlMTczZTIwYmZkMzk4NjQ3ODI3MTI0YjdkYjczNDA5ZTZiYjNi
14
+ MWNhYTAxYjg3NzVhMDc0MTU5ODQ4OTk1MmYzZjUxN2IzODI3NTk1NDNlMTc1
15
+ YTA0YWQ0YWVmMTE2OTFiMTM3NjJmNjY4NDM1YjgwYzgxNjFiNTg=
@@ -1,22 +1,30 @@
1
1
  var PayolaCheckout = {
2
- setUpStripeCheckoutButton: function(options) {
2
+ initialize: function() {
3
+ $(document).on('click', '.payola-checkout-button', function(e) {
4
+ e.preventDefault();
5
+ PayolaCheckout.handleCheckoutButtonClick($(this));
6
+ });
7
+ },
8
+
9
+ handleCheckoutButtonClick: function(button) {
10
+ form = button.parent('form');
11
+ options = form.data();
12
+
3
13
  var handler = StripeCheckout.configure({
4
14
  key: options.publishable_key,
5
15
  image: options.product_image_path,
6
16
  token: function(token) { PayolaCheckout.tokenHandler(token, options) }
7
17
  });
8
18
 
9
- document.getElementById(options.button_id).addEventListener('click', function(e) {
10
- handler.open({
11
- name: options.name,
12
- description: options.description,
13
- amount: options.price,
14
- panelLabel: options.panel_label,
15
- allowRememberMe: options.allow_remember_me,
16
- zipCode: options.verify_zip_code,
17
- email: options.email || undefined
18
- });
19
- e.preventDefault();
19
+ handler.open({
20
+ name: options.name,
21
+ description: options.description,
22
+ amount: options.price,
23
+ panelLabel: options.panel_label,
24
+ allowRememberMe: options.allow_remember_me,
25
+ zipCode: options.verify_zip_code,
26
+ currency: options.currency,
27
+ email: options.email || undefined
20
28
  });
21
29
  },
22
30
 
@@ -67,3 +75,4 @@ var PayolaCheckout = {
67
75
  });
68
76
  }
69
77
  }
78
+ PayolaCheckout.initialize();
@@ -26,7 +26,7 @@ module Payola
26
26
  @sale = CreateSale.call(create_params)
27
27
 
28
28
  if @sale.save
29
- Payola.queue!(@sale)
29
+ Payola.queue!(Payola::ProcessSale, @sale.guid)
30
30
  render json: { guid: @sale.guid }
31
31
  else
32
32
  render json: { error: @sale.errors.full_messages.join(". ") }, status: 400
@@ -0,0 +1,35 @@
1
+ module Payola
2
+ class AdminMailer < ActionMailer::Base
3
+ def receipt(sale_guid)
4
+ ActiveRecord::Base.connection_pool.with_connection do
5
+ @sale = Payola::Sale.find_by(guid: sale_guid)
6
+ @product = @sale.product
7
+ mail(from: Payola.support_email, to: Payola.support_email)
8
+ end
9
+ end
10
+
11
+ def refund(sale_guid)
12
+ ActiveRecord::Base.connection_pool.with_connection do
13
+ @sale = Payola::Sale.find_by(guid: sale_guid)
14
+ @product = @sale.product
15
+ mail(from: Payola.support_email, to: Payola.support_email)
16
+ end
17
+ end
18
+
19
+ def dispute(sale_guid)
20
+ ActiveRecord::Base.connection_pool.with_connection do
21
+ @sale = Payola::Sale.find_by(guid: sale_guid)
22
+ @product = @sale.product
23
+ mail(from: Payola.support_email, to: Payola.support_email)
24
+ end
25
+ end
26
+
27
+ def failure(sale_guid)
28
+ ActiveRecord::Base.connection_pool.with_connection do
29
+ @sale = Payola::Sale.find_by(guid: sale_guid)
30
+ @product = @sale.product
31
+ mail(from: Payola.support_email, to: Payola.support_email)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ module Payola
2
+ class ReceiptMailer < ActionMailer::Base
3
+ add_template_helper ::ApplicationHelper
4
+
5
+ def receipt(sale_guid)
6
+ ActiveRecord::Base.connection_pool.with_connection do
7
+ @sale = Payola::Sale.find_by(guid: sale_guid)
8
+ @product = @sale.product
9
+
10
+ if Payola.pdf_receipt
11
+ pdf = Docverter::Conversion.run do |c|
12
+ c.from = 'html'
13
+ c.to = 'pdf'
14
+ c.content = render_to_string('payola/receipt_mailer/receipt_pdf.html')
15
+ end
16
+ attachments["receipt-#{@sale.guid}.pdf"] = pdf
17
+ end
18
+
19
+ mail_params = {
20
+ to: @sale.email,
21
+ subject: @product.respond_to?(:receipt_subject) ? @product.receipt_subject(@sale) : 'Purchase Receipt',
22
+ }
23
+
24
+ if @product.respond_to?(:receipt_from_address)
25
+ mail_params[:from] = @product.receipt_from_address(@sale)
26
+ end
27
+
28
+ mail(mail_params)
29
+ end
30
+ end
31
+
32
+ def refund(sale_guid)
33
+ ActiveRecord::Base.connection_pool.with_connection do
34
+ @sale = Payola::Sale.find_by(guid: sale_guid)
35
+ @product = @sale.product
36
+
37
+ mail_params = {
38
+ to: @sale.email,
39
+ subject: @product.respond_to?(:refund_subject) ? @product.refund_subject(@sale) : 'Refund Confirmation',
40
+ }
41
+
42
+ mail(mail_params)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -15,6 +15,7 @@ module Payola
15
15
  before_save :populate_guid
16
16
 
17
17
  belongs_to :product, polymorphic: true
18
+ belongs_to :owner, polymorphic: true
18
19
  belongs_to :coupon
19
20
  belongs_to :affiliate
20
21
 
@@ -81,22 +82,29 @@ module Payola
81
82
 
82
83
  def instrument_finish
83
84
  Payola.instrument(instrument_key('finished'), self)
85
+ Payola.instrument(instrument_key('finished', false), self)
84
86
  end
85
87
 
86
88
  def instrument_fail
87
89
  Payola.instrument(instrument_key('failed'), self)
90
+ Payola.instrument(instrument_key('failed', false), self)
88
91
  end
89
92
 
90
93
  def instrument_refund
91
94
  Payola.instrument(instrument_key('refunded'), self)
95
+ Payola.instrument(instrument_key('refunded', false), self)
92
96
  end
93
97
 
94
98
  def product_class
95
99
  product.product_class
96
100
  end
97
101
 
98
- def instrument_key(instrument_type)
99
- "payola.#{product_class}.sale.#{instrument_type}"
102
+ def instrument_key(instrument_type, include_class=true)
103
+ if include_class
104
+ "payola.#{product_class}.sale.#{instrument_type}"
105
+ else
106
+ "payola.sale.#{instrument_type}"
107
+ end
100
108
  end
101
109
 
102
110
  def populate_guid
@@ -10,7 +10,7 @@ module Payola
10
10
  s.email = params[:stripeEmail]
11
11
  s.stripe_token = params[:stripeToken]
12
12
  s.affiliate_id = affiliate.try(:id)
13
- s.currency = product.respond_to?(:currency) ? product.currency : 'usd'
13
+ s.currency = product.respond_to?(:currency) ? product.currency : Payola.default_currency
14
14
  s.signed_custom_fields = params[:signed_custom_fields]
15
15
 
16
16
  if coupon
@@ -0,0 +1,7 @@
1
+ module Payola
2
+ class ProcessSale
3
+ def self.call(guid)
4
+ Sale.find_by(guid: guid).process!
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Payola
2
+ class SendMail
3
+ def self.call(mailer, method, *args)
4
+ mailer.send(method, *args).deliver
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ <!doctype html>
2
+ <html>
3
+ <body>
4
+ <p>Dispute! Boo!</p>
5
+ <p>To: <%= @sale.email %></p>
6
+ <p>Guid: <%= @sale.guid %></p>
7
+ <p>Product: <%= @product.name %></p>
8
+ <p>Amount: <%= formatted_price(@sale.amount) %></p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html>
3
+ <body>
4
+ <p>Failure!</p>
5
+ <p>To: <%= @sale.email %></p>
6
+ <p>Guid: <%= @sale.guid %></p>
7
+ <p>Product: <%= @product.name %></p>
8
+ <p>Amount: <%= formatted_price(@sale.amount) %></p>
9
+ <p>Error: <%= @sale.error %></p>
10
+ </body>
11
+ </html>
@@ -0,0 +1,10 @@
1
+ <!doctype html>
2
+ <html>
3
+ <body>
4
+ <p>Sale! Woohoo!</p>
5
+ <p>To: <%= @sale.email %></p>
6
+ <p>Guid: <%= @sale.guid %></p>
7
+ <p>Product: <%= @product.name %></p>
8
+ <p>Amount: <%= formatted_price(@sale.amount) %></p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,10 @@
1
+ <!doctype html>
2
+ <html>
3
+ <body>
4
+ <p>Refund!</p>
5
+ <p>To: <%= @sale.email %></p>
6
+ <p>Guid: <%= @sale.guid %></p>
7
+ <p>Product: <%= @product.name %></p>
8
+ <p>Amount: <%= formatted_price(@sale.amount) %></p>
9
+ </body>
10
+ </html>
@@ -0,0 +1,4 @@
1
+ <p>Thank you for buying <%= @product.name %></p>
2
+ <p>Your transaction ID is: <b><%= @sale.guid %></p>
3
+ <p><%= link_to "Click here to pick up your product", payola.confirm_url(guid: @sale.guid) %></p>
4
+ <p>Attached you'll find a PDF receipt for your records.</p>
@@ -0,0 +1,7 @@
1
+ Thanks for buying <%= @product.name %>.
2
+
3
+ Your transaction ID is: <%= @sale.guid %>
4
+
5
+ Click here to read the book: <%= payola.confirm_url(guid: @sale.guid) %>
6
+
7
+ Attached you'll find a PDF receipt for your records.
@@ -0,0 +1,83 @@
1
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
2
+ "http://www.w3.org/TR/html4/loose.dtd">
3
+ <html>
4
+ <head>
5
+ <title>PDF Receipt</title>
6
+ <style type="text/css">
7
+ body {
8
+ font-family:Tahoma, sans-serif;
9
+ font-size: 0.75em;
10
+ }
11
+
12
+ img {
13
+ border:0;
14
+ }
15
+
16
+ #address {
17
+ height:181px;
18
+ }
19
+
20
+ table {
21
+ width:100%;
22
+ }
23
+
24
+ td {
25
+ padding:5px;
26
+ }
27
+
28
+ tr.odd {
29
+ background:#e1ffe1;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <div id="page">
35
+ <div id="address">
36
+ <p>
37
+ Transaction # <%= @sale.guid %><br />
38
+ Created on <%= @sale.created_at.strftime('%Y-%m-%d') %><br />
39
+ </p>
40
+ </div><!--end address-->
41
+
42
+ <div id="content">
43
+ <p>
44
+ <strong>Customer Details</strong><br />
45
+ Email: <%= @sale.email %><br />
46
+ Payment Method:
47
+ <% if @sale.stripe_id %>
48
+ <%= @sale.card_type %> ending in <%= @sale.card_last4 %>
49
+ <% else %>
50
+ <%= @sale.stripe_token %>
51
+ <% end %>
52
+ <% if @sale.customer_address %>
53
+ <br><br>
54
+ <%= @sale.customer_address.html_safe %>
55
+ <% end %>
56
+ </p>
57
+ <hr>
58
+ <table>
59
+ <tr>
60
+ <td colspan="2"><strong>Description</strong></td>
61
+ <td><strong>Price</strong></td>
62
+ <td><strong>Amount</strong></td>
63
+ </tr>
64
+ <tr class="odd">
65
+ <td colspan="2"><%= truncate(@product.name, length: 50) %></td>
66
+ <td><%= formatted_price(@product.price) %></td>
67
+ <td><%= formatted_price(@sale.amount) %></td>
68
+ </tr>
69
+ <tr>
70
+ <td colspan="2">&nbsp;</td>
71
+ <td><strong>Total</strong></td>
72
+ <td><strong><%= formatted_price(@sale.amount) %></strong></td>
73
+ </tr>
74
+ </table>
75
+ <hr>
76
+ <p>
77
+ Thank you for your order! If you have any questions please email <%= Payola.support_email %>.
78
+ </p>
79
+ </div><!--end content-->
80
+ </div><!--end page-->
81
+ </body>
82
+
83
+ </html>
@@ -0,0 +1,8 @@
1
+ <!doctype html>
2
+ <html>
3
+ <body>
4
+ <p>
5
+ This email confirms that your purchase has been refunded. <%= formatted_price(@sale.amount) %> will be returned to your <%= @sale.card_type %> ending in <%= @sale.card_last4 %> within 5-10 business days.
6
+ </p>
7
+ </body>
8
+ </html>
@@ -10,6 +10,7 @@
10
10
  email = local_assigns.fetch :email, ''
11
11
  verify_zip_code = local_assigns.fetch :verify_zip_code, false
12
12
  custom_fields = local_assigns.fetch :custom_fields, nil
13
+ price = local_assigns.fetch :price, sellable.price
13
14
 
14
15
  sale = Payola::Sale.new(product: sellable)
15
16
 
@@ -17,6 +18,8 @@
17
18
 
18
19
  form_id = "#{button_id}-form"
19
20
 
21
+ currency = sellable.respond_to?(:currency) ? sellable.currency : Payola.default_currency
22
+
20
23
  error_div_id = local_assigns.fetch :error_div_id, ''
21
24
  if error_div_id.present?
22
25
  show_default_error_div = false
@@ -24,12 +27,39 @@
24
27
  error_div_id = "#{button_id}-errors"
25
28
  show_default_error_div = true
26
29
  end
30
+
31
+ raw_data = {
32
+ base_path: main_app.payola_path,
33
+ form_id: form_id,
34
+ button_id: button_id,
35
+ error_div_id: error_div_id,
36
+ product_class: sellable.product_class,
37
+ product_permalink: sellable.permalink,
38
+ price: price,
39
+ name: name,
40
+ description: description,
41
+ currency: currency,
42
+ product_image_path: product_image_path,
43
+ publishable_key: Payola.publishable_key_for_sale(sale),
44
+ panel_label: panel_label,
45
+ allow_remember_me: allow_remember_me,
46
+ email: email,
47
+ verify_zip_code: verify_zip_code
48
+ }
49
+
50
+ raw_data[:signed_custom_fields] = sale.verifier.generate(custom_fields) if custom_fields
51
+
52
+ html_hash = {}
53
+ raw_data.each do |k,v|
54
+ html_hash["data-#{k}"] = v
55
+ end
56
+ html_hash["id"] = form_id
27
57
  %>
28
58
 
29
59
  <script src="https://checkout.stripe.com/checkout.js"></script>
30
60
  <link rel="stylesheet" href="https://checkout.stripe.com/v3/checkout/button.css"></link>
31
61
 
32
- <%= form_tag payola.buy_path(product_class: sellable.product_class, permalink: sellable.permalink), id: form_id do %>
62
+ <%= form_tag payola.buy_path(product_class: sellable.product_class, permalink: sellable.permalink), html_hash do %>
33
63
  <button class="<%= button_class %> payola-checkout-button" id="<%= button_id %>">
34
64
  <span class="payola-checkout-button-text" style="display: block; <%= button_inner_style %>"><%= button_text %></span>
35
65
  <span class="payola-checkout-button-spinner" style="display: none; <%= button_inner_style %>">Please wait...</span>
@@ -38,25 +68,3 @@
38
68
  <div class="payola-error-target" style="display:none", id="<%= error_div_id %>"></div>
39
69
  <% end %>
40
70
  <% end %>
41
- <script type="text/javascript">
42
- PayolaCheckout.setUpStripeCheckoutButton({
43
- base_path: "<%= main_app.payola_path %>",
44
- form_id: "<%= form_id %>",
45
- button_id: "<%= button_id %>",
46
- error_div_id: "<%= error_div_id %>",
47
- product_class: "<%= sellable.product_class %>",
48
- product_permalink: "<%= sellable.permalink %>",
49
- price: <%= sellable.price %>,
50
- name: "<%= name %>",
51
- description: "<%= description %>",
52
- product_image_path: "<%= product_image_path %>",
53
- publishable_key: "<%= Payola.publishable_key_for_sale(sale) %>",
54
- panel_label: "<%= panel_label %>",
55
- allow_remember_me: <%= allow_remember_me %>,
56
- email: "<%= email %>",
57
- verify_zip_code: <%= verify_zip_code %>
58
- <% if custom_fields %>
59
- ,signed_custom_fields: "<%= sale.verifier.generate(custom_fields) %>"
60
- <% end %>
61
- });
62
- </script>