solidus_braintree 1.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +78 -0
- data/.gem_release.yml +5 -0
- data/.github/stale.yml +1 -0
- data/.github_changelog_generator +2 -0
- data/.gitignore +21 -10
- data/.rspec +1 -1
- data/.rubocop.yml +86 -0
- data/CHANGELOG.md +186 -18
- data/Gemfile +35 -17
- data/LICENSE +26 -0
- data/README.md +389 -24
- data/Rakefile +6 -16
- data/app/assets/config/solidus_braintree_manifest.js +0 -0
- data/app/assets/javascripts/spree/backend/solidus_braintree/client.js +239 -0
- data/app/assets/javascripts/spree/backend/solidus_braintree/constants.js +89 -0
- data/app/assets/javascripts/spree/backend/solidus_braintree/hosted_form.js +46 -0
- data/app/assets/javascripts/spree/backend/solidus_braintree/promise.js +20 -0
- data/app/assets/javascripts/spree/backend/solidus_braintree.js +96 -0
- data/app/assets/stylesheets/spree/backend/solidus_braintree.scss +28 -0
- data/app/decorators/controllers/solidus_braintree/admin_payments_controller_decorator.rb +11 -0
- data/app/decorators/controllers/solidus_braintree/client_tokens_controller.rb +41 -0
- data/app/decorators/models/solidus_braintree/spree/store_decorator.rb +20 -0
- data/app/decorators/models/solidus_braintree/spree/user_decorator.rb +13 -0
- data/app/helpers/solidus_braintree/braintree_admin_helper.rb +23 -0
- data/app/models/application_record.rb +5 -0
- data/app/models/solidus_braintree/address.rb +64 -0
- data/app/models/solidus_braintree/avs_result.rb +69 -0
- data/app/models/solidus_braintree/configuration.rb +39 -0
- data/app/models/solidus_braintree/customer.rb +8 -0
- data/app/models/solidus_braintree/gateway.rb +437 -0
- data/app/models/solidus_braintree/response.rb +80 -0
- data/app/models/solidus_braintree/source.rb +140 -0
- data/app/models/solidus_braintree/transaction.rb +31 -0
- data/app/models/solidus_braintree/transaction_address.rb +88 -0
- data/app/models/solidus_braintree/transaction_import.rb +98 -0
- data/app/views/spree/api/payments/source_views/_braintree.json.jbuilder +3 -0
- data/bin/console +4 -1
- data/bin/dummy-app +37 -0
- data/bin/rails +5 -5
- data/bin/rails-dummy-app +17 -0
- data/bin/rails-engine +13 -0
- data/bin/rails-sandbox +16 -0
- data/bin/rake +7 -0
- data/bin/rspec +11 -0
- data/bin/sandbox +61 -0
- data/bin/setup +5 -4
- data/config/locales/en.yml +94 -2
- data/config/locales/it.yml +56 -0
- data/config/routes.rb +12 -3
- data/db/migrate/20160830061749_create_solidus_paypal_braintree_sources.rb +16 -0
- data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +13 -0
- data/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb +11 -0
- data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +7 -0
- data/db/migrate/20170203191030_add_credit_card_to_braintree_configuration.rb +6 -0
- data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +38 -0
- data/db/migrate/20170508085402_add_not_null_constraint_to_sources_payment_type.rb +14 -0
- data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
- data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
- data/db/migrate/20211222170950_add_paypal_funding_source_to_solidus_paypal_braintree_sources.rb +5 -0
- data/db/migrate/20220104150301_add_venmo_to_braintree_configuration.rb +5 -0
- data/db/migrate/20230109080950_rename_solidus_paypal_braintree_source_type.rb +31 -0
- data/db/migrate/20230210104310_add_device_data_to_braintree_sources.rb +5 -0
- data/lib/controllers/backend/solidus_braintree/configurations_controller.rb +48 -0
- data/lib/generators/solidus_braintree/install/install_generator.rb +155 -19
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_280x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_320x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_375x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_280x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_320x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_375x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_blue_acceptance_mark.svg +15 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_blue_button_280x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_blue_button_320x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_blue_button_375x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_blue_logo.svg +18 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_white_acceptance_mark.svg +20 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_white_button_280x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_white_button_320x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_white_button_375x48.svg +19 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/images/solidus_braintree/venmo/venmo_white_logo.svg +18 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/paypal_button.js +34 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/ajax.js +13 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/apple_pay_button.js +179 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/checkout.js +113 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/client.js +239 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/constants.js +89 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/frontend.js +15 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/hosted_form.js +48 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/paypal_button.js +178 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/paypal_messaging.js +22 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/promise.js +20 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree/venmo_button.js +86 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/javascripts/spree/frontend/solidus_braintree.js +1 -0
- data/lib/generators/solidus_braintree/install/templates/app/assets/stylesheets/spree/frontend/solidus_braintree.scss +62 -0
- data/lib/generators/solidus_braintree/install/templates/app/controllers/solidus_braintree/checkouts_controller.rb +31 -0
- data/lib/generators/solidus_braintree/install/templates/app/controllers/solidus_braintree/transactions_controller.rb +67 -0
- data/lib/generators/solidus_braintree/install/templates/app/helpers/solidus_braintree/braintree_checkout_helper.rb +60 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/checkouts/existing_payment/_braintree.html.erb +2 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/checkouts/payment/_braintree.html.erb +23 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/payments/_braintree_payment_details.html.erb +9 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_apple_pay_button.html.erb +27 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_braintree_errors.html.erb +16 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_braintree_head_scripts.html.erb +26 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_braintree_hosted_fields.html.erb +40 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_paypal_cart_button.html.erb +38 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_paypal_checkout_button.html.erb +32 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
- data/lib/generators/solidus_braintree/install/templates/app/views/spree/shared/_venmo_button.html.erb +33 -0
- data/lib/generators/solidus_braintree/install/templates/config/initializers/solidus_braintree.rb +6 -0
- data/lib/solidus_braintree/country_mapper.rb +37 -0
- data/lib/solidus_braintree/engine.rb +61 -11
- data/lib/solidus_braintree/extension_configuration.rb +23 -0
- data/lib/solidus_braintree/request_protection.rb +21 -0
- data/lib/solidus_braintree/version.rb +3 -1
- data/lib/solidus_braintree.rb +14 -2
- data/lib/solidus_paypal_braintree.rb +6 -0
- data/lib/views/backend/solidus_braintree/configurations/list.html.erb +63 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_braintree.html.erb +16 -0
- data/lib/views/backend/spree/admin/payments/source_views/_braintree.html.erb +39 -0
- data/lib/views/backend/spree/admin/shared/preference_fields/_preference_select.html.erb +13 -0
- data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_braintree.html.erb +16 -0
- data/lib/views/backend_v2.4/spree/admin/shared/preference_fields/_hash.html.erb +12 -0
- data/solidus_braintree.gemspec +37 -38
- data/spec/controllers/solidus_braintree/checkouts_controller_spec.rb +99 -0
- data/spec/controllers/solidus_braintree/client_tokens_controller_spec.rb +55 -0
- data/spec/controllers/solidus_braintree/configurations_controller_spec.rb +73 -0
- data/spec/controllers/solidus_braintree/transactions_controller_spec.rb +183 -0
- data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
- data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
- data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
- data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
- data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
- data/spec/fixtures/cassettes/braintree/token.yml +63 -0
- data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
- data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
- data/spec/fixtures/cassettes/checkout/update.yml +71 -0
- data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +171 -0
- data/spec/fixtures/cassettes/checkout/valid_venmo_transaction.yml +599 -0
- data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
- data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
- data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
- data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
- data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
- data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
- data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
- data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
- data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
- data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
- data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
- data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
- data/spec/fixtures/cassettes/gateway/customer.yml +79 -0
- data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
- data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
- data/spec/fixtures/cassettes/gateway/void.yml +137 -0
- data/spec/fixtures/cassettes/source/bin.yml +295 -0
- data/spec/fixtures/cassettes/source/card_type.yml +267 -0
- data/spec/fixtures/cassettes/source/last4.yml +267 -0
- data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
- data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
- data/spec/fixtures/views/carts/_cart_footer.html.erb +18 -0
- data/spec/helpers/solidus_braintree/braintree_admin_helper_spec.rb +17 -0
- data/spec/helpers/solidus_braintree/braintree_checkout_helper_spec.rb +70 -0
- data/spec/models/solidus_braintree/address_spec.rb +71 -0
- data/spec/models/solidus_braintree/avs_result_spec.rb +317 -0
- data/spec/models/solidus_braintree/gateway_spec.rb +774 -0
- data/spec/models/solidus_braintree/response_spec.rb +280 -0
- data/spec/models/solidus_braintree/source_spec.rb +555 -0
- data/spec/models/solidus_braintree/transaction_address_spec.rb +235 -0
- data/spec/models/solidus_braintree/transaction_import_spec.rb +302 -0
- data/spec/models/solidus_braintree/transaction_spec.rb +86 -0
- data/spec/models/spree/store_spec.rb +14 -0
- data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
- data/spec/solidus_braintree_helper.rb +7 -0
- data/spec/support/solidus_braintree/capybara.rb +7 -0
- data/spec/support/solidus_braintree/factories.rb +55 -0
- data/spec/support/solidus_braintree/gateway_helpers.rb +29 -0
- data/spec/support/solidus_braintree/order_ready_for_payment.rb +44 -0
- data/spec/support/solidus_braintree/order_walkthrough.rb +87 -0
- data/spec/support/solidus_braintree/vcr.rb +42 -0
- data/spec/support/solidus_braintree/with_prepended_view_fixtures.rb +19 -0
- data/spec/system/backend/configuration_spec.rb +23 -0
- data/spec/system/backend/new_payment_spec.rb +136 -0
- data/spec/system/frontend/braintree_credit_card_checkout_spec.rb +199 -0
- data/spec/system/frontend/paypal_checkout_spec.rb +169 -0
- data/spec/system/frontend/venmo_checkout_spec.rb +193 -0
- metadata +289 -255
- data/.travis.yml +0 -41
- data/LICENSE.txt +0 -21
- data/app/controllers/spree/api/braintree_client_token_controller.rb +0 -13
- data/app/helpers/braintree_view_helpers.rb +0 -20
- data/app/models/concerns/solidus_braintree/add_name_validation_concern.rb +0 -8
- data/app/models/concerns/solidus_braintree/inject_device_data_concern.rb +0 -18
- data/app/models/concerns/solidus_braintree/payment_braintree_nonce_concern.rb +0 -8
- data/app/models/concerns/solidus_braintree/permitted_attributes_concern.rb +0 -11
- data/app/models/concerns/solidus_braintree/skip_require_card_numbers_concern.rb +0 -14
- data/app/models/concerns/solidus_braintree/use_data_field_concern.rb +0 -23
- data/app/models/credit_card_decorator.rb +0 -3
- data/app/models/payment_decorator.rb +0 -2
- data/app/models/permitted_attributes_decorator.rb +0 -1
- data/app/models/solidus/gateway/braintree_gateway.rb +0 -306
- data/app/overrides/spree/checkout/_confirm/braintree_security.html.erb.deface +0 -9
- data/app/views/spree/admin/payments/source_forms/_braintree.html.erb +0 -38
- data/app/views/spree/admin/payments/source_views/_braintree.html.erb +0 -30
- data/app/views/spree/checkout/payment/_braintree.html.erb +0 -55
- data/app/views/spree/checkout/payment/_braintree_initialization.html.erb +0 -12
- data/config/initializers/braintree.rb +0 -1
- data/db/migrate/20150910170527_add_data_to_credit_card.rb +0 -5
- data/db/migrate/20160426221931_add_braintree_device_data_to_order.rb +0 -5
- data/lib/assets/javascripts/spree/backend/braintree/solidus_braintree.js +0 -59
- data/lib/assets/javascripts/spree/frontend/braintree/solidus_braintree.js +0 -144
- data/lib/assets/javascripts/vendor/braintree.js +0 -8
- data/lib/assets/stylesheets/spree/frontend/solidus_braintree.scss +0 -26
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_braintree/request_protection'
|
4
|
+
|
5
|
+
module SolidusBraintree
|
6
|
+
class Source < ::Spree::PaymentSource
|
7
|
+
include RequestProtection
|
8
|
+
|
9
|
+
PAYPAL = "PayPalAccount"
|
10
|
+
APPLE_PAY = "ApplePayCard"
|
11
|
+
VENMO = "VenmoAccount"
|
12
|
+
CREDIT_CARD = "CreditCard"
|
13
|
+
|
14
|
+
enum paypal_funding_source: {
|
15
|
+
applepay: 0, bancontact: 1, blik: 2, boleto: 3, card: 4, credit: 5, eps: 6, giropay: 7, ideal: 8,
|
16
|
+
itau: 9, maxima: 10, mercadopago: 11, mybank: 12, oxxo: 13, p24: 14, paylater: 15, paypal: 16, payu: 17,
|
17
|
+
sepa: 18, sofort: 19, trustly: 20, venmo: 21, verkkopankki: 22, wechatpay: 23, zimpler: 24
|
18
|
+
}, _suffix: :funding
|
19
|
+
|
20
|
+
belongs_to :user, class_name: ::Spree::UserClassHandle.new, optional: true
|
21
|
+
belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
|
22
|
+
has_many :payments, as: :source, class_name: "Spree::Payment", dependent: :destroy
|
23
|
+
|
24
|
+
belongs_to :customer, class_name: "SolidusBraintree::Customer", optional: true
|
25
|
+
|
26
|
+
validates :payment_type, inclusion: [PAYPAL, APPLE_PAY, VENMO, CREDIT_CARD]
|
27
|
+
|
28
|
+
before_validation :clear_device_data_if_blank
|
29
|
+
before_save :clear_paypal_funding_source, unless: :paypal?
|
30
|
+
|
31
|
+
scope(:with_payment_profile, -> { joins(:customer) })
|
32
|
+
scope(:credit_card, -> { where(payment_type: CREDIT_CARD) })
|
33
|
+
|
34
|
+
delegate :bin, :last_4, :card_type, :expiration_month, :expiration_year, :email,
|
35
|
+
:username, :source_description, to: :braintree_payment_method, allow_nil: true
|
36
|
+
|
37
|
+
# Aliases to match Spree::CreditCard's interface
|
38
|
+
alias_method :last_digits, :last_4
|
39
|
+
alias_method :month, :expiration_month
|
40
|
+
alias_method :year, :expiration_year
|
41
|
+
alias_method :cc_type, :card_type
|
42
|
+
|
43
|
+
# we are not currenctly supporting an "imported" flag
|
44
|
+
def imported
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
def actions
|
49
|
+
%w[capture void credit]
|
50
|
+
end
|
51
|
+
|
52
|
+
def can_capture?(payment)
|
53
|
+
payment.pending? || payment.checkout?
|
54
|
+
end
|
55
|
+
|
56
|
+
def can_void?(payment)
|
57
|
+
return false unless payment.response_code
|
58
|
+
|
59
|
+
transaction = protected_request do
|
60
|
+
braintree_client.transaction.find(payment.response_code)
|
61
|
+
end
|
62
|
+
Gateway::VOIDABLE_STATUSES.include?(transaction.status)
|
63
|
+
rescue ActiveMerchant::ConnectionError
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
67
|
+
def can_credit?(payment)
|
68
|
+
payment.completed? && payment.credit_allowed > 0
|
69
|
+
end
|
70
|
+
|
71
|
+
def friendly_payment_type
|
72
|
+
I18n.t(payment_type.underscore, scope: "solidus_braintree.payment_type")
|
73
|
+
end
|
74
|
+
|
75
|
+
def apple_pay?
|
76
|
+
payment_type == APPLE_PAY
|
77
|
+
end
|
78
|
+
|
79
|
+
def paypal?
|
80
|
+
payment_type == PAYPAL
|
81
|
+
end
|
82
|
+
|
83
|
+
def venmo?
|
84
|
+
payment_type == VENMO
|
85
|
+
end
|
86
|
+
|
87
|
+
def reusable?
|
88
|
+
token.present?
|
89
|
+
end
|
90
|
+
|
91
|
+
def credit_card?
|
92
|
+
payment_type == CREDIT_CARD
|
93
|
+
end
|
94
|
+
|
95
|
+
def display_number
|
96
|
+
if paypal?
|
97
|
+
email
|
98
|
+
elsif venmo?
|
99
|
+
username
|
100
|
+
else
|
101
|
+
"XXXX-XXXX-XXXX-#{last_digits.to_s.rjust(4, 'X')}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def display_paypal_funding_source
|
106
|
+
I18n.t(paypal_funding_source,
|
107
|
+
scope: 'solidus_braintree.paypal_funding_sources',
|
108
|
+
default: paypal_funding_source)
|
109
|
+
end
|
110
|
+
|
111
|
+
def display_payment_type
|
112
|
+
"#{I18n.t('solidus_braintree.payment_type.label')}: #{friendly_payment_type}"
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def braintree_payment_method
|
118
|
+
return unless braintree_client
|
119
|
+
|
120
|
+
@braintree_payment_method ||= protected_request do
|
121
|
+
braintree_client.payment_method.find(token)
|
122
|
+
end
|
123
|
+
rescue ActiveMerchant::ConnectionError, ArgumentError => e
|
124
|
+
Rails.logger.warn("#{e}: token unknown or missing for #{inspect}")
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
|
128
|
+
def braintree_client
|
129
|
+
@braintree_client ||= payment_method.try(:braintree)
|
130
|
+
end
|
131
|
+
|
132
|
+
def clear_device_data_if_blank
|
133
|
+
self.device_data = nil if device_data.blank?
|
134
|
+
end
|
135
|
+
|
136
|
+
def clear_paypal_funding_source
|
137
|
+
self.paypal_funding_source = nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
module SolidusBraintree
|
6
|
+
class Transaction
|
7
|
+
include ActiveModel::Model
|
8
|
+
|
9
|
+
attr_accessor :nonce, :payment_method, :payment_type, :paypal_funding_source, :address, :email, :phone
|
10
|
+
|
11
|
+
validates :nonce, presence: true
|
12
|
+
validates :payment_method, presence: true
|
13
|
+
validates :payment_type, presence: true
|
14
|
+
validates :email, presence: true
|
15
|
+
|
16
|
+
validate do
|
17
|
+
unless payment_method.is_a? SolidusBraintree::Gateway
|
18
|
+
errors.add(:payment_method, 'Must be braintree')
|
19
|
+
end
|
20
|
+
if address&.invalid?
|
21
|
+
address.errors.each do |error|
|
22
|
+
errors.add(:address, error.full_message)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def address_attributes=(attributes)
|
28
|
+
self.address = TransactionAddress.new attributes
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
require 'solidus_braintree/country_mapper'
|
5
|
+
|
6
|
+
module SolidusBraintree
|
7
|
+
class TransactionAddress
|
8
|
+
include ActiveModel::Model
|
9
|
+
include ActiveModel::Validations::Callbacks
|
10
|
+
include SolidusBraintree::CountryMapper
|
11
|
+
|
12
|
+
attr_accessor :country_code, :name, :city, :zip, :state_code,
|
13
|
+
:address_line_1, :address_line_2, :first_name, :last_name
|
14
|
+
|
15
|
+
validates :address_line_1, :city, :zip, :country_code, presence: true
|
16
|
+
validates :name, presence: true, unless: ->(address){ address.first_name.present? }
|
17
|
+
|
18
|
+
before_validation do
|
19
|
+
self.country_code = country_code.presence || "us"
|
20
|
+
end
|
21
|
+
|
22
|
+
validates :spree_country, presence: true
|
23
|
+
validates :state_code, :spree_state, presence: true, if: :should_match_state_model?
|
24
|
+
|
25
|
+
def initialize(attributes = {})
|
26
|
+
country_name = attributes.delete(:country_name) || ""
|
27
|
+
if attributes[:country_code].blank?
|
28
|
+
attributes[:country_code] = iso_from_name(country_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
super(attributes)
|
32
|
+
end
|
33
|
+
|
34
|
+
def spree_country
|
35
|
+
country_code && (@country ||= ::Spree::Country.find_by(iso: country_code.upcase))
|
36
|
+
end
|
37
|
+
|
38
|
+
def spree_state
|
39
|
+
spree_country && state_code && ( @state ||= spree_country.states.find_by(
|
40
|
+
::Spree::State.arel_table[:name].matches(state_code).or(
|
41
|
+
::Spree::State.arel_table[:abbr].matches(state_code)
|
42
|
+
)
|
43
|
+
))
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_spree_address
|
47
|
+
address = ::Spree::Address.new(
|
48
|
+
city: city,
|
49
|
+
country: spree_country,
|
50
|
+
address1: address_line_1,
|
51
|
+
address2: address_line_2,
|
52
|
+
zipcode: zip
|
53
|
+
)
|
54
|
+
|
55
|
+
if SolidusSupport.combined_first_and_last_name_in_address?
|
56
|
+
address.name = begin
|
57
|
+
if first_name.nil?
|
58
|
+
name
|
59
|
+
else
|
60
|
+
[first_name, last_name].join(" ")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
::Spree::Deprecation.warn("first_name and last_name are deprecated. Use name instead.", caller)
|
65
|
+
if first_name.nil?
|
66
|
+
first, last = SolidusBraintree::Address.split_name(name)
|
67
|
+
address.firstname = first
|
68
|
+
address.lastname = last || "(left blank)"
|
69
|
+
else
|
70
|
+
address.firstname = first_name
|
71
|
+
address.lastname = last_name || "(left blank)"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if spree_state
|
76
|
+
address.state = spree_state
|
77
|
+
else
|
78
|
+
address.state_name = state_code
|
79
|
+
end
|
80
|
+
address
|
81
|
+
end
|
82
|
+
|
83
|
+
# Check to see if this address should match to a state model in the database
|
84
|
+
def should_match_state_model?
|
85
|
+
spree_country&.states_required?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
|
5
|
+
module SolidusBraintree
|
6
|
+
class TransactionImport
|
7
|
+
class InvalidImportError < StandardError; end
|
8
|
+
|
9
|
+
include ActiveModel::Model
|
10
|
+
|
11
|
+
validate do
|
12
|
+
errors.add("Address", "is invalid") if address&.invalid?
|
13
|
+
|
14
|
+
if transaction.invalid?
|
15
|
+
transaction.errors.each do |error|
|
16
|
+
errors.add(error.attribute, error.message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
errors.none?
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :transaction, :order
|
23
|
+
|
24
|
+
def initialize(order, transaction)
|
25
|
+
@order = order
|
26
|
+
@transaction = transaction
|
27
|
+
end
|
28
|
+
|
29
|
+
def source
|
30
|
+
SolidusBraintree::Source.new(
|
31
|
+
nonce: transaction.nonce,
|
32
|
+
payment_type: transaction.payment_type,
|
33
|
+
payment_method: transaction.payment_method,
|
34
|
+
paypal_funding_source: transaction.paypal_funding_source,
|
35
|
+
user: user
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
delegate :user, to: :order
|
40
|
+
|
41
|
+
def import!(end_state, restart_checkout: false)
|
42
|
+
if valid?
|
43
|
+
order.email = user&.email || transaction.email
|
44
|
+
|
45
|
+
if address
|
46
|
+
order.shipping_address = order.billing_address = address
|
47
|
+
# work around a bug in most solidus versions
|
48
|
+
# about tax zone cachine between address changes
|
49
|
+
order.instance_variable_set(:@tax_zone, nil)
|
50
|
+
end
|
51
|
+
|
52
|
+
payment = order.payments.new(
|
53
|
+
source: source,
|
54
|
+
payment_method: transaction.payment_method,
|
55
|
+
amount: order.total
|
56
|
+
)
|
57
|
+
|
58
|
+
order.save!
|
59
|
+
order.restart_checkout_flow if restart_checkout
|
60
|
+
advance_order(payment, end_state)
|
61
|
+
else
|
62
|
+
raise InvalidImportError,
|
63
|
+
"Validation failed: #{errors.full_messages.join(', ')}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def address
|
68
|
+
transaction.address && transaction.address.to_spree_address.tap do |address|
|
69
|
+
address.phone = transaction.phone
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def state_before_current?(state)
|
74
|
+
steps = order.checkout_steps
|
75
|
+
steps.index(state) < (steps.index(order.state) || 0)
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def advance_order(payment, end_state)
|
81
|
+
return if state_before_current?(end_state)
|
82
|
+
|
83
|
+
until order.state == end_state
|
84
|
+
order.next!
|
85
|
+
update_payment_total(payment) if order.payment?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def update_payment_total(payment)
|
90
|
+
payment_total = order.payments.where(state: %w[checkout pending]).sum(:amount)
|
91
|
+
payment_difference = order.outstanding_balance - payment_total
|
92
|
+
|
93
|
+
return unless payment_difference != 0
|
94
|
+
|
95
|
+
payment.update!(amount: payment.amount + payment_difference)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/bin/console
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
3
5
|
require "bundler/setup"
|
4
6
|
require "solidus_braintree"
|
5
7
|
|
6
8
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
9
|
# with your gem easier. You can also use a different console, if you like.
|
10
|
+
$LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"])
|
8
11
|
|
9
12
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
13
|
# require "pry"
|
11
14
|
# Pry.start
|
12
15
|
|
13
16
|
require "irb"
|
14
|
-
IRB.start
|
17
|
+
IRB.start(__FILE__)
|
data/bin/dummy-app
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
extension_name="solidus_braintree"
|
6
|
+
|
7
|
+
# Stay away from the bundler env of the containing extension.
|
8
|
+
function unbundled {
|
9
|
+
ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- "$@"
|
10
|
+
}
|
11
|
+
|
12
|
+
# "sqlite" is set by the ORB extension instead of "sqlite3",
|
13
|
+
# all other options are already in the format expected by `rails new`.
|
14
|
+
test "$DB" = "sqlite" && export DB="sqlite3"
|
15
|
+
|
16
|
+
rm -rf ./dummy-app
|
17
|
+
rails new dummy-app \
|
18
|
+
--database=${DB:-sqlite3} \
|
19
|
+
--skip-git \
|
20
|
+
--skip-keeps \
|
21
|
+
--skip-rc \
|
22
|
+
--skip-bootsnap \
|
23
|
+
--skip-test
|
24
|
+
|
25
|
+
if [ ! -d "dummy-app" ]; then
|
26
|
+
echo 'dummy-app rails application failed'
|
27
|
+
exit 1
|
28
|
+
fi
|
29
|
+
|
30
|
+
cd ./dummy-app
|
31
|
+
unbundled bundle add solidus --github solidusio/solidus --branch "${BRANCH:-master}" --version '> 0.a'
|
32
|
+
unbundled bundle exec rake db:drop db:create
|
33
|
+
unbundled bundle exec rails generate solidus:install --auto-accept --payment-method=none --no-seed --no-sample "$@"
|
34
|
+
unbundled bundle add $extension_name --path ..
|
35
|
+
unbundled bundle exec rails generate $extension_name:install --migrate --specs=all
|
36
|
+
|
37
|
+
|
data/bin/rails
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
if %w[g generate].include? ARGV.first
|
4
|
+
exec "#{__dir__}/rails-engine", *ARGV
|
5
|
+
else
|
6
|
+
exec "#{__dir__}/rails-sandbox", *ARGV
|
7
|
+
end
|
data/bin/rails-dummy-app
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root = "#{__dir__}/.."
|
4
|
+
app_root = "#{root}/dummy-app"
|
5
|
+
|
6
|
+
unless File.exist? "#{app_root}/bin/rails"
|
7
|
+
warn 'Creating the dummy-app app...'
|
8
|
+
Dir.chdir root do
|
9
|
+
system "#{root}/bin/dummy-app" or begin
|
10
|
+
warn 'Automatic creation of the dummy-app app failed'
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Dir.chdir app_root
|
17
|
+
exec "#{app_root}/bin/rails", *ARGV
|
data/bin/rails-engine
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails gems
|
3
|
+
# installed from the root of your application.
|
4
|
+
|
5
|
+
ENGINE_ROOT = File.expand_path('..', __dir__)
|
6
|
+
ENGINE_PATH = File.expand_path('../lib/solidus_braintree/engine', __dir__)
|
7
|
+
|
8
|
+
# Set up gems listed in the Gemfile.
|
9
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
10
|
+
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
11
|
+
|
12
|
+
require 'rails/all'
|
13
|
+
require 'rails/engine/commands'
|
data/bin/rails-sandbox
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
app_root = 'sandbox'
|
4
|
+
|
5
|
+
unless File.exist? "#{app_root}/bin/rails"
|
6
|
+
warn 'Creating the sandbox app...'
|
7
|
+
Dir.chdir "#{__dir__}/.." do
|
8
|
+
system "#{__dir__}/sandbox" or begin
|
9
|
+
warn 'Automatic creation of the sandbox app failed'
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Dir.chdir app_root
|
16
|
+
exec 'bin/rails', *ARGV
|
data/bin/rake
ADDED
data/bin/rspec
ADDED
data/bin/sandbox
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
case "$DB" in
|
6
|
+
postgres|postgresql)
|
7
|
+
RAILSDB="postgresql"
|
8
|
+
;;
|
9
|
+
mysql)
|
10
|
+
RAILSDB="mysql"
|
11
|
+
;;
|
12
|
+
sqlite|'')
|
13
|
+
RAILSDB="sqlite3"
|
14
|
+
;;
|
15
|
+
*)
|
16
|
+
echo "Invalid DB specified: $DB"
|
17
|
+
exit 1
|
18
|
+
;;
|
19
|
+
esac
|
20
|
+
|
21
|
+
if [ ! -z $SOLIDUS_BRANCH ]
|
22
|
+
then
|
23
|
+
BRANCH=$SOLIDUS_BRANCH
|
24
|
+
else
|
25
|
+
BRANCH="master"
|
26
|
+
fi
|
27
|
+
|
28
|
+
extension_name="solidus_braintree"
|
29
|
+
|
30
|
+
# Stay away from the bundler env of the containing extension.
|
31
|
+
function unbundled {
|
32
|
+
ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- "$@"
|
33
|
+
}
|
34
|
+
|
35
|
+
rm -rf ./sandbox
|
36
|
+
rails new sandbox \
|
37
|
+
--database="$RAILSDB" \
|
38
|
+
--skip-git \
|
39
|
+
--skip-keeps \
|
40
|
+
--skip-rc \
|
41
|
+
--skip-bootsnap \
|
42
|
+
--skip-test
|
43
|
+
|
44
|
+
if [ ! -d "sandbox" ]; then
|
45
|
+
echo 'sandbox rails application failed'
|
46
|
+
exit 1
|
47
|
+
fi
|
48
|
+
|
49
|
+
cd ./sandbox
|
50
|
+
unbundled bundle add solidus --github solidusio/solidus --branch "${BRANCH:-master}" --version '> 0.a'
|
51
|
+
unbundled bundle exec rake db:drop db:create
|
52
|
+
unbundled bundle exec rails generate solidus:install --payment-method=none --auto-accept "$@"
|
53
|
+
unbundled bundle add ${extension_name} --path '../'
|
54
|
+
unbundled bundle exec rails generate ${extension_name}:install --frontend=starter --migrate=true
|
55
|
+
|
56
|
+
echo
|
57
|
+
echo "🚀 Sandbox app successfully created for ${extension_name}!"
|
58
|
+
echo "🚀 Using $RAILSDB and Solidus $BRANCH"
|
59
|
+
echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
|
60
|
+
echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
|
61
|
+
echo "🚀 This app is intended for test purposes."
|
data/bin/setup
CHANGED
data/config/locales/en.yml
CHANGED
@@ -1,4 +1,96 @@
|
|
1
1
|
en:
|
2
|
+
activerecord:
|
3
|
+
models:
|
4
|
+
solidus_braintree/gateway: Braintree
|
5
|
+
attributes:
|
6
|
+
solidus_braintree/configuration:
|
7
|
+
paypal: PayPal
|
8
|
+
apple_pay: Apple Pay
|
9
|
+
credit_card: Credit Card
|
10
|
+
three_d_secure: 3D Secure
|
11
|
+
venmo: Venmo
|
12
|
+
spree:
|
13
|
+
admin:
|
14
|
+
tab:
|
15
|
+
braintree: Braintree
|
16
|
+
client_sdk_enabled: Client SDK enabled?
|
17
|
+
environment: Environment
|
18
|
+
merchant_currency_map: Merchant currency map
|
19
|
+
merchant_id: Merchant ID
|
20
|
+
paypal_payee_email_map: Paypal payee email map
|
21
|
+
private_key: Private key
|
22
|
+
public_key: Public key
|
23
|
+
token_generation_enabled: Token generation enabled?
|
24
|
+
paypal_flow: PayPal flow
|
25
|
+
paypal_funding: 'PayPal Funding: %{funding}'
|
26
|
+
paypal_funding_source: 'PayPal Funding Source'
|
2
27
|
solidus_braintree:
|
3
|
-
|
4
|
-
|
28
|
+
braintree: Braintree
|
29
|
+
nonce: Nonce
|
30
|
+
token: Token
|
31
|
+
errors:
|
32
|
+
default_error: "Something bad happened!"
|
33
|
+
paypal_popup_closed: "PayPal popup has been closed before payment completion."
|
34
|
+
empty_fields: "All fields are empty! Please fill out the form."
|
35
|
+
invalid_fields: "Some payment input fields are invalid."
|
36
|
+
invalid_card: "Credit card data is incorrect."
|
37
|
+
network_error: "Network error occurred."
|
38
|
+
duplicate_iframe: "Duplicate Braintree iframe."
|
39
|
+
fail_on_duplicate: "This payment method already exists in your vault."
|
40
|
+
cvv_verification_failed: "CVV did not pass verification."
|
41
|
+
threeds:
|
42
|
+
authentication_failed: "3D Secure authentication failed. Please try again using a different payment method."
|
43
|
+
venmo:
|
44
|
+
canceled: "App is not available or you aborted the payment flow."
|
45
|
+
desktop_canceled: "Venmo authorization was canceled by closing the Venmo Desktop modal."
|
46
|
+
app_canceled: "Payment flow was canceled."
|
47
|
+
payment_type:
|
48
|
+
label: Payment Type
|
49
|
+
apple_pay_card: Apple Pay
|
50
|
+
credit_card: Credit Card
|
51
|
+
pay_pal_account: PayPal
|
52
|
+
venmo_account: Venmo
|
53
|
+
configurations:
|
54
|
+
title: Braintree Configurations
|
55
|
+
tab: Braintree
|
56
|
+
update_success: Successfully updated Braintree configurations.
|
57
|
+
update_error: An error occurred while updating Braintree configurations.
|
58
|
+
gateway_rejection_reasons:
|
59
|
+
avs: AVS check failed.
|
60
|
+
avs_and_cvv: AVS and CVV check failed.
|
61
|
+
cvv: CVV check failed.
|
62
|
+
duplicate: Duplicate transaction.
|
63
|
+
fraud: Credit card was rejected because of potential fraud.
|
64
|
+
three_d_secure: 3D Secure check failed.
|
65
|
+
transaction_statuses:
|
66
|
+
authorization_expired: Payment authorization has expired.
|
67
|
+
failed: An error occurred when sending the transaction to the processor.
|
68
|
+
gateway_rejected: Gateway rejected this transaction.
|
69
|
+
processor_declined: Processor declined this transaction.
|
70
|
+
settlement_declined: Settlement was declined by processor.
|
71
|
+
paypal_funding_sources:
|
72
|
+
applepay: 'Apple Pay'
|
73
|
+
bancontact: 'Bancontact'
|
74
|
+
blik: 'BLIK'
|
75
|
+
boleto: 'Boleto'
|
76
|
+
card: 'Credit or debit card'
|
77
|
+
credit: 'PayPal Credit'
|
78
|
+
eps: 'eps'
|
79
|
+
giropay: 'giropay'
|
80
|
+
ideal: 'iDEAL'
|
81
|
+
itau: 'Itau'
|
82
|
+
maxima: 'Maxima'
|
83
|
+
mercadopago: 'Mercado Pago'
|
84
|
+
mybank: 'MyBank'
|
85
|
+
oxxo: 'Oxxo'
|
86
|
+
p24: 'Przelewy24'
|
87
|
+
paylater: 'Pay Later'
|
88
|
+
paypal: 'PayPal'
|
89
|
+
payu: 'PayU'
|
90
|
+
sepa: 'SEPA-Lastschrift'
|
91
|
+
sofort: 'Sofort'
|
92
|
+
trustly: 'Trustly'
|
93
|
+
venmo: 'Venmo'
|
94
|
+
verkkopankki: 'Verkkopankki'
|
95
|
+
wechatpay: 'WeChat Pay'
|
96
|
+
zimpler: 'Zimpler'
|