workarea-paypal 2.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintignore +2 -0
  4. data/.eslintrc +24 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  6. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.gitignore +19 -0
  9. data/.rspec +2 -0
  10. data/.scss-lint.yml +188 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +314 -0
  13. data/CODE_OF_CONDUCT.md +3 -0
  14. data/CONTRIBUTING.md +3 -0
  15. data/Gemfile +6 -0
  16. data/LICENSE +52 -0
  17. data/README.md +47 -0
  18. data/Rakefile +54 -0
  19. data/app/assets/images/workarea/admin/payment_icons/paypal.svg +1 -0
  20. data/app/assets/images/workarea/storefront/payment_icons/paypal.svg +1 -0
  21. data/app/assets/javascripts/workarea/storefront/paypal/modules/update_checkout_submit_text.js +60 -0
  22. data/app/controllers/workarea/storefront/checkout/place_order_controller.decorator +13 -0
  23. data/app/controllers/workarea/storefront/paypal_controller.rb +51 -0
  24. data/app/models/workarea/payment.decorator +27 -0
  25. data/app/models/workarea/payment/authorize/paypal.rb +31 -0
  26. data/app/models/workarea/payment/capture/paypal.rb +35 -0
  27. data/app/models/workarea/payment/purchase/paypal.rb +31 -0
  28. data/app/models/workarea/payment/refund/paypal.rb +26 -0
  29. data/app/models/workarea/payment/tender/paypal.rb +11 -0
  30. data/app/models/workarea/search/order_text.decorator +8 -0
  31. data/app/services/workarea/paypal/setup.rb +114 -0
  32. data/app/services/workarea/paypal/update.rb +69 -0
  33. data/app/view_models/workarea/admin/paypal_view_model.rb +6 -0
  34. data/app/view_models/workarea/store_front/checkout/payment_view_model.decorator +18 -0
  35. data/app/view_models/workarea/store_front/credit_card_view_model.decorator +7 -0
  36. data/app/view_models/workarea/store_front/paypal_view_model.rb +6 -0
  37. data/app/views/workarea/admin/orders/tenders/_paypal.html.haml +2 -0
  38. data/app/views/workarea/api/orders/tenders/_paypal.json.jbuilder +3 -0
  39. data/app/views/workarea/storefront/carts/_paypal_checkout.html.haml +1 -0
  40. data/app/views/workarea/storefront/checkouts/_paypal_error.html.haml +6 -0
  41. data/app/views/workarea/storefront/checkouts/_paypal_payment.html.haml +12 -0
  42. data/app/views/workarea/storefront/order_mailer/tenders/_paypal.html.haml +3 -0
  43. data/app/views/workarea/storefront/order_mailer/tenders/_paypal.text.haml +2 -0
  44. data/app/views/workarea/storefront/orders/tenders/_paypal.html.haml +2 -0
  45. data/bin/rails +17 -0
  46. data/config/initializers/append_points.rb +19 -0
  47. data/config/initializers/workarea.rb +7 -0
  48. data/config/locales/en.yml +19 -0
  49. data/config/routes.rb +4 -0
  50. data/lib/workarea/paypal.rb +34 -0
  51. data/lib/workarea/paypal/engine.rb +8 -0
  52. data/lib/workarea/paypal/version.rb +5 -0
  53. data/test/dummy/Rakefile +6 -0
  54. data/test/dummy/app/assets/config/manifest.js +4 -0
  55. data/test/dummy/app/assets/javascripts/application.js +13 -0
  56. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  57. data/test/dummy/app/controllers/application_controller.rb +3 -0
  58. data/test/dummy/app/helpers/application_helper.rb +2 -0
  59. data/test/dummy/app/jobs/application_job.rb +2 -0
  60. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  61. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  62. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  63. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  64. data/test/dummy/bin/bundle +3 -0
  65. data/test/dummy/bin/rails +4 -0
  66. data/test/dummy/bin/rake +4 -0
  67. data/test/dummy/bin/setup +34 -0
  68. data/test/dummy/bin/update +29 -0
  69. data/test/dummy/config.ru +5 -0
  70. data/test/dummy/config/application.rb +24 -0
  71. data/test/dummy/config/boot.rb +5 -0
  72. data/test/dummy/config/cable.yml +9 -0
  73. data/test/dummy/config/environment.rb +5 -0
  74. data/test/dummy/config/environments/development.rb +54 -0
  75. data/test/dummy/config/environments/production.rb +86 -0
  76. data/test/dummy/config/environments/test.rb +43 -0
  77. data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
  78. data/test/dummy/config/initializers/assets.rb +11 -0
  79. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  80. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  81. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  82. data/test/dummy/config/initializers/inflections.rb +16 -0
  83. data/test/dummy/config/initializers/mime_types.rb +4 -0
  84. data/test/dummy/config/initializers/new_framework_defaults.rb +21 -0
  85. data/test/dummy/config/initializers/session_store.rb +3 -0
  86. data/test/dummy/config/initializers/workarea.rb +5 -0
  87. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  88. data/test/dummy/config/locales/en.yml +23 -0
  89. data/test/dummy/config/puma.rb +47 -0
  90. data/test/dummy/config/routes.rb +5 -0
  91. data/test/dummy/config/secrets.yml +22 -0
  92. data/test/dummy/config/spring.rb +6 -0
  93. data/test/dummy/db/seeds.rb +2 -0
  94. data/test/dummy/log/.keep +0 -0
  95. data/test/dummy/public/404.html +67 -0
  96. data/test/dummy/public/422.html +67 -0
  97. data/test/dummy/public/500.html +66 -0
  98. data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
  99. data/test/dummy/public/apple-touch-icon.png +0 -0
  100. data/test/dummy/public/favicon.ico +0 -0
  101. data/test/integration/workarea/storefront/paypal_integration_test.rb +345 -0
  102. data/test/integration/workarea/storefront/place_order_integration_test.decorator +11 -0
  103. data/test/models/workarea/payment/authorize/paypal_test.rb +94 -0
  104. data/test/models/workarea/payment/capture/paypal_test.rb +69 -0
  105. data/test/models/workarea/payment/purchase/paypal_test.rb +94 -0
  106. data/test/models/workarea/payment/refund/paypal_test.rb +69 -0
  107. data/test/models/workarea/payment_test.decorator +34 -0
  108. data/test/models/workarea/search/admin/order_test.decorator +32 -0
  109. data/test/services/workarea/paypal/setup_test.rb +120 -0
  110. data/test/services/workarea/paypal/update_test.rb +221 -0
  111. data/test/system/workarea/storefront/cart_system_test.decorator +14 -0
  112. data/test/system/workarea/storefront/logged_in_checkout_system_test.decorator +9 -0
  113. data/test/test_helper.rb +10 -0
  114. data/workarea-paypal.gemspec +21 -0
  115. metadata +170 -0
