Coupons
Coupons is a Rails engine for creating discount coupons.
Installation
Add this line to your application's Gemfile:
gem 'coupons'
You also need one pagination library. You can choose between paginate or kaminari, so make sure one of these libs is added to your Gemfile as well.
gem 'paginate'
# or
gem 'kaminari'
And then execute:
$ bundle
Or install it yourself as:
$ gem install coupons
Usage
After installing Coupons
, add the following line to your config/routes.rb
file.
mount Coupons::Engine => '/', as: 'coupons_engine'
You can visit /coupons
to access the dashboard.
Creating coupons
There are two types of coupons: percentage or amount.
- percentage: applies the percentage discount to total amount.
- amount: applies the amount discount to the total amount.
Defining the coupon code format
The coupon code is generated with Coupons.configuration.generator
. By default, it creates a 6-chars long uppercased alpha-numeric code. You can use any object that implements the call
method and returns a string. The following implementation generates coupon codes like AWESOME-B7CB
.
Coupons.configure do |config|
config.generator = proc do
token = SecureRandom.hex[0, 4].upcase
"AWESOME-#{token}"
end
end
You can always override the generated coupon code through the dashboard or Ruby.
Working with coupons
Imagine that you created the coupon RAILSCONF15
as a $100 discount; you can apply it to any amount using the Coupons.apply
method. Notice that this method won't redeem the coupon code and it's supposed to be used on the checkout page.
Coupons.apply('RAILSCONF15', amount: 600.00)
#=> {:amount => 600.0, :discount => 100.0, :total => 500.0}
When a coupon is invalid/non-redeemable, it returns the discount amount as 0
.
Coupons.apply('invalid', amount: 100.00)
#=> {:amount => 100.0, :discount => 0, :total => 100.0}
To redeem the coupon you can use Coupon.redeem
.
Coupons.redeem('RAILSCONF15', amount: 600.00)
#=> {:amount => 600.0, :discount => 100.0, :total => 500.0}
coupon = Coupons::Models::Coupon.last
coupon.redemptions_count
#=> 1
coupon.redemptions
#=> [#<Coupons::Models::CouponRedemption:0x0000010e388290>]
Defining the coupon finder strategy
By default, the first redeemable coupon is used. You can set any of the following strategies.
-
Coupons::Finders::FirstAvailable
: returns the first redeemable coupon available. -
Coupons::Finders::SmallerDiscount
: returns the smaller redeemable discount available. -
Coupons::Finders::LargerDiscount
: returns the larger redeemable discount available.
To define a different strategy, set the Coupons.configurable.finder
attribute.
Coupons.configure do |config|
config.finder = Coupons::Finders::SmallerDiscount
end
A finder can be any object that receives the coupon code and the options (which must include the amount
key). Here's how the smaller discount finder is implemented.
module Coupons
module Finders
SmallerDiscount = proc do |code, options = {}|
coupons = Models::Coupon.where(code: code).all.select(&:redeemable?)
coupons.min do |a, b|
a = a.apply(options)
b = b.apply(options)
a[:discount] <=> b[:discount]
end
end
end
end
Injecting helper methods
The whole coupon interaction can be made through some helpers methods. You can extend any object with Coupons::Helpers
module. So do it in your initializer file or in your controller, whatever suits you best.
coupons = Object.new.extend(Coupons::Helpers)
Now you can do all the interactions through the coupons
variable.
Authorizing access to the dashboard
Coupons has a flexible authorization system, meaning you can do whatever you want. All you have to do is defining the authorization strategy by setting Coupons.configuration.authorizer
. By default, it disables access to the production
environment, as you can see below.
Coupons.configure do |config|
config.authorizer = proc do |controller|
if Rails.env.production?
controller.render(
text: 'Coupons: not enabled in production environments',
status: 403
)
end
end
end
To define your own strategy, like doing basic authentication, you can do something like this:
Coupons.configure do |config|
config.authorizer = proc do |controller|
controller.authenticate_or_request_with_http_basic do |user, password|
user == 'admin' && password == 'sekret'
end
end
end
Attaching coupons to given records
To be written.
Creating complex discount rules
To be written.
JSON endpoint
You may want to apply discounts using AJAX, so you can give instant feedback. In this case, you'll find the /coupons/apply
endpoint useful.
var response = $.get('/coupons/apply', {amount: 600.0, coupon: 'RAILSCONF15'});
response.done(function(options)) {
console.log(options);
//=> {amount: 600.0, discount: 100.0, total: 500.0}
});
If you provide invalid amount/coupon, then it'll return zero values, like {amount: 0, discount: 0, total: 0}
.
I18n support
Coupons uses I18n. It has support for en
and pt-BR
. You can contribute with your language by translating the file config/en.yml.
Screenshots
Contributing
- Before implementing anything, create an issue to discuss your idea. This only applies to big changes and new features.
- Fork it ( https://github.com/fnando/coupons/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request