solidus_paypal_commerce_platform 0.3.0 → 0.4.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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +4 -0
  3. data/.rubocop.yml +4 -4
  4. data/CHANGELOG.md +46 -2
  5. data/Gemfile +2 -2
  6. data/LICENSE +1 -1
  7. data/README.md +25 -0
  8. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform/button_actions.js +3 -1
  9. data/app/assets/javascripts/spree/frontend/solidus_paypal_commerce_platform/buttons.js +14 -0
  10. data/app/controllers/solidus_paypal_commerce_platform/payments_controller.rb +4 -2
  11. data/app/helpers/solidus_paypal_commerce_platform/button_options_helper.rb +4 -0
  12. data/app/models/solidus_paypal_commerce_platform/gateway.rb +20 -1
  13. data/app/models/solidus_paypal_commerce_platform/payment_method.rb +22 -0
  14. data/app/models/solidus_paypal_commerce_platform/payment_source.rb +11 -0
  15. data/app/models/solidus_paypal_commerce_platform/paypal_order.rb +26 -5
  16. data/app/models/solidus_paypal_commerce_platform/wizard.rb +1 -1
  17. data/bin/rails-engine +1 -1
  18. data/bin/sandbox +1 -1
  19. data/config/locales/en.yml +34 -1
  20. data/db/migrate/20211220133406_add_paypal_funding_source_to_paypal_commerce_platform_sources.rb +5 -0
  21. data/lib/solidus_paypal_commerce_platform/client.rb +3 -3
  22. data/lib/solidus_paypal_commerce_platform/configuration.rb +2 -2
  23. data/lib/solidus_paypal_commerce_platform/engine.rb +13 -4
  24. data/lib/solidus_paypal_commerce_platform/testing_support/factories.rb +2 -1
  25. data/lib/solidus_paypal_commerce_platform/version.rb +1 -1
  26. data/lib/views/backend/spree/admin/payments/source_views/_paypal_commerce_platform.html.erb +3 -0
  27. data/lib/views/frontend/solidus_paypal_commerce_platform/payments/_payment.html.erb +4 -0
  28. data/lib/views/frontend/spree/checkout/payment/_paypal_commerce_platform.html.erb +17 -3
  29. data/solidus_paypal_commerce_platform.gemspec +3 -3
  30. metadata +14 -64
  31. data/spec/features/backend/new_payment_method_spec.rb +0 -40
  32. data/spec/features/frontend/cart_spec.rb +0 -46
  33. data/spec/features/frontend/checkout_spec.rb +0 -89
  34. data/spec/features/frontend/product_spec.rb +0 -91
  35. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/CHECKOUT.ORDER.COMPLETED.v2.json +0 -121
  36. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/CHECKOUT.ORDER.PROCESSED.v2.json +0 -121
  37. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.COMPLETED.v1.json +0 -50
  38. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.COMPLETED.v2.json +0 -72
  39. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.DENIED.v1.json +0 -50
  40. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.DENIED.v2.json +0 -68
  41. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.REFUNDED.v1.json +0 -51
  42. data/spec/jobs/solidus_paypal_commerce_platform/fixtures/PAYMENT.CAPTURE.REFUNDED.v2.json +0 -63
  43. data/spec/jobs/solidus_paypal_commerce_platform/webhook_job_spec.rb +0 -44
  44. data/spec/lib/solidus_paypal_commerce_platform/client_spec.rb +0 -21
  45. data/spec/lib/solidus_paypal_commerce_platform/configuration_spec.rb +0 -91
  46. data/spec/models/solidus_paypal_commerce_platform/payment_method_spec.rb +0 -121
  47. data/spec/models/solidus_paypal_commerce_platform/payment_source_spec.rb +0 -62
  48. data/spec/models/solidus_paypal_commerce_platform/paypal_address_spec.rb +0 -67
  49. data/spec/models/solidus_paypal_commerce_platform/state_guesser_spec.rb +0 -38
  50. data/spec/models/solidus_paypal_commerce_platform/wizard_spec.rb +0 -9
  51. data/spec/requests/solidus_paypal_commerce_platform/orders_controller_spec.rb +0 -36
  52. data/spec/requests/solidus_paypal_commerce_platform/shipping_rates_controller_spec.rb +0 -44
  53. data/spec/requests/solidus_paypal_commerce_platform/wizard_controller_spec.rb +0 -44
  54. data/spec/spec_helper.rb +0 -32
  55. data/spec/support/capybara.rb +0 -11
  56. data/spec/support/paypal_sdk_script_tag_helper.rb +0 -13
