Skip to content

Commit 8e95255

Browse files
committed
Opt-in Mailers
This adds some emails you can opt-in to sending to your customers. Opt into them like this: ```ruby Payola.configure do |config| config.send_email_for :receipt, :admin_receipt end ``` Possible emails include: * `:receipt` * `:refund` * `:admin_receipt` * `:admin_dispute` * `:admin_refund` * `:admin_failure` `:receipt` and `:refund` both send to the email address on the `Payola::Sale` instance from the `support_email` address. All of the `:admin` messages are sent from and to the `support_email` address.
1 parent 8e377d2 commit 8e95255

File tree

17 files changed

+339
-4
lines changed

17 files changed

+339
-4
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Payola is a drop-in Rails engine that lets you sell one or more products by just
1212
* Asynchronous payments, usable with any background processing system
1313
* Full webhook integration
1414
* Easy extension hooks for adding your own functionality
15+
* Customizable emails
1516

1617
To see Payola in action, check out the site for [Mastering Modern Payments: Using Stripe with Rails](https://www.masteringmodernpayments.com). Read the book to find out the whys behind Payola's design.
1718

@@ -116,6 +117,12 @@ Payola wraps the StripeEvent gem for event processing and adds a few special sal
116117

117118
(In these examples, `<product_class>` is the underscore'd version of the product's class name.)
118119

120+
You can also subscribe to generic events that do not have the `product_class` included in them. Those are:
121+
122+
* `payola.sale.finished`, when a sale completes successfully
123+
* `payola.sale.failed`, when a charge fails
124+
* `payola.sale.refunded`, when a charge is refunded
125+
119126
### Pre-charge Filter
120127

121128
You can set a callback that Payola will call immediately before attempting to make a charge. You can use this to, for example, check to see if the email address has been used already. To stop Payola from making a charge, throw a `RuntimeError`. The sale will be set to `errored` state and the message attached to the runtime error will be propogated back to the user.
@@ -196,6 +203,34 @@ Payola.background_worker = lambda do |klass, *args|
196203
end
197204
```
198205

206+
### Emails
207+
208+
Payola includes basic emails that you can optionally send to your customers and yourself. Opt into them like this:
209+
210+
```ruby
211+
Payola.configure do |config|
212+
config.send_email_for :receipt, :admin_receipt
213+
end
214+
```
215+
216+
Possible emails include:
217+
218+
* `:receipt`
219+
* `:refund`
220+
* `:admin_receipt`
221+
* `:admin_dispute`
222+
* `:admin_refund`
223+
* `:admin_failure`
224+
225+
`:receipt` and `:refund` both send to the email address on the
226+
`Payola::Sale` instance from the `support_email` address. All of
227+
the `:admin` messages are sent from and to the `support_email`
228+
address.
229+
230+
To customize the content of the emails, copy the appropriate views ([receipt](app/views/payola/receipt_mailer), [admin](app/views/payola/admin_mailer)) into your app at the same path (`app/views/payola/<whatever>`) and modify them as you like. You have access to `@sale` and `@product`, which is just a shortcut to `@sale.product`.
231+
232+
You can include a PDF with your receipt by setting the `include_pdf_receipt` option to `true`. This will send the `receipt_pdf.html` template to [Docverter](http://www.docverter.com) for conversion to PDF. See the [Docverter README](https://github.com/docverter/docverter) for installation instructions if you would like to run your own instance.
233+
199234
## Custom Forms
200235

201236
Payola's custom form support is basic but functional. Setting up a custom form has two steps. First, include the `stripe_header` partial in your layout's `<head>` tag:

app/mailers/payola/admin_mailer.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

app/models/payola/sale.rb

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,29 @@ def charge_card
8282

8383
def instrument_finish
8484
Payola.instrument(instrument_key('finished'), self)
85+
Payola.instrument(instrument_key('finished', false), self)
8586
end
8687

8788
def instrument_fail
8889
Payola.instrument(instrument_key('failed'), self)
90+
Payola.instrument(instrument_key('failed', false), self)
8991
end
9092

9193
def instrument_refund
9294
Payola.instrument(instrument_key('refunded'), self)
95+
Payola.instrument(instrument_key('refunded', false), self)
9396
end
9497

9598
def product_class
9699
product.product_class
97100
end
98101

99-
def instrument_key(instrument_type)
100-
"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
101108
end
102109

103110
def populate_guid

app/services/payola/send_mail.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 commit comments

Comments
 (0)