solidus_conekta 1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +90 -0
  4. data/Rakefile +21 -0
  5. data/app/assets/javascripts/spree/backend/solidus_conekta.js +2 -0
  6. data/app/assets/javascripts/spree/frontend/conekta.js +635 -0
  7. data/app/assets/javascripts/spree/frontend/easyXDM.min.js +25 -0
  8. data/app/assets/javascripts/spree/frontend/printThis.js +146 -0
  9. data/app/assets/javascripts/spree/frontend/solidus_conekta.js.coffee +63 -0
  10. data/app/assets/javascripts/spree/frontend/utils.js.erb +35 -0
  11. data/app/assets/stylesheets/spree/backend/print.css.sass +12 -0
  12. data/app/assets/stylesheets/spree/backend/solidus_conekta.css +3 -0
  13. data/app/assets/stylesheets/spree/frontend/conekta.css.sass +45 -0
  14. data/app/assets/stylesheets/spree/frontend/solidus_conekta.css +4 -0
  15. data/app/controllers/spree/checkout_controller_decorator.rb +30 -0
  16. data/app/controllers/spree/conekta/payments_controller.rb +16 -0
  17. data/app/models/spree/billing_integration/conekta_gateway/bank.rb +30 -0
  18. data/app/models/spree/billing_integration/conekta_gateway/card.rb +35 -0
  19. data/app/models/spree/billing_integration/conekta_gateway/cash.rb +30 -0
  20. data/app/models/spree/billing_integration/conekta_gateway/monthly_payment.rb +35 -0
  21. data/app/models/spree/billing_integration/conekta_gateway.rb +31 -0
  22. data/app/models/spree/conekta_payment.rb +16 -0
  23. data/app/models/spree/credit_card_decorator.rb +8 -0
  24. data/app/models/spree/line_item_decorator.rb +11 -0
  25. data/app/models/spree/order_decorator.rb +19 -0
  26. data/app/models/spree/payment_decorator.rb +18 -0
  27. data/app/models/spree.rb +5 -0
  28. data/app/overrides/add_conekta_scripts_to_the_checkout.rb +4 -0
  29. data/app/views/spree/admin/payments/_banorte.html.erb +32 -0
  30. data/app/views/spree/admin/payments/_oxxo.html.erb +29 -0
  31. data/app/views/spree/admin/payments/_spei.html.erb +30 -0
  32. data/app/views/spree/admin/payments/source_forms/_conekta.html.erb +3 -0
  33. data/app/views/spree/admin/payments/source_forms/_conekta_card.html.erb +3 -0
  34. data/app/views/spree/admin/payments/source_views/_conekta.html.erb +1 -0
  35. data/app/views/spree/admin/payments/source_views/_conekta_card.html.erb +19 -0
  36. data/app/views/spree/checkout/_conekta_scripts.html.erb +17 -0
  37. data/app/views/spree/checkout/existing_payment/_conekta_card.html.erb +9 -0
  38. data/app/views/spree/checkout/payment/_conekta.html.erb +3 -0
  39. data/app/views/spree/checkout/payment/_conekta_card.html.erb +75 -0
  40. data/app/views/spree/conekta/gateway_options.json.jbuilder +34 -0
  41. data/app/views/spree/conekta/payments/_bank.html.erb +0 -0
  42. data/app/views/spree/conekta/payments/_banorte.html.erb +30 -0
  43. data/app/views/spree/conekta/payments/_card.html.erb +0 -0
  44. data/app/views/spree/conekta/payments/_cash.html.erb +0 -0
  45. data/app/views/spree/conekta/payments/_oxxo.html.erb +36 -0
  46. data/app/views/spree/conekta/payments/_spei.html.erb +28 -0
  47. data/app/views/spree/conekta/payments/show.html.erb +25 -0
  48. data/config/locales/en.yml +26 -0
  49. data/config/locales/es-MX.yml +26 -0
  50. data/config/routes.rb +5 -0
  51. data/db/migrate/20130904161251_create_spree_conekta_payments.rb +9 -0
  52. data/db/migrate/20130904172418_rename_type_column_to_conekta_payment.rb +7 -0
  53. data/db/migrate/20140220160614_add_installments_to_spree_credit_card.rb +5 -0
  54. data/db/migrate/20140509145837_add_payment_method_id_to_spree_conekta_payment.rb +6 -0
  55. data/db/migrate/20140509151116_add_user_id_to_spree_conekta_payment.rb +6 -0
  56. data/lib/generators/solidus_conekta/install/install_generator.rb +31 -0
  57. data/lib/solidus_conekta/engine.rb +40 -0
  58. data/lib/solidus_conekta/factories.rb +6 -0
  59. data/lib/solidus_conekta/version.rb +3 -0
  60. data/lib/solidus_conekta.rb +10 -0
  61. data/lib/spree/conekta/client.rb +36 -0
  62. data/lib/spree/conekta/configuration.rb +25 -0
  63. data/lib/spree/conekta/credit_card.rb +42 -0
  64. data/lib/spree/conekta/credit_card_collection.rb +36 -0
  65. data/lib/spree/conekta/customer.rb +39 -0
  66. data/lib/spree/conekta/exchange.rb +30 -0
  67. data/lib/spree/conekta/fake_response.rb +10 -0
  68. data/lib/spree/conekta/monthly_payment_provider.rb +26 -0
  69. data/lib/spree/conekta/payment_notification_handler.rb +36 -0
  70. data/lib/spree/conekta/payment_source/bank.rb +13 -0
  71. data/lib/spree/conekta/payment_source/card.rb +21 -0
  72. data/lib/spree/conekta/payment_source/cash.rb +13 -0
  73. data/lib/spree/conekta/provider.rb +134 -0
  74. data/lib/spree/conekta/response.rb +14 -0
  75. data/lib/tasks/solidus_conekta_tasks.rake +4 -0
  76. metadata +509 -0