@@ -1,121 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::PaymentMethod, type: :model do
4
- let(:paypal_payment_method) { create(:paypal_payment_method) }
5
- let(:payment) { create(:payment) }
6
- let(:completed_payment) { create(:payment, :completed) }
7
- let(:response) { Struct(status_code: status_code, result: result, headers: headers) }
8
- let(:status_code) { 201 }
9
- let(:result) { nil }
10
- let(:headers) { {} }
11
-
12
- def Struct(data) # rubocop:disable Naming/MethodName
13
- Struct.new(*data.keys, keyword_init: true).new(data)
14
- end
15
-
16
- before { allow_any_instance_of(PayPal::PayPalHttpClient).to receive(:execute) { response } }
17
-
18
- describe "#purchase" do
19
- let(:result) { Struct(purchase_units: [Struct(payments: payments)]) }
20
- let(:payments) { Struct(captures: [Struct(id: SecureRandom.hex(4))]) }
21
-
22
- it "sends a purchase request to paypal" do
23
- paypal_order_id = SecureRandom.hex(8)
24
- source = paypal_payment_method.payment_source_class.create(paypal_order_id: paypal_order_id)
25
- expect_request(:OrdersCaptureRequest).to receive(:new).with(paypal_order_id).and_call_original
26
- paypal_payment_method.purchase(1000, source, {})
27
- end
28
- end
29
-
30
- describe "#authorize" do
31
- let(:result) { Struct(purchase_units: [Struct(payments: payments)]) }
32
- let(:payments) { Struct(authorizations: [Struct(id: SecureRandom.hex(4))]) }
33
-
34
- it "sends an authorize request to paypal" do
35
- paypal_order_id = SecureRandom.hex(8)
36
- source = paypal_payment_method.payment_source_class.create(paypal_order_id: paypal_order_id)
37
- expect_request(:OrdersAuthorizeRequest).to receive(:new).with(paypal_order_id)
38
- paypal_payment_method.authorize(1000, source, {})
39
- end
40
- end
41
-
42
- describe "#capture" do
43
- let(:result) { Struct(id: SecureRandom.hex(4)) }
44
-
45
- it "sends a capture request to paypal" do
46
- authorization_id = SecureRandom.hex(8)
47
- source = paypal_payment_method.payment_source_class.create(authorization_id: authorization_id)
48
- payment.source = source
49
- expect_request(:AuthorizationsCaptureRequest).to receive(:new).with(authorization_id).and_call_original
50
- paypal_payment_method.capture(1000, {}, originator: payment)
51
- end
52
- end
53
-
54
- describe "#void" do
55
- it "sends a void request to paypal" do
56
- authorization_id = SecureRandom.hex(8)
57
- source = paypal_payment_method.payment_source_class.create(authorization_id: authorization_id)
58
- payment.source = source
59
- expect_request(:AuthorizationsVoidRequest).to receive(:new).with(authorization_id)
60
- paypal_payment_method.void(nil, originator: payment)
61
- end
62
- end
63
-
64
- describe "#credit" do
65
- let(:result) { Struct(id: SecureRandom.hex(4)) }
66
-
67
- it "sends a refund request to paypal" do
68
- capture_id = SecureRandom.hex(4)
69
- source = paypal_payment_method.payment_source_class.create(capture_id: capture_id)
70
- completed_payment.source = source
71
- expect_request(:CapturesRefundRequest).to receive(:new).with(capture_id).and_call_original
72
- paypal_payment_method.credit(1000, {}, originator: completed_payment.refunds.new(amount: 12))
73
- expect(source.refund_id).not_to be_blank
74
- end
75
- end
76
-
77
- describe '.javascript_sdk_url' do
78
- subject(:url) { URI(paypal_payment_method.javascript_sdk_url(order: order)) }
79
-
80
- context 'when checkout_steps include "confirm"' do
81
- let(:order) { instance_double(Spree::Order, checkout_steps: { "confirm" => "bar" }) }
82
-
83
- it 'sets autocommit' do
84
- expect(url.query.split("&")).to include("commit=false")
85
- end
86
- end
87
-
88
- context 'when checkout_steps does not include "confirm"' do
89
- let(:order) { instance_double(Spree::Order, checkout_steps: { "foo" => "bar" }) }
90
-
91
- it 'disables autocommit' do
92
- expect(url.query.split("&")).to include("commit=true")
93
- end
94
- end
95
-
96
- context 'when messaging is turned on' do
97
- let(:order) { instance_double(Spree::Order, checkout_steps: { "foo" => "bar" }) }
98
-
99
- it 'includes messaging component' do
100
- paypal_payment_method.preferences.update(display_credit_messaging: true)
101
- expect(url.query.split("&")).to include("components=buttons%2Cmessages")
102
- end
103
- end
104
-
105
- context 'when messaging is turned off' do
106
- let(:order) { instance_double(Spree::Order, checkout_steps: { "foo" => "bar" }) }
107
-
108
- it 'only includes buttons components' do
109
- paypal_payment_method.preferences.update(display_credit_messaging: false)
110
- expect(url.query.split("&")).not_to include("messages")
111
- expect(url.query.split("&")).to include("components=buttons")
112
- end
113
- end
114
- end
115
-
116
- private
117
-
118
- def expect_request(name)
119
- expect(SolidusPaypalCommercePlatform::Gateway.const_get(name))
120
- end
121
- end
@@ -1,62 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::PaymentSource, type: :model do
4
- let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
5
- let(:paypal_payment_method) { create(:paypal_payment_method) }
6
- let(:paypal_order_id) { "foo-123" }
7
- let(:payment_source) {
8
- described_class.new(
9
- paypal_order_id: paypal_order_id,
10
- payment_method: paypal_payment_method,
11
- )
12
- }
13
- let(:payment) {
14
- order.payments.create!(
15
- payment_method: paypal_payment_method,
16
- source: payment_source
17
- )
18
- }
19
- let(:paypal_order_status) { "COMPLETED" }
20
-
21
- before do
22
- allow_any_instance_of(SolidusPaypalCommercePlatform::Client).to receive(:execute) do |_client, request|
23
- expect(request).to be_a(SolidusPaypalCommercePlatform::Gateway::OrdersGetRequest) # rubocop:disable RSpec/ExpectInHook
24
- OpenStruct.new(result: OpenStruct.new(status: paypal_order_status))
25
- end
26
- end
27
-
28
- describe '#actions' do
29
- context 'when the payment is not yet completed' do
30
- it 'shows "capture" and "void"' do
31
- expect(payment.actions).to contain_exactly("capture", "void")
32
- end
33
- end
34
-
35
- context 'when the payment is completed and captured' do
36
- before do
37
- payment.update!(state: :completed, amount: 123)
38
- payment_source.update!(capture_id: 1234)
39
- end
40
-
41
- it 'shows "credit"' do
42
- expect(payment.actions).to contain_exactly("credit")
43
- end
44
-
45
- context 'when the PayPal order status is not COMPLETED' do
46
- let(:paypal_order_status) { "FOOBAR" }
47
-
48
- it 'hides the "credit" action' do
49
- expect(payment.actions).not_to include("credit")
50
- end
51
- end
52
- end
53
-
54
- context 'when it cannot capture' do
55
- it 'also cannot void' do
56
- # One for "capture", and one for "void"
57
- expect(payment_source).to receive(:can_capture?).and_return(false).twice
58
- expect(payment.actions).not_to include("void")
59
- end
60
- end
61
- end
62
- end
@@ -1,67 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::PaypalAddress, type: :model do
4
- let(:order) { create(:order) }
5
- let(:original_address) { create(:address) }
6
- let(:address) { create(:address, name_attributes) }
7
- let(:params) {
8
- {
9
- updated_address: {
10
- admin_area_1: address.state.abbr,
11
- admin_area_2: address.city,
12
- address_line_1: address.address1,
13
- address_line_2: address.address2,
14
- postal_code: address.zipcode,
15
- country_code: address.country.iso
16
- },
17
- recipient: {
18
- name: {
19
- given_name: "Alexander",
20
- surname: "Hamilton"
21
- }
22
- }
23
- }
24
- }
25
-
26
- describe "#update_address" do
27
- subject{ order.ship_address }
28
-
29
- it "formats PayPal addresses correctly" do
30
- order.ship_address = original_address
31
-
32
- described_class.new(order).update(params)
33
-
34
- expect(subject.state).to eq address.state
35
- expect(subject.city).to eq address.city
36
- expect(subject.address1).to eq address.address1
37
- expect(subject.address2).to eq address.address2
38
- expect(subject.zipcode).to eq address.zipcode
39
- expect(subject.country).to eq address.country
40
- if SolidusSupport.combined_first_and_last_name_in_address?
41
- expect(subject.name).to eq address.name
42
- else
43
- expect(subject.firstname).to eq address.firstname
44
- expect(subject.lastname).to eq address.lastname
45
- end
46
- expect(subject.phone).to eq original_address.phone
47
- end
48
-
49
- context "when no address exists" do
50
- it "produce a valid address" do
51
- order.ship_address = nil
52
-
53
- described_class.new(order).update(params)
54
-
55
- expect(subject).to be_present
56
- end
57
- end
58
- end
59
-
60
- def name_attributes
61
- if SolidusSupport.combined_first_and_last_name_in_address?
62
- { name: "Alexander Hamilton" }
63
- else
64
- { firstname: "Alexander", lastname: "Hamilton" }
65
- end
66
- end
67
- end
@@ -1,38 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::StateGuesser, type: :model do
4
- let(:country) { create(:country, iso: "IT") }
5
- let!(:state) { create(:state, country: country, name: "Abruzzo") }
6
-
7
- describe "#guess" do
8
- context "with a guessable state error" do
9
- it "correctly guesses the state" do
10
- expect(
11
- described_class.new("Pescara", country).guess
12
- ).to eq state
13
- end
14
-
15
- it "guesses the state using an abbreviation" do
16
- expect(
17
- described_class.new("PE", country).guess
18
- ).to eq state
19
- end
20
- end
21
-
22
- context "with an unsolvable state error" do
23
- it "returns nil" do
24
- expect(
25
- described_class.new("Gondor", country).guess
26
- ).to eq nil
27
- end
28
- end
29
-
30
- context "with an already correct state" do
31
- it "returns the correct state" do
32
- expect(
33
- described_class.new("Abruzzo", country).guess
34
- ).to eq state
35
- end
36
- end
37
- end
38
- end
@@ -1,9 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::Wizard do
4
- describe "#nonce" do
5
- it 'is between 45 and 128 chars long' do
6
- expect(subject.nonce.size).to be_between(45, 128)
7
- end
8
- end
9
- end
@@ -1,36 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::OrdersController, type: :request do
4
- stub_authorization!
5
- let(:order) { create(:order_with_line_items) }
6
-
7
- describe "GET /solidus_paypal_commerce_platform/verify_total" do
8
- context "when the amount is correct" do
9
- it "passes" do
10
- params = {
11
- order_id: order.number,
12
- paypal_total: order.total,
13
- format: :json
14
- }
15
-
16
- get solidus_paypal_commerce_platform.verify_total_path, params: params
17
-
18
- expect(response).to have_http_status(:ok)
19
- end
20
- end
21
-
22
- context "when the amount is incorrect" do
23
- it "fails" do
24
- params = {
25
- order_id: order.number,
26
- paypal_total: order.total - 1,
27
- format: :json
28
- }
29
-
30
- get solidus_paypal_commerce_platform.verify_total_path, params: params
31
-
32
- expect(response).to have_http_status(:bad_request)
33
- end
34
- end
35
- end
36
- end
@@ -1,44 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::ShippingRatesController, type: :request do
4
- stub_authorization!
5
- let(:new_country) { create(:country, iso: "IT", states_required: true) }
6
- let(:new_state) { create(:state, country: new_country) }
7
- let(:new_address) { build(:address, country: new_country, state: new_state) }
8
- let(:order) { Spree::TestingSupport::OrderWalkthrough.up_to(:payment) }
9
- let(:paypal_address) {
10
- {
11
- country_code: new_country.iso,
12
- city: new_address.city,
13
- state: new_state&.abbr,
14
- postal_code: new_address.zipcode
15
- }
16
- }
17
-
18
- describe "GET /simulate_shipping_rates" do
19
- before do
20
- get solidus_paypal_commerce_platform.shipping_rates_path, params: {
21
- order_id: order.number,
22
- address: paypal_address
23
- }
24
- end
25
-
26
- it "returns a paypal_order without the simulated address" do
27
- expect(response.body).not_to include "admin_area_1\":\"#{new_address.state.abbr}\""
28
- expect(response.body).not_to include "admin_area_1\":\"#{order.ship_address.state.abbr}\""
29
- end
30
-
31
- it "does not modify original address" do
32
- expect(order.ship_address).not_to eq new_address
33
- end
34
-
35
- context "with an invalid address" do
36
- let(:new_state) { nil }
37
-
38
- it "returns a list of errors" do
39
- expect(response.status).to be(422) # unprocessable_entity
40
- expect(response.body).to include "State can't be blank"
41
- end
42
- end
43
- end
44
- end
@@ -1,44 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe SolidusPaypalCommercePlatform::WizardController, type: :request do
4
- stub_authorization!
5
-
6
- let(:wizard) { SolidusPaypalCommercePlatform::Wizard.new }
7
-
8
- describe "POST /solidus_paypal_commerce_platform/wizard" do
9
- let(:params) {
10
- {
11
- authCode: "PFc4d2vp4DVfHqcnEHbGAA12C_H29U39NM_vmQrZBPzdLcxA12Br-GzjbliqXGu3AG6Gfwo5G9GTal6REkcKGMREc9fPsR_wv",
12
- sharedId: "awj45zMAy1XonxWfgIhjjGHZGAPStkZFzXp4bfe1QmNWA-9DL6HkpklD0skHig4vVF7zVZD8Uwy5Qop4",
13
- nonce: wizard.nonce,
14
- }
15
- }
16
-
17
- it "creates a new payment method from data received from PayPal" do
18
- expect_any_instance_of(SolidusPaypalCommercePlatform::Client).to receive(:execute) do |_client, request|
19
- case request
20
- when SolidusPaypalCommercePlatform::AccessTokenAuthorizationRequest
21
- OpenStruct.new(result: OpenStruct.new(access_token: "ACCESS-TOKEN"))
22
- when SolidusPaypalCommercePlatform::FetchMerchantCredentialsRequest
23
- expect(request.headers.fetch("Authorization")).to eq("Bearer ACCESS-TOKEN")
24
- OpenStruct.new(result: OpenStruct.new(
25
- client_id: "CLIENT-ID",
26
- client_secret: "CLIENT-SECRET",
27
- ))
28
- else
29
- raise "unexpected request: #{request}"
30
- end
31
- end.twice
32
-
33
- expect {
34
- post solidus_paypal_commerce_platform.wizard_index_path, params: params
35
- }.to change(SolidusPaypalCommercePlatform::PaymentMethod, :count).from(0).to(1)
36
-
37
- payment_method = SolidusPaypalCommercePlatform::PaymentMethod.last
38
-
39
- expect(payment_method.preferred_client_id).to eq("CLIENT-ID")
40
- expect(payment_method.preferred_client_secret).to eq("CLIENT-SECRET")
41
- expect(response).to have_http_status(:created)
42
- end
43
- end
44
- end
data/spec/spec_helper.rb DELETED
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Configure Rails Environment
4
- ENV['RAILS_ENV'] = 'test'
5
-
6
- # Run Coverage report
7
- require 'solidus_dev_support/rspec/coverage'
8
-
9
- # Create the dummy app if it's still missing.
10
- dummy_env = "#{__dir__}/dummy/config/environment.rb"
11
- system 'bin/rake extension:test_app' unless File.exist? dummy_env
12
- require dummy_env
13
-
14
- # Requires factories and other useful helpers defined in spree_core.
15
- require 'solidus_dev_support/rspec/feature_helper'
16
- require 'spree/testing_support/order_walkthrough'
17
-
18
- # Requires supporting ruby files with custom matchers and macros, etc,
19
- # in spec/support/ and its subdirectories.
20
- Dir["#{__dir__}/support/**/*.rb"].sort.each { |f| require f }
21
-
22
- # Requires factories defined in lib/solidus_paypal_commerce_platform/testing_support/factories.rb
23
- SolidusDevSupport::TestingSupport::Factories.load_for(SolidusPaypalCommercePlatform::Engine)
24
-
25
- RSpec.configure do |config|
26
- config.infer_spec_type_from_file_location!
27
- config.use_transactional_fixtures = false
28
-
29
- if Spree.solidus_gem_version < Gem::Version.new('2.11')
30
- config.extend Spree::TestingSupport::AuthorizationHelpers::Request, type: :system
31
- end
32
- end
@@ -1,11 +0,0 @@
1
- require "capybara/cuprite"
2
-
3
- Capybara.javascript_driver = :cuprite
4
-
5
- Capybara.register_driver(:cuprite) do |app|
6
- # Set `export INSPECTOR=true` and add `page.driver.debug(binding)` to open a Chrome tab
7
- # to debug the browser status.
8
- #
9
- # See also: https://github.com/rubycdp/cuprite#debugging
10
- Capybara::Cuprite::Driver.new(app, window_size: [1920, 1080], inspector: ENV["INSPECTOR"])
11
- end
@@ -1,13 +0,0 @@
1
- module PaypalSdkScriptTagHelper
2
- def js_sdk_script_query
3
- URI(page.find('script[src*="sdk/js?"]', visible: false)[:src]).query.split('&')
4
- end
5
-
6
- def js_sdk_script_partner_id
7
- page.find('script[src*="sdk/js?"]', visible: false)['data-partner-attribution-id']
8
- end
9
- end
10
-
11
- RSpec.configure do |config|
12
- config.include PaypalSdkScriptTagHelper, type: :feature
13
- end