payola-payments 1.0.8 → 1.1.0

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 (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>