@@ -0,0 +1,36 @@
1
+ module Spree::Conekta
2
+ module Client
3
+ CONEKTA_API = 'https://api.conekta.io/'
4
+
5
+ attr_accessor :auth_token
6
+
7
+ def post(params)
8
+ Oj.load connection.post(endpoint, Oj.dump(params)).body
9
+ end
10
+
11
+ def get
12
+ Oj.load connection.get(endpoint).body
13
+ end
14
+
15
+ def connection
16
+ Faraday.new(url: CONEKTA_API) do |faraday|
17
+ faraday.request :url_encoded
18
+
19
+ faraday.headers = headers
20
+ faraday.adapter :typhoeus
21
+ faraday.basic_auth(auth_token, nil)
22
+ end
23
+ end
24
+
25
+ def headers
26
+ {
27
+ 'Accept' => ' application/vnd.conekta-v0.3.0+json',
28
+ 'Content-type' => ' application/json'
29
+ }
30
+ end
31
+
32
+ def endpoint
33
+ raise 'Not Implemented'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module Conekta
3
+
4
+ class << self
5
+ attr_accessor :configuration
6
+ end
7
+
8
+ def self.configure
9
+ self.configuration ||= Configuration.new
10
+ yield(configuration)
11
+ end
12
+
13
+ class Configuration
14
+ attr_accessor :installment_options, :installment_default
15
+
16
+ def installment_options
17
+ @installment_options ||= :installment_options
18
+ end
19
+
20
+ def installment_default
21
+ @installment_default ||= :installment_default
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ module Spree
2
+ module Conekta
3
+ class CreditCard
4
+ include Spree::Conekta::Client
5
+
6
+ attr_reader :customer
7
+ attr_accessor :id, :brand, :last4
8
+
9
+ def self.create(customer, token, auth_token)
10
+ new(customer, token: token, auth_token: auth_token)
11
+ end
12
+
13
+ def self.build(customer, card, auth_token)
14
+ new(customer, card: card, auth_token: auth_token)
15
+ end
16
+
17
+ def initialize(customer, options)
18
+ @customer = customer
19
+ @auth_token = options[:auth_token]
20
+ build_card(options[:card]) if options[:card]
21
+ create_card(options[:token]) if options[:token]
22
+ end
23
+
24
+ def endpoint
25
+ "customers/#{customer.id}/cards"
26
+ end
27
+
28
+ private
29
+
30
+ def build_card(card)
31
+ @id = card['id']
32
+ @brand = card['brand']
33
+ @last4 = card['last4']
34
+ end
35
+
36
+ def create_card(token)
37
+ response = post(token: token)
38
+ build_card(response)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ module Spree
2
+ module Conekta
3
+ class CreditCardCollection
4
+ include Enumerable
5
+ include Spree::Conekta::Client
6
+
7
+ extend Forwardable
8
+
9
+ def_instance_delegators :@cards, :each, :size
10
+
11
+ attr_reader :customer
12
+
13
+ def initialize(customer, options)
14
+ @customer = customer
15
+ @auth_token = options[:auth_token]
16
+ @cards = build_cards(get)
17
+ end
18
+
19
+ def add(token)
20
+ @cards << Spree::Conekta::CreditCard.create(customer, token, auth_token)
21
+ end
22
+
23
+ def endpoint
24
+ "customers/#{customer.id}/cards"
25
+ end
26
+
27
+ private
28
+
29
+ def build_cards(response)
30
+ response.map do |credit_card|
31
+ Spree::Conekta::CreditCard.build(customer, credit_card, auth_token: auth_token)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ module Spree
2
+ module Conekta
3
+ class Customer
4
+ include Spree::Conekta::Client
5
+
6
+ attr_reader :user, :options, :credit_cards
7
+
8
+ def initialize(user, options = {})
9
+ @user = user
10
+ @options = options
11
+ @auth_token = options[:auth_token]
12
+ end
13
+
14
+ def id
15
+ @id ||= user.gateway_customer_profile_id || create_new_client
16
+ end
17
+
18
+ def add_credit_card(token)
19
+ credit_cards.add(token)
20
+ end
21
+
22
+ def credit_cards
23
+ @credit_cards ||= Spree::Conekta::CreditCardCollection.new(self, auth_token: auth_token)
24
+ end
25
+
26
+ def endpoint
27
+ 'customers'
28
+ end
29
+
30
+ private
31
+
32
+ def create_new_client
33
+ response = post(name: user.email, email: user.email)
34
+ user.update_column(:gateway_customer_profile_id, response['id']) if options[:persist]
35
+ response['id']
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ module Spree::Conekta
2
+ class Exchange
3
+
4
+ EXCHANCE_SERVICES = 'https://currency-api.appspot.com/api/'
5
+
6
+ def initialize(amount, currency_from)
7
+ @amount = (amount.to_i)
8
+ @currency_from = currency_from
9
+ get_exchange_rate
10
+ end
11
+
12
+ def amount_exchanged
13
+ @amount_exchanged ||= (@amount * @exchange_rate).round
14
+ end
15
+
16
+ private
17
+
18
+ def get_exchange_rate
19
+ response = JSON.parse(connection.get("#{@currency_from}/MXN.json?amount=#{@amount}").body)
20
+ @exchange_rate = response['rate'].nil? ? 0 : response['rate'].to_f
21
+ end
22
+
23
+ def connection
24
+ Faraday.new(url: EXCHANCE_SERVICES) do |faraday|
25
+ faraday.request :url_encoded
26
+ faraday.adapter :typhoeus
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module Spree::Conekta
2
+ class FakeResponse < ActiveMerchant::Billing::Response
3
+
4
+ def initialize
5
+ @success = true
6
+ @message = "Orden devuelta"
7
+ @params = {}
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ module Spree::Conekta
2
+ class MonthlyPaymentProvider
3
+ attr_accessor :auth_token
4
+
5
+ attr_reader :options
6
+
7
+ def initialize(options = {})
8
+ @options = options
9
+ @auth_token = options[:auth_token]
10
+ end
11
+
12
+ def authorize(amount, source, gateway_options = {})
13
+ Response.new(parse_conekta_response(source.conekta_response), gateway_options)
14
+ end
15
+
16
+ def capture(amount, source, gateway_options = {})
17
+ Response.new({}, gateway_options)
18
+ end
19
+
20
+ private
21
+
22
+ def parse_conekta_response(response)
23
+ Oj.load response || '{}'
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ require 'celluloid'
2
+
3
+ module Spree
4
+ module Conekta
5
+ class PaymentNotificationHandler
6
+ include Celluloid
7
+
8
+ attr_reader :params, :action, :order, :delay
9
+
10
+ ACTIONS = Hash.new(:failure!).merge! 'charge.paid' => :capture!
11
+
12
+ def initialize(params, delay = 60)
13
+ @params = params
14
+ @delay = delay
15
+ @action = ACTIONS[params['type']]
16
+ @order = params['data']['object']['reference_id'].split('-').first
17
+ end
18
+
19
+ def perform_action
20
+ after(delay) do
21
+ ActiveRecord::Base.connection_pool.with_connection do
22
+ payment.try(action)
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def payment
30
+ ActiveRecord::Base.connection_pool.with_connection do
31
+ Spree::Payment.find_by_order_number(order)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ module Spree::Conekta::PaymentSource
2
+ module Bank
3
+ def request(common, method, gateway_options)
4
+ type = gateway_options[:type]
5
+ type ||= 'banorte'
6
+ common['bank'] = {
7
+ 'type' => type
8
+ }
9
+ end
10
+
11
+ module_function :request
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ module Spree::Conekta::PaymentSource
2
+ module Card
3
+ def request(common, method, gateway_options)
4
+ common['card'] = method.gateway_payment_profile_id
5
+ common['monthly_installments'] = installments_number(method)
6
+ end
7
+
8
+ def self.customer_address(gateway_options)
9
+ customer = gateway_options[:billing_address]
10
+ customer['street1'] = customer.delete(:address1)
11
+ customer['street2'] = customer.delete(:address2)
12
+ customer
13
+ end
14
+
15
+ def installments_number(source)
16
+ [3,6,9,12].include?(source.installments_number) ? source.installments_number : nil
17
+ end
18
+
19
+ module_function :request, :installments_number
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Spree::Conekta::PaymentSource
2
+ module Cash
3
+ def request(common, method, gateway_options)
4
+ type = gateway_options[:type]
5
+ type ||= 'oxxo'
6
+ common['cash'] = {
7
+ 'type' => type
8
+ }
9
+ end
10
+
11
+ module_function :request
12
+ end
13
+ end
@@ -0,0 +1,134 @@
1
+ module Spree::Conekta
2
+ class Provider
3
+ include Spree::Conekta::Client
4
+
5
+ attr_accessor :auth_token, :source_method
6
+
7
+ attr_reader :options
8
+
9
+ PAYMENT_SOURCES = {
10
+ 'card' => Spree::Conekta::PaymentSource::Card,
11
+ 'banorte' => Spree::Conekta::PaymentSource::Bank,
12
+ 'spei' => Spree::Conekta::PaymentSource::Bank,
13
+ 'oxxo' => Spree::Conekta::PaymentSource::Cash
14
+ }
15
+
16
+ def initialize(options = {})
17
+ @options = options
18
+ @auth_token = options[:auth_token]
19
+ @source_method = payment_processor(options[:source_method])
20
+ end
21
+
22
+ def authorize(amount, method_params, gateway_options = {})
23
+ common = build_common(amount, gateway_options)
24
+ commit common, method_params, gateway_options.merge({type: @options[:source_method]})
25
+ end
26
+
27
+ alias_method :purchase, :authorize
28
+
29
+ def capture(amount, method_params, gateway_options = {})
30
+ Response.new({}, gateway_options)
31
+ end
32
+
33
+ def endpoint
34
+ 'charges'
35
+ end
36
+
37
+ def payment_processor(source_name)
38
+ PAYMENT_SOURCES[source_name]
39
+ end
40
+
41
+ def supports?(brand)
42
+ %w(visa master).include? brand
43
+ end
44
+
45
+ def credit(credit_cents, response_code, gateway_options)
46
+ Spree::Conekta::FakeResponse.new
47
+ end
48
+
49
+ private
50
+
51
+ def commit(common, method_params, gateway_options)
52
+ source_method.request(common, method_params, gateway_options)
53
+ Spree::Conekta::Response.new post(common), source_method
54
+ end
55
+
56
+ def build_common(amount, gateway_params)
57
+ if source_method == Spree::Conekta::PaymentSource::Cash && gateway_params[:currency] != 'MXN'
58
+ return build_common_to_cash(amount, gateway_params)
59
+ else
60
+ {
61
+ 'amount' => amount,
62
+ 'reference_id' => gateway_params[:order_id],
63
+ 'currency' => gateway_params[:currency],
64
+ 'description' => gateway_params[:order_id],
65
+ 'details' => details(gateway_params)
66
+ }
67
+ end
68
+ end
69
+
70
+ def details(gateway_params)
71
+ {
72
+ 'name' => gateway_params[:billing_address][:name],
73
+ 'email' => gateway_params[:email],
74
+ 'phone' => gateway_params[:billing_address][:phone],
75
+ 'billing_address' => billing_address(gateway_params),
76
+ 'line_items' => line_items(gateway_params),
77
+ 'shipment' => shipment(gateway_params)
78
+ }
79
+ end
80
+
81
+ def shipping_address(gateway_params)
82
+ {
83
+ 'street1' => gateway_params[:shipping_address][:address1],
84
+ 'street2' => gateway_params[:shipping_address][:address2],
85
+ 'city' => gateway_params[:shipping_address][:city],
86
+ 'state' => gateway_params[:shipping_address][:state],
87
+ 'country' => gateway_params[:shipping_address][:country],
88
+ 'zip' => gateway_params[:shipping_address][:zip]
89
+ }
90
+ end
91
+
92
+ def billing_address(gateway_params)
93
+ {
94
+ 'email' => gateway_params[:email],
95
+ 'street1' => gateway_params[:billing_address][:address1],
96
+ 'street2' => gateway_params[:billing_address][:address2],
97
+ 'city' => gateway_params[:billing_address][:city],
98
+ 'state' => gateway_params[:billing_address][:state],
99
+ 'country' => gateway_params[:billing_address][:country],
100
+ 'zip' => gateway_params[:billing_address][:zip]
101
+ }
102
+ end
103
+
104
+ def line_items(gateway_params)
105
+ order = Spree::Order.find_by_number(gateway_params[:order_id].split('-').first)
106
+ order.line_items.map(&:to_conekta)
107
+ end
108
+
109
+ def shipment(gateway_params)
110
+ order = Spree::Order.find_by_number(gateway_params[:order_id].split('-').first)
111
+ shipment = order.shipments[0]
112
+ carrier = (shipment.present? ? shipment.shipping_method.name : "other")
113
+ traking_id = (shipment.present? ? shipment.tracking : nil)
114
+ {
115
+ 'price' => gateway_params[:shipping],
116
+ 'address' => shipping_address(gateway_params),
117
+ 'service' => "other",
118
+ 'carrier' => carrier,
119
+ 'tracking_id' => traking_id
120
+ }
121
+ end
122
+
123
+ def build_common_to_cash(amount, gateway_params)
124
+ amount_exchanged = Spree::Conekta::Exchange.new(amount, gateway_params[:currency]).amount_exchanged
125
+ {
126
+ 'amount' => amount_exchanged,
127
+ 'reference_id' => gateway_params[:order_id],
128
+ 'currency' => "MXN",
129
+ 'description' => gateway_params[:order_id],
130
+ 'details' => details(gateway_params)
131
+ }
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,14 @@
1
+ module Spree::Conekta
2
+ class Response < ActiveMerchant::Billing::Response
3
+ attr_accessor :response, :source_method, :status
4
+
5
+ def initialize(response, source_method)
6
+ @success = !(response.eql?('null') || response.include?('type')) if response
7
+ @message = @success ? 'Ok' : response['message']
8
+ @params = response
9
+ @params['message'] = @success ? 'Ok' : response['message_to_purchaser']
10
+ @status = response['status']
11
+ @source_method = source_method
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :solidus_conekta do
3
+ # # Task goes here
4
+ # end