@@ -0,0 +1,11 @@
1
+ module Workarea
2
+ class Payment::Tender::Paypal < Payment::Tender
3
+ field :token, type: String
4
+ field :payer_id, type: String
5
+ field :details, type: Hash
6
+
7
+ def slug
8
+ :paypal
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ decorate Search::OrderText, with: :paypal do
3
+ def payment_text
4
+ return 'PayPal' if payment.paypal?
5
+ super
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,114 @@
1
+ module Workarea
2
+ module Paypal
3
+ class Setup
4
+ attr_reader :order, :user, :shipping, :context
5
+ delegate :request, to: :context
6
+ delegate :gateway, to: Workarea::Paypal
7
+
8
+ def initialize(order, user, shipping, context)
9
+ @order = order
10
+ @user = user
11
+ @shipping = shipping
12
+ @context = context
13
+ end
14
+
15
+ def paypal_response
16
+ @paypal_response ||= gateway.setup_authorization(
17
+ order.total_price.cents,
18
+ ip: request.remote_ip,
19
+ return_url: context.complete_paypal_url(order.id),
20
+ cancel_return_url: context.cart_url,
21
+ currency: order.total_price.currency,
22
+ subtotal: subtotal,
23
+ shipping: order.shipping_total.cents,
24
+ handling: 0,
25
+ tax: order.tax_total.cents,
26
+ items: items,
27
+ shipping_address: shipping_address,
28
+ order_id: order.id
29
+ )
30
+ end
31
+
32
+ def items
33
+ order_items + item_level_discounts + order_level_discounts
34
+ end
35
+
36
+ def subtotal
37
+ order.subtotal_price.cents +
38
+ order.price_adjustments.adjusting('order').discounts.sum.cents
39
+ end
40
+
41
+ def shipping_address
42
+ serialize_address(shipping.try(:address) || user.try(:default_shipping_address))
43
+ end
44
+
45
+ def token
46
+ paypal_response.token
47
+ end
48
+
49
+ def redirect_url
50
+ gateway.redirect_url_for(token)
51
+ end
52
+
53
+ private
54
+
55
+ def serialize_address(address)
56
+ return {} unless address
57
+
58
+ name = "#{address.first_name} #{address.last_name}"
59
+ phone = [address.phone_number, address.phone_extension].reject(&:blank?).join(' x')
60
+
61
+ { name: name,
62
+ address1: address.street,
63
+ address2: address.street_2,
64
+ city: address.city,
65
+ state: address.region,
66
+ country: address.country,
67
+ zip: address.postal_code,
68
+ phone: phone }
69
+ end
70
+
71
+ def order_items
72
+ order.items.map do |item|
73
+ item_discount_total = item.price_adjustments.discounts.sum.cents
74
+
75
+ {
76
+ name: item.product_attributes.with_indifferent_access[:name],
77
+ quantity: item.quantity,
78
+ amount: (item.total_price.cents - item_discount_total) / item.quantity
79
+ }
80
+ end
81
+ end
82
+
83
+ def item_level_discounts
84
+ order_items_with_discounts = order.items.select do |item|
85
+ item.price_adjustments.discounts.present?
86
+ end
87
+
88
+ order_items_with_discounts.flat_map do |item|
89
+ item.price_adjustments.discounts.map do |discount|
90
+ {
91
+ name: discount.description.titleize,
92
+ quantity: 1,
93
+ amount: discount.amount.cents
94
+ }
95
+ end
96
+ end
97
+ end
98
+
99
+ def order_level_discounts
100
+ order
101
+ .price_adjustments
102
+ .adjusting('order')
103
+ .discounts
104
+ .map do |discount|
105
+ {
106
+ name: discount.description.titleize,
107
+ quantity: 1,
108
+ amount: discount.amount.cents
109
+ }
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,69 @@
1
+ module Workarea
2
+ module Paypal
3
+ class Update
4
+ attr_reader :order, :payment, :shipping, :token
5
+
6
+ def initialize(order, payment, shipping, token)
7
+ @order = order
8
+ @payment = payment
9
+ @shipping = shipping
10
+ @token = token
11
+ end
12
+
13
+ def details
14
+ @details ||= Workarea::Paypal.gateway.details_for(token)
15
+ end
16
+
17
+ def apply
18
+ order.email ||= details.email
19
+ set_shipping_address(details.params)
20
+ set_shipping_service
21
+ set_billing_address(details.params)
22
+
23
+ payment.clear_credit_card
24
+ payment.set_paypal(
25
+ token: details.token,
26
+ payer_id: details.payer_id,
27
+ details: details.params
28
+ )
29
+
30
+ Pricing.perform(order, shipping)
31
+ payment.adjust_tender_amounts(order.total_price)
32
+ end
33
+
34
+ private
35
+
36
+ def set_shipping_address(params)
37
+ shipping.set_address(
38
+ first_name: params['first_name'],
39
+ last_name: params['last_name'],
40
+ street: params['PaymentDetails']['ShipToAddress']['Street1'],
41
+ street_2: params['PaymentDetails']['ShipToAddress']['Street2'],
42
+ city: params['PaymentDetails']['ShipToAddress']['CityName'],
43
+ region: params['PaymentDetails']['ShipToAddress']['StateOrProvince'],
44
+ postal_code: params['PaymentDetails']['ShipToAddress']['PostalCode'],
45
+ country: params['PaymentDetails']['ShipToAddress']['Country'],
46
+ phone_number: params['PaymentDetails']['ShipToAddress']['Phone']
47
+ )
48
+ end
49
+
50
+ def set_billing_address(params)
51
+ payment.set_address(
52
+ first_name: params['PayerInfo']['PayerName']['FirstName'],
53
+ last_name: params['PayerInfo']['PayerName']['LastName'],
54
+ street: params['PayerInfo']['Address']['Street1'],
55
+ street_2: params['PayerInfo']['Address']['Street2'],
56
+ city: params['PayerInfo']['Address']['CityName'],
57
+ region: params['PayerInfo']['Address']['StateOrProvince'],
58
+ postal_code: params['PayerInfo']['Address']['PostalCode'],
59
+ country: params['PayerInfo']['Address']['Country'],
60
+ phone_number: params['PayerInfo']['Address']['Phone']
61
+ )
62
+ end
63
+
64
+ def set_shipping_service
65
+ Checkout::Steps::Shipping.new(self).update({})
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,6 @@
1
+ module Workarea
2
+ module Admin
3
+ class PaypalViewModel < ApplicationViewModel
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ module Workarea
2
+ decorate Storefront::Checkout::PaymentViewModel, with: :paypal do
3
+ def paypal?
4
+ payment.paypal?
5
+ end
6
+
7
+ def using_new_card?
8
+ super && !paypal?
9
+ end
10
+
11
+ def credit_cards
12
+ super.map do |card|
13
+ card.options[:paypal] = paypal?
14
+ card
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Storefront::CreditCardViewModel, with: :paypal do
3
+ def selected?
4
+ super && !options[:paypal]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Workarea
2
+ module Storefront
3
+ class PaypalViewModel < ApplicationViewModel
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,2 @@
1
+ = t("workarea.admin.orders.tenders.paypal.title")
2
+ = number_to_currency tender.amount
@@ -0,0 +1,3 @@
1
+ json.type 'paypal'
2
+ json.token tender.token
3
+ json.payer_id tender.payer_id
@@ -0,0 +1 @@
1
+ %p.cart__checkout-action.cart__checkout-action--paypal= link_to(image_tag('https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif'), start_paypal_path, alt: t('workarea.storefront.paypal.check_out'))
@@ -0,0 +1,6 @@
1
+ - if step.paypal? && step.payment.paypal.errors.any?
2
+ - step.payment.paypal.errors.full_messages.each do |message|
3
+ .message.message--error
4
+ %p.message__type
5
+ %span.message__icon= t('workarea.storefront.messages.error')
6
+ %p.message__text= message
@@ -0,0 +1,12 @@
1
+ .checkout-payment__primary-method{ class: ('checkout-payment__primary-method--selected' if @step.paypal?) }
2
+ .button-property
3
+ = hidden_field_tag 'from_checkout', 'from_checkout'
4
+ .value= radio_button_tag 'payment', 'paypal', step.paypal?, data: { update_checkout_submit_text: { prevent: step.paypal?, text: t('workarea.storefront.paypal.checkout_submit_text') }.to_json }
5
+ = label_tag 'payment[paypal]', nil, class: 'button-property__name' do
6
+ %span.button-property__text= t('workarea.storefront.paypal.paypal')
7
+ %p.checkout-payment__primary-method-description
8
+ = inline_svg('workarea/admin/payment_icons/paypal.svg', class: 'payment-icon', title: 'PayPal')
9
+ - if @step.paypal?
10
+ %span= t('workarea.storefront.paypal.payment_recieved')
11
+ - else
12
+ %span= t('workarea.storefront.paypal.on_continue')
@@ -0,0 +1,3 @@
1
+ %p{ style: "margin: 0 0 6px; font: 13px/1.5 arial; color: #{@config.text_color};" }
2
+ = t('workarea.storefront.paypal.paid_with_paypal')
3
+ = number_to_currency tender.amount
@@ -0,0 +1,2 @@
1
+ = t('workarea.storefront.paypal.paid_with_paypal')
2
+ = number_to_currency tender.amount
@@ -0,0 +1,2 @@
1
+ %p= t('workarea.storefront.paypal.paid_with_paypal')
2
+ %p= number_to_currency(tender.amount)
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 4 gems installed from the root of your application.
3
+
4
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
5
+ ENGINE_PATH = File.expand_path('../../lib/workarea/paypal/engine', __FILE__)
6
+
7
+ # Set up gems listed in the Gemfile.
8
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
9
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
10
+
11
+ require 'action_controller/railtie'
12
+ require 'action_view/railtie'
13
+ require 'action_mailer/railtie'
14
+ require 'rails/test_unit/railtie'
15
+ require 'sprockets/railtie'
16
+
17
+ require 'rails/engine/commands'
@@ -0,0 +1,19 @@
1
+ Workarea::Plugin.append_partials(
2
+ 'storefront.cart_checkout_actions',
3
+ 'workarea/storefront/carts/paypal_checkout'
4
+ )
5
+
6
+ Workarea::Plugin.append_partials(
7
+ 'storefront.payment_error',
8
+ 'workarea/storefront/checkouts/paypal_error'
9
+ )
10
+
11
+ Workarea::Plugin.append_partials(
12
+ 'storefront.payment_method',
13
+ 'workarea/storefront/checkouts/paypal_payment'
14
+ )
15
+
16
+ Workarea::Plugin.append_javascripts(
17
+ 'storefront.modules',
18
+ 'workarea/storefront/paypal/modules/update_checkout_submit_text'
19
+ )
@@ -0,0 +1,7 @@
1
+ Workarea.config.tender_types << :paypal
2
+
3
+ # Gateway application_id is sent as PayPal BN code for revenue sharing
4
+ # 'WebLinc_SP' should be used for all WebLinc installations
5
+ ActiveMerchant::Billing::PaypalGateway.application_id = 'WebLinc_SP'
6
+
7
+ Workarea::Paypal.auto_configure_gateway
@@ -0,0 +1,19 @@
1
+ ---
2
+ en:
3
+ workarea:
4
+ admin:
5
+ orders:
6
+ tenders:
7
+ paypal:
8
+ title: PayPal
9
+ storefront:
10
+ paypal:
11
+ address_error: We can not ship to this address. Please enter a new address.
12
+ check_out: Check Out with PayPal
13
+ checkout_submit_text: Continue to PayPal
14
+ on_continue: When you continue, you will be taken to the PayPal website.
15
+ paid_with_paypal: Paid with PayPal
16
+ payment_recieved: Thank You! We have received your information back from PayPal.
17
+ Please confirm that the shipping address and shipping method are correct,
18
+ then click on 'Place Order'.
19
+ paypal: PayPal
@@ -0,0 +1,4 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ get 'paypal/start' => 'paypal#start', as: :start_paypal
3
+ get 'paypal/complete/:order_id' => 'paypal#complete', as: :complete_paypal
4
+ end
@@ -0,0 +1,34 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ module Workarea
6
+ module Paypal
7
+ def self.gateway
8
+ Workarea.config.gateways.paypal
9
+ end
10
+
11
+ def self.gateway=(gateway)
12
+ Workarea.config.gateways.paypal = gateway
13
+ end
14
+
15
+ def self.auto_configure_gateway
16
+ if Rails.application.secrets.paypal.present?
17
+ self.gateway = ActiveMerchant::Billing::PaypalExpressGateway.new(
18
+ Rails.application.secrets.paypal.deep_symbolize_keys
19
+ )
20
+ elsif gateway.blank?
21
+ self.gateway = ActiveMerchant::Billing::BogusGateway.new
22
+ end
23
+
24
+ if ENV['HTTP_PROXY'].present? && gateway.present?
25
+ parsed = URI.parse(ENV['HTTP_PROXY'])
26
+ gateway.proxy_address = parsed.host
27
+ gateway.proxy_port = parsed.port
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ require 'workarea/paypal/version'
34
+ require 'workarea/paypal/engine'
@@ -0,0 +1,8 @@
1
+ module Workarea
2
+ module Paypal
3
+ class Engine < ::Rails::Engine
4
+ include Workarea::Plugin
5
+ isolate_namespace Workarea::Paypal
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module Paypal
3
+ VERSION = '2.0.8'.freeze
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,4 @@
1
+
2
+ //= link_tree ../images
3
+ //= link_directory ../javascripts .js
4
+ //= link_directory ../stylesheets .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .