solidus_mercadopago 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +68 -0
  4. data/Rakefile +31 -0
  5. data/app/assets/javascripts/spree/backend/solidus_mercado_pago.js +0 -0
  6. data/app/assets/javascripts/spree/frontend/solidus_mercado_pago.js +25 -0
  7. data/app/assets/stylesheets/spree/backend/solidus_mercado_pago.css +1 -0
  8. data/app/assets/stylesheets/spree/frontend/solidus_mercado_pago.css +1 -0
  9. data/app/controllers/spree/mercadopago_controller.rb +70 -0
  10. data/app/models/mercadopago/client.rb +64 -0
  11. data/app/models/mercadopago/client/api.rb +46 -0
  12. data/app/models/mercadopago/client/authentication.rb +38 -0
  13. data/app/models/mercadopago/client/preferences.rb +22 -0
  14. data/app/models/mercadopago/notification.rb +10 -0
  15. data/app/models/mercadopago/order_preferences_builder.rb +67 -0
  16. data/app/models/spree/payment_method/mercadopago.rb +48 -0
  17. data/app/services/mercadopago/handle_received_notification.rb +20 -0
  18. data/app/services/mercadopago/process_notification.rb +58 -0
  19. data/app/views/spree/admin/payments/source_forms/_mercadopago.html.erb +1 -0
  20. data/app/views/spree/admin/payments/source_views/_mercadopago.html.erb +1 -0
  21. data/app/views/spree/checkout/mercado_pago_error.html.erb +0 -0
  22. data/app/views/spree/checkout/payment/_mercadopago.html.erb +15 -0
  23. data/config/locales/en.yml +4 -0
  24. data/config/locales/es.yml +4 -0
  25. data/config/locales/pt-BR.yml +4 -0
  26. data/config/routes.rb +8 -0
  27. data/db/migrate/20141201204026_create_solidus_mercadopago_notifications.rb +11 -0
  28. data/lib/generators/solidus_mercadopago/install/install_generator.rb +46 -0
  29. data/lib/solidus_mercadopago.rb +6 -0
  30. data/lib/solidus_mercadopago/engine.rb +21 -0
  31. data/lib/solidus_mercadopago/factories.rb +8 -0
  32. data/lib/solidus_mercadopago/version.rb +5 -0
  33. data/lib/tasks/mercado_user.rake +7 -0
  34. metadata +227 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d1603f3a8a1fb150b29457dcf870d703db1736b7313d8204efcbfd73a9cc40c6
4
+ data.tar.gz: 9ecf2ed9ce55e859cd87d723d828bdfd1b05374058f0528a0407eff0ffa005eb
5
+ SHA512:
6
+ metadata.gz: e91e4dadda35074f45ec10bd5c301b148fe9c14e4ae1ed7ff5ec02ee58978646df244449167a1ed1972afa7be04d2afa76576d3e349600f16237bc66ecf73217
7
+ data.tar.gz: '017930045ab6b1773109dacbae1c62930c98a27e45ece144cc14fc40963684eddf20feeef0cb85ca37dc949905b113568766e29b04af9d4401cd0c26b2d50e9e'
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2012 Manuel Barros Reyes
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name Spree nor the names of its contributors may be used to
13
+ endorse or promote products derived from this software without specific
14
+ prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,68 @@
1
+ Solidus Mercado Pago Payment Method.
2
+ =================================
3
+
4
+ [![Circle CI](https://circleci.com/gh/magma-labs/solidus_mercadopago/tree/master.svg?style=shield)](https://circleci.com/gh/magma-labs/solidus_mercadopago/tree/master)
5
+
6
+ This project is a fork from [spree version maintained by ngelx](https://github.com/ngelx/solidus_mercadopago).
7
+
8
+
9
+ ```
10
+ gem 'solidus_mercadopago', git: "git@github.com:magma-labs/solidus_mercadopago.git"
11
+ ```
12
+
13
+ You should run inside your project
14
+
15
+ ```
16
+ bundle exec rails g solidus_mercadopago:install
17
+ ```
18
+
19
+ This will import assets and migrations
20
+
21
+ Basic Setup
22
+ -----
23
+
24
+ ## Retrieve Mercado pago account details
25
+ You'll need the following account details:
26
+
27
+ - Client ID
28
+ - Client secret
29
+ - Sandbox (A boolean value to indicate if we're using the sandbox or not)
30
+
31
+ These values can be obtained by logging in to your Mercado Pago account, going to `Credentials -> Basic checkout`
32
+
33
+ ## Create a new payment method
34
+
35
+ Payment methods can accept preferences either directly entered in admin.
36
+
37
+ 1. Visit `/admin/payment_methods/new`
38
+ 2. Set `Mercadopago` as provider.
39
+ 3. Save to update the form
40
+ 4. Set your credentials into the corresponding fields
41
+
42
+ By default, your preferences can be gained from your environment vars (`MERCADOPAGO_CLIENT_ID`, `MERCADOPAGO_CLIENT_SECRET`), except by `sandbox`; its default value is `true`.
43
+
44
+
45
+ Other option is create the payment method from admin interface:
46
+
47
+
48
+ Usage
49
+ -----
50
+
51
+ - Add a new payment method in the admin panel of type Spree::PaymentMethod::Mercadopago
52
+ - After adding the payment method you will be able to configure your Client ID and Client Secret (provided by Mercado Pago).
53
+
54
+ IPN
55
+ ---
56
+
57
+ For IPN you need to configure the notification URL in Mercado Pago's site. The notification URL will be `http[s]://[your_domain]/mercado_pago/ipn`. Please review Mercado Pago's documentation at http://developers.Mercadopago.com/ for the correct place where to configure IPN notification URLs.
58
+
59
+
60
+ Testing
61
+ -------
62
+
63
+ - Clone this repo
64
+ - `ln -s docker-compose-{platform}.yml docker-compose.yml`
65
+ - `docker-sync start` **Only in mac**
66
+ - `docker-compose up`
67
+ - `docker-compose run web bundle exec rake test_app` to build a dummy app directory inside specs
68
+ - `docker-compose run web bundle exec rspec spec`
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ begin
8
+ require 'spree/testing_support/extension_rake'
9
+ require 'rubocop/rake_task'
10
+ require 'rspec/core/rake_task'
11
+
12
+ RSpec::Core::RakeTask.new(:spec)
13
+ RuboCop::RakeTask.new
14
+
15
+ task default: %i(first_run rubocop spec)
16
+ rescue LoadError
17
+ # no rspec available
18
+ end
19
+
20
+ task :first_run do
21
+ if Dir['spec/dummy'].empty?
22
+ Rake::Task[:test_app].invoke
23
+ Dir.chdir('../../')
24
+ end
25
+ end
26
+
27
+ desc 'Generates a dummy app for testing'
28
+ task :test_app do
29
+ ENV['LIB_NAME'] = 'solidus_mercadopago'
30
+ Rake::Task['extension:test_app'].invoke
31
+ end
@@ -0,0 +1,25 @@
1
+ //= require spree/frontend
2
+
3
+ Mercadopago = {
4
+ hidePaymentSaveAndContinueButton: function(paymentMethod) {
5
+ if (Mercadopago.paymentMethodID && paymentMethod.val() == Mercadopago.paymentMethodID) {
6
+ $('.continue').hide();
7
+ $('[data-hook=coupon_code]').hide();
8
+ } else {
9
+ $('.continue').show();
10
+ $('[data-hook=coupon_code]').show();
11
+ }
12
+ }
13
+ };
14
+
15
+ $(document).ready(function() {
16
+ checkedPaymentMethod = $('div[data-hook="checkout_payment_step"] input[type="radio"]:checked');
17
+ Mercadopago.hidePaymentSaveAndContinueButton(checkedPaymentMethod);
18
+ paymentMethods = $('div[data-hook="checkout_payment_step"] input[type="radio"]').click(function (e) {
19
+ Mercadopago.hidePaymentSaveAndContinueButton($(e.target));
20
+ });
21
+
22
+ $('button.mercadopago_button').click(function(event){
23
+ $(event.target).prop("disabled",true);
24
+ });
25
+ });
@@ -0,0 +1 @@
1
+ /* Custom backend assets */
@@ -0,0 +1 @@
1
+ /* Custom frontend assets */
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ class MercadopagoController < StoreController
5
+ protect_from_forgery except: :ipn
6
+ # skip_before_action :set_current_order, only: :ipn
7
+
8
+ def checkout
9
+ current_order.state_name == :payment || raise(ActiveRecord::RecordNotFound)
10
+ payment_method = PaymentMethod::Mercadopago.find(params[:payment_method_id])
11
+ payment = current_order.payments
12
+ .create!(amount: current_order.total, payment_method: payment_method)
13
+ payment.started_processing!
14
+
15
+ preferences = ::Mercadopago::OrderPreferencesBuilder
16
+ .new(current_order, payment, callback_urls)
17
+ .preferences_hash
18
+
19
+ provider = payment_method.provider
20
+ provider.create_preferences(preferences)
21
+
22
+ redirect_to provider.redirect_url
23
+ end
24
+
25
+ # Success/pending callbacks are currently aliases, this may change
26
+ # if required.
27
+ def success
28
+ payment.order.next
29
+ flash.notice = Spree.t(:order_processed_successfully)
30
+ flash['order_completed'] = true
31
+ redirect_to spree.order_path(payment.order)
32
+ end
33
+
34
+ def failure
35
+ payment.failure!
36
+ flash.notice = Spree.t(:payment_processing_failed)
37
+ flash['order_completed'] = true
38
+ redirect_to spree.checkout_state_path(state: :payment)
39
+ end
40
+
41
+ def ipn
42
+ notification = Mercadopago::Notification
43
+ .new(operation_id: params[:id], topic: params[:topic])
44
+
45
+ if notification.save
46
+ Mercadopago::HandleReceivedNotification.new(notification).process!
47
+ status = :ok
48
+ else
49
+ status = :bad_request
50
+ end
51
+
52
+ render json: :empty, status: status
53
+ end
54
+
55
+ private
56
+
57
+ def payment
58
+ @payment ||= Spree::Payment.where(number: params[:external_reference])
59
+ .first
60
+ end
61
+
62
+ def callback_urls
63
+ @callback_urls ||= {
64
+ success: mercadopago_success_url,
65
+ pending: mercadopago_success_url,
66
+ failure: mercadopago_failure_url
67
+ }
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest_client'
4
+ require 'mercadopago/client/authentication'
5
+ require 'mercadopago/client/preferences'
6
+ require 'mercadopago/client/api'
7
+
8
+ module Mercadopago
9
+ class Client
10
+ # These three includes are because of the user of line_item_description from
11
+ # ProductsHelper
12
+ include Authentication
13
+ include Preferences
14
+ include Api
15
+
16
+ attr_reader :errors
17
+ attr_reader :auth_response
18
+ attr_reader :preferences_response
19
+
20
+ def initialize(payment_method, options = {})
21
+ @payment_method = payment_method
22
+ @api_options = options.clone
23
+ @errors = []
24
+ end
25
+
26
+ def get_operation_info(operation_id)
27
+ url = create_url(notifications_url(operation_id), access_token: access_token)
28
+ options = { content_type: 'application/x-www-form-urlencoded', accept: 'application/json' }
29
+ get(url, options, quiet: true)
30
+ end
31
+
32
+ # def get_external_reference(operation_id)
33
+ # response = send_notification_request(operation_id)
34
+ # if response
35
+ # response['collection']['external_reference']
36
+ # end
37
+ # end
38
+
39
+ def get_payment_status(external_reference)
40
+ response = send_search_request(external_reference: external_reference, access_token: access_token)
41
+
42
+ if response['results'].empty?
43
+ 'pending'
44
+ else
45
+ response['results'][0]['collection']['status']
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def log_error(msg, response, request, result)
52
+ Rails.logger.info msg
53
+ Rails.logger.info "response: #{response}."
54
+ Rails.logger.info "request args: #{request.args}."
55
+ Rails.logger.info "result #{result}."
56
+ end
57
+
58
+ def send_search_request(params, _options = {})
59
+ url = create_url(search_url, params)
60
+ options = { content_type: 'application/x-www-form-urlencoded', accept: 'application/json' }
61
+ get(url, options)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class Mercadopago::Client
6
+ module Api
7
+ def redirect_url
8
+ point_key = sandbox ? 'sandbox_init_point' : 'init_point'
9
+ @preferences_response[point_key]
10
+ end
11
+
12
+ private
13
+
14
+ def notifications_url(operation_id)
15
+ sandbox_part = sandbox ? 'sandbox/' : ''
16
+ "https://api.mercadolibre.com/#{sandbox_part}collections/notifications/#{operation_id}"
17
+ end
18
+
19
+ def search_url
20
+ sandbox_part = sandbox ? 'sandbox/' : ''
21
+ "https://api.mercadolibre.com/#{sandbox_part}collections/search"
22
+ end
23
+
24
+ def create_url(url, params = {})
25
+ uri = URI(url)
26
+ uri.query = URI.encode_www_form(params)
27
+ uri.to_s
28
+ end
29
+
30
+ def preferences_url(token)
31
+ create_url('https://api.mercadolibre.com/checkout/preferences', access_token: token)
32
+ end
33
+
34
+ def sandbox
35
+ @payment_method.preferred_sandbox
36
+ end
37
+
38
+ def get(url, request_options = {}, options = {})
39
+ response = RestClient.get(url, request_options)
40
+ JSON.parse(response)
41
+ # TODO: add class to rescue
42
+ rescue => e
43
+ raise e unless options[:quiet]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class Mercadopago::Client
6
+ module Authentication
7
+ def authenticate
8
+ response = send_authentication_request
9
+ @auth_response = JSON.parse(response)
10
+ rescue RestClient::Exception => e
11
+ @errors << I18n.t(:authentication_error, scope: :mercadopago)
12
+ raise e.message
13
+ end
14
+
15
+ private
16
+
17
+ def send_authentication_request
18
+ RestClient.post(
19
+ 'https://api.mercadolibre.com/oauth/token',
20
+ { grant_type: 'client_credentials', client_id: client_id, client_secret: client_secret },
21
+ content_type: 'application/x-www-form-urlencoded', accept: 'application/json'
22
+ )
23
+ end
24
+
25
+ def client_id
26
+ @payment_method.preferred_client_id
27
+ end
28
+
29
+ def client_secret
30
+ @payment_method.preferred_client_secret
31
+ end
32
+
33
+ def access_token
34
+ authenticate unless @auth_response
35
+ @auth_response['access_token']
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class Mercadopago::Client
6
+ module Preferences
7
+ def create_preferences(preferences)
8
+ response = send_preferences_request(preferences)
9
+ @preferences_response = JSON.parse(response)
10
+ rescue RestClient::Exception => e
11
+ @errors << I18n.t(:authentication_error, scope: :mercadopago)
12
+ raise e.message
13
+ end
14
+
15
+ private
16
+
17
+ def send_preferences_request(preferences)
18
+ RestClient.post(preferences_url(access_token), preferences.to_json,
19
+ content_type: 'application/json', accept: 'application/json')
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mercadopago
4
+ class Notification < ActiveRecord::Base
5
+ self.table_name = 'mercadopago_notifications'
6
+
7
+ validates :topic, presence: true, inclusion: { in: %w[payment preapproval authorized_payment merchant_order] }
8
+ validates :operation_id, presence: true
9
+ end
10
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mercadopago
4
+ class OrderPreferencesBuilder
5
+ include ActionView::Helpers::TextHelper
6
+ include ActionView::Helpers::SanitizeHelper
7
+ include Spree::ProductsHelper
8
+
9
+ def initialize(order, payment, callback_urls, payer_data = nil)
10
+ @order = order
11
+ @payment = payment
12
+ @callback_urls = callback_urls
13
+ @payer_data = payer_data
14
+ end
15
+
16
+ def preferences_hash
17
+ {
18
+ external_reference: @payment.number,
19
+ back_urls: @callback_urls,
20
+ payer: @payer_data,
21
+ items: generate_items
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def generate_items
28
+ items = []
29
+
30
+ items += generate_items_from_line_items
31
+ items += generate_items_from_adjustments
32
+ items += generate_items_from_shipments
33
+
34
+ items
35
+ end
36
+
37
+ def generate_items_from_shipments
38
+ @order.shipments.collect do |shipment|
39
+ {
40
+ title: shipment.shipping_method.name,
41
+ unit_price: shipment.cost.to_f + shipment.adjustment_total.to_f,
42
+ quantity: 1
43
+ }
44
+ end
45
+ end
46
+
47
+ def generate_items_from_line_items
48
+ @order.line_items.collect do |line_item|
49
+ {
50
+ title: line_item_description_text(line_item.variant.product.name),
51
+ unit_price: line_item.price.to_f,
52
+ quantity: line_item.quantity
53
+ }
54
+ end
55
+ end
56
+
57
+ def generate_items_from_adjustments
58
+ @order.all_adjustments.eligible.collect do |adjustment|
59
+ {
60
+ title: line_item_description_text(adjustment.label),
61
+ unit_price: adjustment.amount.to_f,
62
+ quantity: 1
63
+ }
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ class PaymentMethod::Mercadopago < PaymentMethod
5
+ preference :sandbox, :boolean, default: true
6
+ preference :client_id, :string, default: ENV['Mercadopago_CLIENT_ID']
7
+ preference :client_secret, :string, default: ENV['Mercadopago_CLIENT_SECRET']
8
+
9
+ def payment_profiles_supported?
10
+ false
11
+ end
12
+
13
+ def provider_class
14
+ ::Mercadopago::Client
15
+ end
16
+
17
+ def provider(additional_options = {})
18
+ @provider ||= begin
19
+ options = { sandbox: preferred_sandbox }
20
+ client = provider_class.new(self, options.merge(additional_options))
21
+ client.authenticate
22
+ client
23
+ end
24
+ end
25
+
26
+ def source_required?
27
+ false
28
+ end
29
+
30
+ def auto_capture?
31
+ false
32
+ end
33
+
34
+ ## Admin options
35
+
36
+ def can_void?(payment)
37
+ payment.state != 'void'
38
+ end
39
+
40
+ def actions
41
+ %w[void]
42
+ end
43
+
44
+ def void(*_args)
45
+ ActiveMerchant::Billing::Response.new(true, '', {}, {})
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mercadopago
4
+ class HandleReceivedNotification
5
+ def initialize(notification)
6
+ @notification = notification
7
+ end
8
+
9
+ # The purpose of this method is to enable async/sync processing
10
+ # of Mercado Pago IPNs. For simplicity processing is synchronous but
11
+ # if you would like to enqueue the processing via Resque/Ost/etc you
12
+ # will be able to do it.
13
+ def process!
14
+ # Sync
15
+ ProcessNotification.new(@notification).process!
16
+ # Async Will be configurable via block for example:
17
+ # Resque.enqueue(ProcessNotificationWorker, {id: @notification.id})
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Process notification:
4
+ # ---------------------
5
+ # Fetch collection information
6
+ # Find payment by external reference
7
+ # If found
8
+ # Update payment status
9
+ # Notify user
10
+ # If not found
11
+ # Ignore notification (maybe payment from outside Spree)
12
+ module Mercadopago
13
+ class ProcessNotification
14
+ # Equivalent payment states
15
+ # MP state => Spree state
16
+ # =======================
17
+ #
18
+ # approved => complete
19
+ # pending => pend
20
+ # in_process => pend
21
+ # rejected => failed
22
+ # refunded => void
23
+ # cancelled => void
24
+ # in_mediation => pend
25
+ # charged_back => void
26
+ STATES = {
27
+ complete: %w[approved],
28
+ failure: %w[rejected],
29
+ void: %w[refunded cancelled charged_back]
30
+ }.freeze
31
+
32
+ attr_reader :notification
33
+
34
+ def initialize(notification)
35
+ @notification = notification
36
+ end
37
+
38
+ def process!
39
+ # Fix: Payment method is an instance of Spree::PaymentMethod::Mercadopago not THE class
40
+ client = ::Spree::PaymentMethod::Mercadopago.first.provider
41
+ raw_op_info = client.get_operation_info(notification.operation_id)
42
+ op_info = raw_op_info['collection'] if raw_op_info.present?
43
+ # TODO: rewrite this.
44
+ if op_info && (payment = Spree::Payment.where(number: op_info['external_reference']).first)
45
+ if STATES[:complete].include?(op_info['status'])
46
+ payment.complete
47
+ elsif STATES[:failure].include?(op_info['status'])
48
+ payment.failure
49
+ elsif STATES[:void].include?(op_info['status'])
50
+ payment.void
51
+ end
52
+
53
+ # When Spree issue #5246 is fixed we can remove this line
54
+ payment.order.updater.update
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,15 @@
1
+ <script>
2
+ MercadoPago.paymentMethodID = "<%= payment_method.id %>";
3
+ </script>
4
+
5
+ <div class="row">
6
+ <div class="twelve rows alpha omega">
7
+ <img src="http://imgmp.mlstatic.com/org-img/banners/ar/medios/575X40.jpg" title="MercadoPago - Medios de pago" alt="MercadoPago - Medios de pago" width="575" height="40"/>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="row">
12
+ <div class="twelve rows alpha omega">
13
+ <%= link_to I18n.t(:make_payment, scope: :mercadopago), mercadopago_checkout_path(payment_method_id: payment_method.id), method: :post, class: "button primary" %>
14
+ </div>
15
+ </div>
@@ -0,0 +1,4 @@
1
+ en:
2
+ mercadopago:
3
+ make_payment: "Pay with Mercado Pago"
4
+ authentication_error: "It was not possible to authenticate with Mercado Pago"
@@ -0,0 +1,4 @@
1
+ es:
2
+ mercadopago:
3
+ make_payment: "Realizar pago en Mercado Pago"
4
+ authentication_error: "Hubo un error al realizar la autenticación con Mercado Pago"
@@ -0,0 +1,4 @@
1
+ pt-BR:
2
+ mercadopago:
3
+ make_payment: "Pagar com Mercado Pago"
4
+ authentication_error: "Não foi possível autenticar com Mercado Pago"
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ Spree::Core::Engine.routes.draw do
4
+ post '/mercadopago/checkout', to: 'mercadopago#checkout', as: :mercadopago_checkout
5
+ get '/mercadopago/success', to: 'mercadopago#success', as: :mercadopago_success
6
+ get '/mercadopago/failure', to: 'mercadopago#failure', as: :mercadopago_failure
7
+ post '/mercadopago/ipn', to: 'mercadopago#ipn', as: :mercadopago_ipn
8
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSolidusMercadopagoNotifications < ActiveRecord::Migration[4.2]
4
+ def change
5
+ create_table :mercadopago_notifications do |t|
6
+ t.string :topic
7
+ t.string :operation_id
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ # rubocop:disable Style/RegexpLiteral
2
+ # rubocop:disable Rails/Output
3
+
4
+ module SolidusMercadopago
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ class_option :auto_run_migrations, type: :boolean, default: false
8
+ class_option :auto_skip_migrations, type: :boolean, default: false
9
+
10
+ def add_javascripts
11
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_mercadopago\n"
12
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_mercadopago\n"
13
+ end
14
+
15
+ def add_stylesheets
16
+ frontend_css_file = 'vendor/assets/stylesheets/spree/frontend/all.css'
17
+ backend_css_file = 'vendor/assets/stylesheets/spree/backend/all.css'
18
+
19
+ return unless File.exist?(backend_css_file) && File.exist?(frontend_css_file)
20
+
21
+ inject_into_file frontend_css_file, " *= require spree/frontend/solidus_mercadopago\n", before: /\*\//, verbose: true
22
+ inject_into_file backend_css_file, " *= require spree/backend/solidus_mercadopago\n", before: /\*\//, verbose: true
23
+ end
24
+
25
+ def add_migrations
26
+ if !options[:auto_skip_migrations]
27
+ run 'bundle exec rake solidus_mercadopago:install:migrations'
28
+ else
29
+ puts 'Skipping rake solidus_mercadopago:install:migrations, don\'t forget to run it!'
30
+ end
31
+ end
32
+
33
+ def run_migrations
34
+ run_migrations = options[:auto_skip_migrations] ||
35
+ options[:auto_run_migrations] ||
36
+ ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
37
+
38
+ if run_migrations && !options[:auto_skip_migrations]
39
+ run 'bundle exec rake db:migrate'
40
+ else
41
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'solidus_core'
4
+ require 'solidus_support'
5
+ require 'solidus_mercadopago/version'
6
+ require 'solidus_mercadopago/engine'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spree/core'
4
+
5
+ module SolidusMercadopago
6
+ class Engine < Rails::Engine
7
+ include SolidusSupport::EngineExtensions
8
+
9
+ isolate_namespace ::Spree
10
+ engine_name 'solidus_mercadopago'
11
+
12
+ # use rspec for tests
13
+ config.generators do |g|
14
+ g.test_framework :rspec
15
+ end
16
+
17
+ initializer 'spree_payment_network.register.payment_methods' do |app|
18
+ app.config.spree.payment_methods << Spree::PaymentMethod::Mercadopago
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ FactoryBot.define do
4
+ # Define your Spree extensions Factories within this file to enable applications, and other extensions to use and override them.
5
+ #
6
+ # Example adding this to your spec_helper will load these Factories for use:
7
+ # require 'solidus_mercadopago/factories'
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusMercadopago
4
+ VERSION = '1.0.0'.freeze
5
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :mercadopago do
4
+ desc 'Creates test user using MP API'
5
+ task test_user: :environment do
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,227 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solidus_mercadopago
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Tapia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: solidus
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
47
+ - !ruby/object:Gem::Dependency
48
+ name: solidus_auth_devise
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ - - "<"
55
+ - !ruby/object:Gem::Version
56
+ version: '3'
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '2.0'
64
+ - - "<"
65
+ - !ruby/object:Gem::Version
66
+ version: '3'
67
+ - !ruby/object:Gem::Dependency
68
+ name: solidus_support
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: deface
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '1.0'
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '1.0'
95
+ - !ruby/object:Gem::Dependency
96
+ name: coffee-rails
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ - !ruby/object:Gem::Dependency
110
+ name: capybara-accessible
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: poltergeist
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: solidus_dev_support
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ - !ruby/object:Gem::Dependency
152
+ name: webmock
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ description: Integrates Mercado Pago with Solidus
166
+ email: jonathan.tapia@magmalabs.io
167
+ executables: []
168
+ extensions: []
169
+ extra_rdoc_files: []
170
+ files:
171
+ - LICENSE
172
+ - README.md
173
+ - Rakefile
174
+ - app/assets/javascripts/spree/backend/solidus_mercado_pago.js
175
+ - app/assets/javascripts/spree/frontend/solidus_mercado_pago.js
176
+ - app/assets/stylesheets/spree/backend/solidus_mercado_pago.css
177
+ - app/assets/stylesheets/spree/frontend/solidus_mercado_pago.css
178
+ - app/controllers/spree/mercadopago_controller.rb
179
+ - app/models/mercadopago/client.rb
180
+ - app/models/mercadopago/client/api.rb
181
+ - app/models/mercadopago/client/authentication.rb
182
+ - app/models/mercadopago/client/preferences.rb
183
+ - app/models/mercadopago/notification.rb
184
+ - app/models/mercadopago/order_preferences_builder.rb
185
+ - app/models/spree/payment_method/mercadopago.rb
186
+ - app/services/mercadopago/handle_received_notification.rb
187
+ - app/services/mercadopago/process_notification.rb
188
+ - app/views/spree/admin/payments/source_forms/_mercadopago.html.erb
189
+ - app/views/spree/admin/payments/source_views/_mercadopago.html.erb
190
+ - app/views/spree/checkout/mercado_pago_error.html.erb
191
+ - app/views/spree/checkout/payment/_mercadopago.html.erb
192
+ - config/locales/en.yml
193
+ - config/locales/es.yml
194
+ - config/locales/pt-BR.yml
195
+ - config/routes.rb
196
+ - db/migrate/20141201204026_create_solidus_mercadopago_notifications.rb
197
+ - lib/generators/solidus_mercadopago/install/install_generator.rb
198
+ - lib/solidus_mercadopago.rb
199
+ - lib/solidus_mercadopago/engine.rb
200
+ - lib/solidus_mercadopago/factories.rb
201
+ - lib/solidus_mercadopago/version.rb
202
+ - lib/tasks/mercado_user.rake
203
+ homepage: http://github.com/magma-labs/solidus_mercadopago
204
+ licenses:
205
+ - BSD-3-Clause
206
+ metadata: {}
207
+ post_install_message:
208
+ rdoc_options: []
209
+ require_paths:
210
+ - lib
211
+ required_ruby_version: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ required_rubygems_version: !ruby/object:Gem::Requirement
217
+ requirements:
218
+ - - ">="
219
+ - !ruby/object:Gem::Version
220
+ version: '0'
221
+ requirements: []
222
+ rubyforge_project:
223
+ rubygems_version: 2.7.6
224
+ signing_key:
225
+ specification_version: 4
226
+ summary: Solidus plugin to integrate Mercado Pago
227
+ test_files: []