nimbleshop_splitable 0.0.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # nimbleshop_splitable extension
2
+
3
+ This is Splitable extension for [nimbleShop](http://nimbleShop.org) .
4
+
5
+ # Documentation
6
+
7
+ Documentation is available at [http://nimbleshop.org/](http://nimbleshop.org) .
8
+
9
+ # License
10
+
11
+ nimbleshop_splitable uses [MIT license](http://www.opensource.org/licenses/mit-license.php) .
Binary file
Binary file
@@ -0,0 +1,17 @@
1
+ module NimbleshopSplitable
2
+ class PaymentsController < ::ActionController::Base
3
+
4
+ def create
5
+ order = Order.find_by_id(session[:order_id])
6
+ processor = NimbleshopSplitable::Processor.new(order: order)
7
+ error, url = processor.create_split(request: request)
8
+
9
+ respond_to do |format|
10
+ format.html do
11
+ error ? render(text: error) : redirect_to(url)
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,60 @@
1
+ module NimbleshopSplitable
2
+ class SplitablesController < ::Admin::PaymentMethodsController
3
+
4
+ protect_from_forgery except: [:notify]
5
+
6
+ before_filter :load_payment_method
7
+
8
+ def notify
9
+ Rails.logger.info "splitable callback received: #{params.to_yaml}"
10
+
11
+ processor = NimbleshopSplitable::Processor.new(invoice: params[:invoice])
12
+
13
+ if processor.acknowledge(params)
14
+ render nothing: true
15
+ else
16
+ Rails.logger.info "webhook with data #{params.to_yaml} was rejected. error: #{processor.errors.join(',')}"
17
+ render "error: #{error}", status: 403
18
+ end
19
+ end
20
+
21
+ def update
22
+ respond_to do |format|
23
+ if @payment_method.update_attributes(post_params[:splitable])
24
+ format.js {
25
+ flash[:notice] = "Splitable record was successfully updated"
26
+ render js: "window.location = '/admin/payment_methods'"
27
+ }
28
+ else
29
+ msg = @payment_method.errors.full_messages.first
30
+ error = %Q[alert("#{msg}")]
31
+ format.js { render js: error }
32
+ end
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ respond_to do |format|
38
+ if @payment_method.destroy
39
+ format.js {
40
+ flash[:notice] = "Splitable record was successfully deleted"
41
+ render js: "window.location = '/admin/payment_methods'" #TODO hardcoded url
42
+ }
43
+ else
44
+ format.js { render js: 'Splitable record could not be deleted. Please try again later.' }
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def post_params
52
+ params.permit(splitable: [ :api_key, :api_secret, :expires_in, :mode] )
53
+ end
54
+
55
+ def load_payment_method
56
+ @payment_method = NimbleshopSplitable::Splitable.first
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,36 @@
1
+ module NimbleshopSplitable
2
+ module ExposedHelper
3
+
4
+ def nimbleshop_splitable_small_image( options = {} )
5
+ image_tag "engines/nimbleshop_splitable/splitable_small.png", {alt: 'splitable icon'}.merge(options)
6
+ end
7
+
8
+ def nimbleshop_splitable_big_image(options = {})
9
+ image_tag "engines/nimbleshop_splitable/splitable_big.png", { alt: 'splitable logo' }.merge(options)
10
+ end
11
+
12
+ def nimbleshop_splitable_image()
13
+ image_tag "engines/nimbleshop_splitable/splitable.png", alt: 'splitable logo'
14
+ end
15
+
16
+ def nimbleshop_splitable_available_payment_options_icons
17
+ return unless NimbleshopSplitable::Splitable.first
18
+ nimbleshop_splitable_small_image
19
+ end
20
+
21
+ def nimbleshop_splitable_payment_form(order)
22
+ return unless NimbleshopSplitable::Splitable.first
23
+ render partial: '/nimbleshop_splitable/payments/new', locals: {order: order}
24
+ end
25
+
26
+ def nimbleshop_splitable_crud_form
27
+ return unless NimbleshopSplitable::Splitable.first
28
+ render partial: '/nimbleshop_splitable/splitables/edit'
29
+ end
30
+
31
+ def nimbleshop_splitable_icon_for_order_payment(order)
32
+ nimbleshop_splitable_small_image height: '10px'
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ module NimbleshopSplitable
2
+ class Splitable < PaymentMethod
3
+
4
+ store_accessor :metadata, :api_key, :api_secret, :expires_in, :mode
5
+
6
+ validates_presence_of :api_key
7
+
8
+ before_save :set_mode
9
+
10
+ private
11
+
12
+ def set_mode
13
+ self.mode ||= 'test'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ <label class='radio'>
2
+ <%= radio_button_tag 'payment_choice', 'splitable' %>
3
+ <%= nimbleshop_splitable_image() %>
4
+ </label>
5
+
6
+ <%= form_for NimbleshopSplitable::Splitable.new, url: nimbleshop_splitable.payment_path, html: { id: 'splitable-payment-form',
7
+ class: 'form-horizontal' } do |f| %>
8
+ <% end %>
9
+
10
+ <%= javascript_tag do %>
11
+ $(function() {
12
+ $("input#payment_choice_splitable:radio").change(function() {
13
+ var $this, val;
14
+ $this = $(this);
15
+ val = $this.val();
16
+
17
+ if (val === "splitable") {
18
+ $("form#splitable-payment-form").submit();
19
+ }
20
+
21
+ });
22
+ });
23
+ <% end %>
@@ -0,0 +1,36 @@
1
+ <div class='payment-method-engine-well'>
2
+
3
+ <div>
4
+ <h2> Splitable </h2>
5
+
6
+ <div class="edit_link">
7
+ <%= link_to 'Edit', '#', class: "nimbleshop-payment-method-edit", id: "nimbleshop-splitable-payment-method-edit" %>
8
+ <%= link_to nimbleshop_splitable.splitable_path, confirm: 'Do you really want to delete Authorize.net payment method',
9
+ class: 'nimbleshop-payment-method-delete',
10
+ method: :delete,
11
+ remote: true do %>
12
+ <i class='icon-remove icon-white'></i>
13
+ <% end %>
14
+ </div>
15
+
16
+ <%= nimbleshop_splitable_big_image %>
17
+
18
+ <div class='clear'></div>
19
+ </div>
20
+
21
+
22
+ <%= render partial: '/nimbleshop_splitable/splitables/form' %>
23
+
24
+ </div>
25
+
26
+ <script>
27
+ $(function(){
28
+
29
+ $('#nimbleshop-splitable-payment-method-edit').toggle(function(){
30
+ $('#nimbleshop-splitable-form-well').show();
31
+ }, function(){
32
+ $('#nimbleshop-splitable-form-well').hide();
33
+ });
34
+
35
+ })
36
+ </script>
@@ -0,0 +1,47 @@
1
+ <div class='well nimbleshop-payment-method-form-well' id='nimbleshop-splitable-form-well' style='display:none;'>
2
+
3
+ <%= form_for NimbleshopSplitable::Splitable.first, url: '/nimbleshop_splitable/splitable',
4
+ remote: true,
5
+ html: { method: 'put',
6
+ id: 'nimbleshop-splitable-form',
7
+ class: 'nimbleshop-payment-method-form form-horizontal'} do |f| %>
8
+
9
+ <fieldset>
10
+ <div class="control-group">
11
+ <%= f.label :api_key, nil, class: 'control-label' %>
12
+ <div class="controls">
13
+ <%= f.text_field :api_key, class: 'span6' %>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="control-group">
18
+ <%= f.label :api_secret, nil, class: 'control-label' %>
19
+ <div class="controls">
20
+ <%= f.text_field :api_secret, class: 'span6' %>
21
+ </div>
22
+ </div>
23
+
24
+ <div class="control-group">
25
+ <%= f.label :expires_in, nil, class: 'control-label' %>
26
+ <div class="controls">
27
+ <%= f.text_field :expires_in, class: 'span6' %>
28
+ </div>
29
+ </div>
30
+
31
+ <div class="control-group">
32
+ <div class="controls">
33
+ <label class='checkbox'>
34
+ <%= f.check_box :mode, {}, 'test', 'production' %> Enable test mode
35
+ </label>
36
+ </div>
37
+ </div>
38
+
39
+ </fieldset>
40
+
41
+ <div class="form-actions">
42
+ <%= f.submit('Submit', class: 'btn btn-primary') %>
43
+ &nbsp;
44
+ <%= link_to t(:cancel), nimbleshop_splitable.splitable_path, class: 'cancel btn' %>
45
+ </div>
46
+ <% end %>
47
+ </div>
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ NimbleshopSplitable::Engine.routes.draw do
2
+ resource :splitable do
3
+ collection do
4
+ post :notify
5
+ end
6
+ end
7
+ resource :payment
8
+ end
@@ -0,0 +1,17 @@
1
+ module NimbleshopSplitable
2
+ class Engine < ::Rails::Engine
3
+
4
+ isolate_namespace NimbleshopSplitable
5
+
6
+ config.to_prepare do
7
+ ::NimbleshopSplitable::Splitable
8
+ end
9
+
10
+ initializer 'nimbleshop_splitable_engine.action_controller' do |app|
11
+ ActiveSupport.on_load :action_controller do
12
+ helper NimbleshopSplitable::ExposedHelper
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,74 @@
1
+ module NimbleshopSplitable
2
+ class Gateway
3
+ include ActiveMerchant::PostsData
4
+
5
+ class_attribute :live_url, :test_url
6
+ self.live_url = 'https://www.splitable.com/api/splits'
7
+ self.test_url = 'https://www.splitable-draft.com/api/splits'
8
+
9
+ def initialize(splitable)
10
+ @api_key = splitable.api_key
11
+ @expires_in = splitable.expires_in
12
+ @mode = splitable.mode
13
+ end
14
+
15
+ def api_notify_url(request)
16
+ Util.localhost2public_url( '/nimbleshop_splitable/splitable/notify', 'http' )
17
+ end
18
+
19
+ def create(order, request)
20
+ params = build_params(order, request)
21
+
22
+ params[:api_key] = @api_key
23
+ params[:api_notify_url] = api_notify_url(request)
24
+ params[:expires_in] = @expires_in
25
+
26
+ unless Rails.env.production?
27
+ Rails.logger.info "params to be posted: #{params.inspect}"
28
+ end
29
+
30
+ url = test? ? self.test_url : self.live_url
31
+ params = params.collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
32
+
33
+ ssl_post(url, params)
34
+ end
35
+
36
+ def build_params(order, request)
37
+ params = {}
38
+ add_order_data(params, order)
39
+ add_product_data(params, order.line_items, request)
40
+ params
41
+ end
42
+
43
+ def add_order_data(params, order)
44
+ params[:total_amount] = order.total_amount_in_cents
45
+ params[:invoice] = order.number
46
+ params[:shipping] = Util.in_cents(ShippingCostCalculator.new(order).shipping_cost)
47
+ params[:tax] = Util.in_cents(TaxCalculator.new(order).tax)
48
+ params[:description] = 'See Splitable integrates nicely with nimbleShop'
49
+ end
50
+
51
+ def add_product_data(params, products, request)
52
+ products.each.with_index(1) do | item, index |
53
+ params.update(to_param(item, index, request))
54
+ end
55
+ end
56
+
57
+ def product_url(item, request)
58
+ request.protocol + request.host_with_port + "/products/#{item.product_permalink}"
59
+ end
60
+
61
+ def to_param(item, index, request)
62
+ {
63
+ "amount_#{index}" => (item.price * 100).to_i,
64
+ "item_name_#{index}" => item.product_name,
65
+ "quantity_#{index}" => item.quantity,
66
+ "url_#{index}" => product_url(item, request)
67
+ }
68
+ end
69
+
70
+ def test?
71
+ @mode == 'test'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,96 @@
1
+ module NimbleshopSplitable
2
+ class Processor < ::Processor::Base
3
+
4
+ attr_reader :errors, :order, :payment_method
5
+
6
+ def gateway
7
+ ::NimbleshopSplitable::Gateway.new(NimbleshopSplitable::Splitable.first)
8
+ end
9
+
10
+ def initialize(options = {})
11
+
12
+ unless @order = options[:order]
13
+ invoice = options[:invoice].try(:to_s)
14
+ @order = Order.find_by_number(invoice)
15
+ end
16
+
17
+ @errors = []
18
+ @payment_method = NimbleshopSplitable::Splitable.first
19
+ end
20
+
21
+ def create_split(options = {})
22
+ options.symbolize_keys!
23
+ options.assert_valid_keys(:request)
24
+
25
+ response = gateway.create(order, options[:request])
26
+ json = ActiveSupport::JSON.decode(response)
27
+ result = json.values_at('error','split_url')
28
+ if result.last
29
+ order.update_attributes(payment_method: payment_method)
30
+ order.pending
31
+ end
32
+ result
33
+ end
34
+
35
+ def acknowledge(params = {})
36
+ if valid?(params)
37
+ send(resolve_payment_kind(params[:payment_status]), params)
38
+ else
39
+ false
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def resolve_payment_kind(payment_status)
46
+ payment_status == 'paid' ? 'purchase' : 'void'
47
+ end
48
+
49
+ def valid?(params)
50
+ @errors.clear
51
+ validate_order
52
+ validate_params(params)
53
+ @errors.empty?
54
+ end
55
+
56
+ def validate_order
57
+ unless order
58
+ @errors << "Unknown invoice number"
59
+ end
60
+ end
61
+
62
+ def validate_params(params = {})
63
+
64
+ unless params[:transaction_id]
65
+ @errors << "transaction_id is blank"
66
+ end
67
+
68
+ unless params[:payment_status]
69
+ @errors << "Parameter payment_status is blank"
70
+ end
71
+ end
72
+
73
+ def do_purchase(options = {})
74
+ add_to_order(options, 'purchased')
75
+ order.update_attributes(payment_method: payment_method)
76
+ order.purchase!
77
+
78
+ true
79
+ end
80
+
81
+ def do_void(options = {})
82
+ add_to_order(options, 'voided')
83
+ order.update_attributes(payment_method: payment_method)
84
+ order.void!
85
+
86
+ true
87
+ end
88
+
89
+ def add_to_order(params, operation)
90
+ order.payment_transactions.create(operation: operation,
91
+ success: true,
92
+ transaction_gid: params[:transaction_id],
93
+ params: params)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,3 @@
1
+ module NimbleshopSplitable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "nimbleshop_splitable/engine"
2
+
3
+ module NimbleshopSplitable
4
+ autoload :Processor, 'nimbleshop_splitable/processor'
5
+ autoload :Gateway, 'nimbleshop_splitable/gateway'
6
+ end
@@ -0,0 +1,29 @@
1
+ # This is a rake task that simulates what splitable will POST as callback.
2
+ #
3
+
4
+ desc "sends callback"
5
+
6
+ namespace :nimbleshop_splitable do
7
+ task :mock_callback => :environment do
8
+
9
+ unless order_number = ENV['order_number']
10
+ puts "Usage: rake mock_callcak order_number=xxxxxxx"
11
+ abort
12
+ end
13
+
14
+ base_url = 'http://localhost:3000'
15
+ endpoint = base_url + '/nimbleshop_splitable/splitable/notify'
16
+
17
+
18
+ params = { invoice: order_number,
19
+ payment_status: "cancelled",
20
+ api_secret: "540f0849518d83d6",
21
+ transaction_id: "49c380175f3c5603287698a4" }
22
+
23
+ require "net/http"
24
+ require "uri"
25
+ uri = URI.parse(endpoint)
26
+ response = Net::HTTP.post_form(uri, params)
27
+
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ namespace :nimbleshop_splitable do
2
+
3
+ desc 'load splitable record'
4
+ task :load_record => :environment do
5
+ if NimbleshopSplitable::Splitable.find_by_permalink('splitable')
6
+ puts "Splitable record already exists"
7
+ else
8
+ NimbleshopSplitable::Splitable.create!(
9
+ {
10
+ api_secret: '92746e4d66cb8993',
11
+ expires_in: 24,
12
+ api_key: '92746e4d66cb8993',
13
+ name: 'Splitable',
14
+ permalink: 'splitable',
15
+ description: %Q[<p> Splitable helps split the cost of product with friends and family. </p> <p> <a href="http://splitable.com"> more information </a> </p>]
16
+ })
17
+ puts "Splitable record was successfuly created"
18
+ end
19
+ end
20
+
21
+ desc 'copies images from engine to main rails application'
22
+ task :copy_images do
23
+ engine_name = 'nimbleshop_splitable'
24
+ origin = File.expand_path('../../app/assets/images', File.dirname(__FILE__))
25
+ destination = Rails.root.join('app', 'assets', 'images', 'engines', engine_name)
26
+ FileUtils.mkdir_p(destination) if !File.exist?(destination)
27
+ Dir[File.join(origin, '**/*')].each { |file| FileUtils.cp(file, File.join(destination) ) unless File.directory?(file) }
28
+ end
29
+
30
+
31
+ end
32
+
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nimbleshop_splitable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.rc1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - Neeraj Singh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activemerchant
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: Provides Splitable functionality to nimbleShop
31
+ email:
32
+ - neeraj@BigBinary.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - app/assets/images/splitable.png
38
+ - app/assets/images/splitable_big.png
39
+ - app/assets/images/splitable_small.png
40
+ - app/controllers/nimbleshop_splitable/payments_controller.rb
41
+ - app/controllers/nimbleshop_splitable/splitables_controller.rb
42
+ - app/helpers/nimbleshop_splitable/exposed_helper.rb
43
+ - app/models/nimbleshop_splitable/splitable.rb
44
+ - app/views/nimbleshop_splitable/payments/_new.html.erb
45
+ - app/views/nimbleshop_splitable/splitables/_edit.html.erb
46
+ - app/views/nimbleshop_splitable/splitables/_form.html.erb
47
+ - config/routes.rb
48
+ - lib/nimbleshop_splitable/engine.rb
49
+ - lib/nimbleshop_splitable/gateway.rb
50
+ - lib/nimbleshop_splitable/processor.rb
51
+ - lib/nimbleshop_splitable/version.rb
52
+ - lib/nimbleshop_splitable.rb
53
+ - lib/tasks/mock_callback.rake
54
+ - lib/tasks/nimbleshop_splitable_tasks.rake
55
+ - README.md
56
+ homepage: http://nimbleShop.org
57
+ licenses: []
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>'
72
+ - !ruby/object:Gem::Version
73
+ version: 1.3.1
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 1.8.23
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Splitable extension for Nimbleshop
80
+ test_files: []