workarea-paypal 2.0.8

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 (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 .