solidus_mercadopago 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []