solidus_paypal_braintree 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +0 -18
  3. data/CHANGELOG.md +9 -0
  4. data/README.md +9 -0
  5. data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +2 -36
  6. data/lib/solidus_paypal_braintree/engine.rb +0 -56
  7. data/lib/solidus_paypal_braintree/version.rb +1 -1
  8. data/lib/solidus_paypal_braintree.rb +1 -6
  9. data/solidus_paypal_braintree.gemspec +7 -5
  10. data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +5 -5
  11. data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +2 -2
  12. data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +2 -2
  13. data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +5 -5
  14. data/spec/features/backend/configuration_spec.rb +3 -3
  15. data/spec/features/backend/new_payment_spec.rb +1 -1
  16. data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +3 -3
  17. data/spec/fixtures/views/spree/orders/edit.html.erb +1 -1
  18. data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +1 -1
  19. data/spec/helpers/solidus_paypal_braintree/braintree_checkout_helper_spec.rb +5 -5
  20. data/spec/models/solidus_paypal_braintree/address_spec.rb +1 -1
  21. data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +1 -1
  22. data/spec/models/solidus_paypal_braintree/gateway_spec.rb +11 -11
  23. data/spec/models/solidus_paypal_braintree/response_spec.rb +1 -1
  24. data/spec/models/solidus_paypal_braintree/source_spec.rb +2 -2
  25. data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +1 -1
  26. data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +12 -12
  27. data/spec/models/solidus_paypal_braintree/transaction_spec.rb +2 -2
  28. data/spec/requests/spree/api/orders_controller_spec.rb +2 -2
  29. data/spec/spec_helper.rb +2 -2
  30. data/spec/support/gateway_helpers.rb +3 -3
  31. metadata +9 -174
  32. data/app/assets/config/solidus_paypal_braintree_manifest.js +0 -1
  33. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_blue_button_280x48.svg +0 -19
  34. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_blue_button_320x48.svg +0 -19
  35. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_blue_button_375x48.svg +0 -19
  36. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_white_button_280x48.svg +0 -19
  37. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_white_button_320x48.svg +0 -19
  38. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_active_white_button_375x48.svg +0 -19
  39. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_blue_acceptance_mark.svg +0 -15
  40. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_blue_button_280x48.svg +0 -19
  41. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_blue_button_320x48.svg +0 -19
  42. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_blue_button_375x48.svg +0 -19
  43. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_blue_logo.svg +0 -18
  44. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_white_acceptance_mark.svg +0 -20
  45. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_white_button_280x48.svg +0 -19
  46. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_white_button_320x48.svg +0 -19
  47. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_white_button_375x48.svg +0 -19
  48. data/app/assets/images/solidus_paypal_braintree/venmo/venmo_white_logo.svg +0 -18
  49. data/app/assets/javascripts/solidus_paypal_braintree/apple_pay_button.js +0 -179
  50. data/app/assets/javascripts/solidus_paypal_braintree/checkout.js +0 -108
  51. data/app/assets/javascripts/solidus_paypal_braintree/client.js +0 -239
  52. data/app/assets/javascripts/solidus_paypal_braintree/constants.js +0 -89
  53. data/app/assets/javascripts/solidus_paypal_braintree/frontend.js +0 -14
  54. data/app/assets/javascripts/solidus_paypal_braintree/hosted_form.js +0 -46
  55. data/app/assets/javascripts/solidus_paypal_braintree/paypal_button.js +0 -178
  56. data/app/assets/javascripts/solidus_paypal_braintree/paypal_messaging.js +0 -22
  57. data/app/assets/javascripts/solidus_paypal_braintree/promise.js +0 -20
  58. data/app/assets/javascripts/solidus_paypal_braintree/venmo_button.js +0 -86
  59. data/app/assets/javascripts/spree/backend/solidus_paypal_braintree.js +0 -96
  60. data/app/assets/javascripts/spree/frontend/paypal_button.js +0 -34
  61. data/app/assets/javascripts/spree/frontend/solidus_paypal_braintree.js +0 -1
  62. data/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.css +0 -4
  63. data/app/assets/stylesheets/spree/backend/solidus_paypal_braintree.scss +0 -28
  64. data/app/assets/stylesheets/spree/frontend/solidus_paypal_braintree.css +0 -51
  65. data/app/decorators/controllers/solidus_paypal_braintree/admin_payments_controller_decorator.rb +0 -11
  66. data/app/decorators/controllers/solidus_paypal_braintree/checkout_controller_decorator.rb +0 -11
  67. data/app/decorators/controllers/solidus_paypal_braintree/client_tokens_controller.rb +0 -41
  68. data/app/decorators/controllers/solidus_paypal_braintree/orders_controller_decorator.rb +0 -11
  69. data/app/decorators/models/solidus_paypal_braintree/spree/store_decorator.rb +0 -20
  70. data/app/decorators/models/solidus_paypal_braintree/spree/user_decorator.rb +0 -13
  71. data/app/helpers/solidus_paypal_braintree/braintree_admin_helper.rb +0 -23
  72. data/app/helpers/solidus_paypal_braintree/braintree_checkout_helper.rb +0 -60
  73. data/app/models/application_record.rb +0 -5
  74. data/app/models/solidus_paypal_braintree/address.rb +0 -64
  75. data/app/models/solidus_paypal_braintree/avs_result.rb +0 -69
  76. data/app/models/solidus_paypal_braintree/configuration.rb +0 -39
  77. data/app/models/solidus_paypal_braintree/customer.rb +0 -8
  78. data/app/models/solidus_paypal_braintree/gateway.rb +0 -433
  79. data/app/models/solidus_paypal_braintree/response.rb +0 -80
  80. data/app/models/solidus_paypal_braintree/source.rb +0 -135
  81. data/app/models/solidus_paypal_braintree/transaction.rb +0 -31
  82. data/app/models/solidus_paypal_braintree/transaction_address.rb +0 -88
  83. data/app/models/solidus_paypal_braintree/transaction_import.rb +0 -98
  84. data/app/overrides/spree/payments/payment/add_paypal_funding_source_to_payment.rb +0 -9
  85. data/app/views/spree/api/payments/source_views/_paypal_braintree.json.jbuilder +0 -3
  86. data/app/views/spree/checkout/existing_payment/_paypal_braintree.html.erb +0 -10
  87. data/app/views/spree/shared/_apple_pay_button.html.erb +0 -27
  88. data/app/views/spree/shared/_braintree_errors.html.erb +0 -16
  89. data/app/views/spree/shared/_braintree_hosted_fields.html.erb +0 -43
  90. data/app/views/spree/shared/_paypal_braintree_head_scripts.html.erb +0 -26
  91. data/app/views/spree/shared/_paypal_cart_button.html.erb +0 -38
  92. data/app/views/spree/shared/_paypal_messaging.html.erb +0 -13
  93. data/app/views/spree/shared/_venmo_button.html.erb +0 -33
  94. data/config/locales/en.yml +0 -96
  95. data/config/locales/it.yml +0 -56
  96. data/config/routes.rb +0 -14
  97. data/db/migrate/20160830061749_create_solidus_paypal_braintree_sources.rb +0 -16
  98. data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +0 -13
  99. data/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb +0 -11
  100. data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +0 -7
  101. data/db/migrate/20170203191030_add_credit_card_to_braintree_configuration.rb +0 -6
  102. data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +0 -32
  103. data/db/migrate/20170508085402_add_not_null_constraint_to_sources_payment_type.rb +0 -11
  104. data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +0 -5
  105. data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +0 -5
  106. data/db/migrate/20211222170950_add_paypal_funding_source_to_solidus_paypal_braintree_sources.rb +0 -5
  107. data/db/migrate/20220104150301_add_venmo_to_braintree_configuration.rb +0 -5
  108. data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +0 -48
  109. data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +0 -31
  110. data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +0 -67
  111. data/lib/generators/solidus_paypal_braintree/install/templates/initializer.rb +0 -6
  112. data/lib/solidus_paypal_braintree/country_mapper.rb +0 -37
  113. data/lib/solidus_paypal_braintree/extension_configuration.rb +0 -23
  114. data/lib/solidus_paypal_braintree/request_protection.rb +0 -21
  115. data/lib/solidus_paypal_braintree/testing_support/factories.rb +0 -53
  116. data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +0 -63
  117. data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +0 -16
  118. data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +0 -39
  119. data/lib/views/backend/spree/admin/shared/preference_fields/_preference_select.html.erb +0 -13
  120. data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +0 -16
  121. data/lib/views/backend_v2.4/spree/admin/shared/preference_fields/_hash.html.erb +0 -12
  122. data/lib/views/frontend/solidus_paypal_braintree/payments/_payment.html.erb +0 -12
  123. data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +0 -23
  124. data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +0 -32
@@ -1,433 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'braintree'
4
- require 'solidus_paypal_braintree/request_protection'
5
-
6
- module SolidusPaypalBraintree
7
- class Gateway < ::Spree::PaymentMethod
8
- include RequestProtection
9
-
10
- class TokenGenerationDisabledError < StandardError; end
11
-
12
- # Error message from Braintree that gets returned by a non voidable transaction
13
- NON_VOIDABLE_STATUS_ERROR_REGEXP = /can only be voided if status is authorized/.freeze
14
-
15
- TOKEN_GENERATION_DISABLED_MESSAGE = 'Token generation is disabled. ' \
16
- 'To re-enable set the `token_generation_enabled` preference on the ' \
17
- 'gateway to `true`.'
18
-
19
- ALLOWED_BRAINTREE_OPTIONS = [
20
- :device_data,
21
- :device_session_id,
22
- :merchant_account_id,
23
- :order_id
24
- ].freeze
25
-
26
- VOIDABLE_STATUSES = [
27
- Braintree::Transaction::Status::SubmittedForSettlement,
28
- Braintree::Transaction::Status::SettlementPending,
29
- Braintree::Transaction::Status::Authorized
30
- ].freeze
31
-
32
- # This is useful in feature tests to avoid rate limited requests from
33
- # Braintree
34
- preference(:client_sdk_enabled, :boolean, default: true)
35
-
36
- preference(:token_generation_enabled, :boolean, default: true)
37
-
38
- # Preferences for configuration of Braintree credentials
39
- preference(:environment, :string, default: 'sandbox')
40
- preference(:merchant_id, :string, default: nil)
41
- preference(:public_key, :string, default: nil)
42
- preference(:private_key, :string, default: nil)
43
- preference(:http_open_timeout, :integer, default: 60)
44
- preference(:http_read_timeout, :integer, default: 60)
45
- preference(:merchant_currency_map, :hash, default: {})
46
- preference(:paypal_payee_email_map, :hash, default: {})
47
-
48
- # Which checkout flow to use (vault/checkout)
49
- preference(:paypal_flow, :string, default: 'vault')
50
-
51
- # A hash that gets passed to the `style` key when initializing the credit card fields.
52
- # See https://developers.braintreepayments.com/guides/hosted-fields/styling/javascript/v3
53
- preference(:credit_card_fields_style, :hash, default: {})
54
-
55
- # A hash that gets its keys passed to the associated braintree field placeholder tag.
56
- # Example: { number: "Enter card number", cvv: "Enter CVV", expirationDate: "mm/yy" }
57
- preference(:placeholder_text, :hash, default: {})
58
-
59
- # Wether to use the JS device data collector
60
- preference(:use_data_collector, :boolean, default: true)
61
-
62
- # Useful for testing purposes, as PayPal will show funding sources based on the buyer's country;
63
- # usually retrieved by their ip geolocation. I.e. Venmo will show for US buyers, but not European.
64
- preference(:force_buyer_country, :string)
65
-
66
- preference(:enable_venmo_funding, :boolean, default: false)
67
-
68
- # When on mobile, paying with Venmo, the user may be returned to the same store tab
69
- # depending on if their browser supports it, otherwise a new tab will be created
70
- # However, returning to a new tab may break the payment checkout flow for some stores, for example,
71
- # if they are single-page applications (SPA). Set this to false if this is the case
72
- preference(:venmo_new_tab_support, :boolean, default: true)
73
-
74
- def partial_name
75
- "paypal_braintree"
76
- end
77
- alias_method :method_type, :partial_name
78
-
79
- def payment_source_class
80
- Source
81
- end
82
-
83
- def braintree
84
- @braintree ||= Braintree::Gateway.new(gateway_options)
85
- end
86
-
87
- def gateway_options
88
- {
89
- environment: preferred_environment.to_sym,
90
- merchant_id: preferred_merchant_id,
91
- public_key: preferred_public_key,
92
- private_key: preferred_private_key,
93
- http_open_timeout: preferred_http_open_timeout,
94
- http_read_timeout: preferred_http_read_timeout,
95
- logger: logger
96
- }
97
- end
98
-
99
- # Create a payment and submit it for settlement all at once.
100
- #
101
- # @api public
102
- # @param money_cents [Number, String] amount to authorize
103
- # @param source [Source] payment source
104
- # @params gateway_options [Hash]
105
- # extra options to send along. e.g.: device data for fraud prevention
106
- # @return [Response]
107
- def purchase(money_cents, source, gateway_options)
108
- protected_request do
109
- result = braintree.transaction.sale(
110
- amount: dollars(money_cents),
111
- **transaction_options(source, gateway_options, submit_for_settlement: true)
112
- )
113
-
114
- Response.build(result)
115
- end
116
- end
117
-
118
- # Authorize a payment to be captured later.
119
- #
120
- # @api public
121
- # @param money_cents [Number, String] amount to authorize
122
- # @param source [Source] payment source
123
- # @params gateway_options [Hash]
124
- # extra options to send along. e.g.: device data for fraud prevention
125
- # @return [Response]
126
- def authorize(money_cents, source, gateway_options)
127
- protected_request do
128
- result = braintree.transaction.sale(
129
- amount: dollars(money_cents),
130
- **transaction_options(source, gateway_options)
131
- )
132
-
133
- Response.build(result)
134
- end
135
- end
136
-
137
- # Collect funds from an authorized payment.
138
- #
139
- # @api public
140
- # @param money_cents [Number, String]
141
- # amount to capture (partial settlements are supported by the gateway)
142
- # @param response_code [String] the transaction id of the payment to capture
143
- # @return [Response]
144
- def capture(money_cents, response_code, _gateway_options)
145
- protected_request do
146
- result = braintree.transaction.submit_for_settlement(
147
- response_code,
148
- dollars(money_cents)
149
- )
150
- Response.build(result)
151
- end
152
- end
153
-
154
- # Used to refeund a customer for an already settled transaction.
155
- #
156
- # @api public
157
- # @param money_cents [Number, String] amount to refund
158
- # @param response_code [String] the transaction id of the payment to refund
159
- # @return [Response]
160
- def credit(money_cents, _source, response_code, _gateway_options)
161
- protected_request do
162
- result = braintree.transaction.refund(
163
- response_code,
164
- dollars(money_cents)
165
- )
166
- Response.build(result)
167
- end
168
- end
169
-
170
- # Used to cancel a transaction before it is settled.
171
- #
172
- # @api public
173
- # @param response_code [String] the transaction id of the payment to void
174
- # @return [Response]
175
- def void(response_code, _source, _gateway_options)
176
- protected_request do
177
- result = braintree.transaction.void(response_code)
178
- Response.build(result)
179
- end
180
- end
181
-
182
- # Will either refund or void the payment depending on its state.
183
- #
184
- # If the transaction has not yet been settled, we can void the transaction.
185
- # Otherwise, we need to issue a refund.
186
- #
187
- # @api public
188
- # @param response_code [String] the transaction id of the payment to void
189
- # @return [Response]
190
- def cancel(response_code)
191
- transaction = protected_request do
192
- braintree.transaction.find(response_code)
193
- end
194
- if VOIDABLE_STATUSES.include?(transaction.status)
195
- void(response_code, nil, {})
196
- else
197
- credit(cents(transaction.amount), nil, response_code, {})
198
- end
199
- end
200
-
201
- # Will void the payment depending on its state or return false
202
- #
203
- # Used by Solidus >= 2.4 instead of +cancel+
204
- #
205
- # If the transaction has not yet been settled, we can void the transaction.
206
- # Otherwise, we return false so Solidus creates a refund instead.
207
- #
208
- # @api public
209
- # @param payment [Spree::Payment] the payment to void
210
- # @return [Response|FalseClass]
211
- def try_void(payment)
212
- transaction = braintree.transaction.find(payment.response_code)
213
- if transaction.status.in? SolidusPaypalBraintree::Gateway::VOIDABLE_STATUSES
214
- # Sometimes Braintree returns a voidable status although it is not voidable anymore.
215
- # When we try to void that transaction we receive an error and need to return false
216
- # so Solidus can create a refund instead.
217
- begin
218
- void(payment.response_code, nil, {})
219
- rescue ActiveMerchant::ConnectionError => e
220
- e.message.match(NON_VOIDABLE_STATUS_ERROR_REGEXP) ? false : raise(e)
221
- end
222
- else
223
- false
224
- end
225
- end
226
-
227
- # Creates a new customer profile in Braintree
228
- #
229
- # @api public
230
- # @param payment [Spree::Payment]
231
- # @return [SolidusPaypalBraintree::Customer]
232
- def create_profile(payment)
233
- source = payment.source
234
-
235
- return if source.token.present? || source.customer.present? || source.nonce.nil?
236
-
237
- result = braintree.customer.create(customer_profile_params(payment))
238
- fail ::Spree::Core::GatewayError, result.message unless result.success?
239
-
240
- customer = result.customer
241
-
242
- source.create_customer!(braintree_customer_id: customer.id).tap do
243
- if customer.payment_methods.any?
244
- source.token = customer.payment_methods.last.token
245
- end
246
-
247
- source.save!
248
- end
249
- end
250
-
251
- # @raise [TokenGenerationDisabledError]
252
- # If `preferred_token_generation_enabled` is false
253
- #
254
- # @return [String]
255
- # The token that should be used along with the Braintree js-client sdk.
256
- #
257
- # @example
258
- # <script>
259
- # var token = #{Spree::Braintree::Gateway.first!.generate_token}
260
- #
261
- # braintree.client.create(
262
- # {
263
- # authorization: token
264
- # },
265
- # function(clientError, clientInstance) {
266
- # ...
267
- # }
268
- # );
269
- # </script>
270
- def generate_token
271
- unless preferred_token_generation_enabled
272
- raise TokenGenerationDisabledError, TOKEN_GENERATION_DISABLED_MESSAGE
273
- end
274
-
275
- braintree.client_token.generate
276
- end
277
-
278
- def payment_profiles_supported?
279
- true
280
- end
281
-
282
- def sources_by_order(order)
283
- source_ids = order.payments.where(payment_method_id: id).pluck(:source_id).uniq
284
- payment_source_class.where(id: source_ids).with_payment_profile
285
- end
286
-
287
- def reusable_sources(order)
288
- if order.completed?
289
- sources_by_order(order)
290
- elsif order.user_id
291
- payment_source_class.where(
292
- payment_method_id: id,
293
- user_id: order.user_id
294
- ).with_payment_profile
295
- else
296
- []
297
- end
298
- end
299
-
300
- private
301
-
302
- # Whether to store this payment method in the PayPal Vault. This only works when the checkout
303
- # flow is "vault", so make sure to call +super+ if you override it.
304
- def store_in_vault
305
- preferred_paypal_flow == 'vault'
306
- end
307
-
308
- def logger
309
- Braintree::Configuration.logger.clone.tap do |logger|
310
- logger.level = Rails.logger.level
311
- end
312
- end
313
-
314
- def dollars(cents)
315
- Money.new(cents).dollars
316
- end
317
-
318
- def cents(dollars)
319
- dollars.to_money.cents
320
- end
321
-
322
- def to_hash(preference_string)
323
- JSON.parse(preference_string.gsub("=>", ":"))
324
- end
325
-
326
- def convert_preference_value(value, type, preference_encryptor = nil)
327
- if type == :hash && value.is_a?(String)
328
- value = to_hash(value)
329
- end
330
- if method(__method__).super_method.arity == 3
331
- super
332
- else
333
- super(value, type)
334
- end
335
- end
336
-
337
- def transaction_options(source, options, submit_for_settlement: false)
338
- params = options.select do |key, _|
339
- ALLOWED_BRAINTREE_OPTIONS.include?(key)
340
- end
341
-
342
- params[:channel] = "Solidus"
343
- params[:options] = { store_in_vault_on_success: store_in_vault }
344
-
345
- if submit_for_settlement
346
- params[:options][:submit_for_settlement] = true
347
- end
348
-
349
- if paypal_email = paypal_payee_email_for(source, options)
350
- params[:options][:paypal] = { payee_email: paypal_email }
351
- end
352
-
353
- if source.venmo? && venmo_business_profile_id
354
- params[:options][:venmo] = { profile_id: venmo_business_profile_id }
355
- end
356
-
357
- if merchant_account_id = merchant_account_for(source, options)
358
- params[:merchant_account_id] = merchant_account_id
359
- end
360
-
361
- if source.token
362
- params[:payment_method_token] = source.token
363
- else
364
- params[:payment_method_nonce] = source.nonce
365
- end
366
-
367
- if source.paypal?
368
- params[:shipping] = braintree_shipping_address(options)
369
- end
370
-
371
- if source.credit_card?
372
- params[:billing] = braintree_billing_address(options)
373
- end
374
-
375
- if source.customer.present?
376
- params[:customer_id] = source.customer.braintree_customer_id
377
- end
378
-
379
- params
380
- end
381
-
382
- def braintree_shipping_address(options)
383
- braintree_address_attributes(options[:shipping_address])
384
- end
385
-
386
- def braintree_billing_address(options)
387
- braintree_address_attributes(options[:billing_address])
388
- end
389
-
390
- def braintree_address_attributes(address)
391
- first, last = address[:name].split(" ", 2)
392
- {
393
- first_name: first,
394
- last_name: last,
395
- street_address: [address[:address1], address[:address2]].compact.join(" "),
396
- locality: address[:city],
397
- postal_code: address[:zip],
398
- region: address[:state],
399
- country_code_alpha2: address[:country]
400
- }
401
- end
402
-
403
- def merchant_account_for(_source, options)
404
- return unless options[:currency]
405
-
406
- preferred_merchant_currency_map[options[:currency]]
407
- end
408
-
409
- def paypal_payee_email_for(source, options)
410
- return unless source.paypal?
411
-
412
- preferred_paypal_payee_email_map[options[:currency]]
413
- end
414
-
415
- def customer_profile_params(payment)
416
- params = {}
417
-
418
- params[:email] = payment&.order&.email
419
-
420
- if store_in_vault && payment.source.try(:nonce)
421
- params[:payment_method_nonce] = payment.source.nonce
422
- end
423
-
424
- params
425
- end
426
-
427
- # override with the Venmo business profile that you want to use for transactions,
428
- # or leave it to be nil if want Braintree to use your default account
429
- def venmo_business_profile_id
430
- nil
431
- end
432
- end
433
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_merchant/billing/response'
4
- require_relative 'avs_result'
5
-
6
- # Response object that all actions on the gateway should return
7
- module SolidusPaypalBraintree
8
- class Response < ActiveMerchant::Billing::Response
9
- # def initialize(success, message, params = {}, options = {})
10
-
11
- class << self
12
- private :new
13
-
14
- # @param result [Braintree::SuccessfulResult, Braintree::ErrorResult]
15
- def build(result)
16
- result.success? ? build_success(result) : build_failure(result)
17
- end
18
-
19
- private
20
-
21
- def build_success(result)
22
- transaction = result.transaction
23
- new(true, transaction.status, {}, response_options(transaction))
24
- end
25
-
26
- def build_failure(result)
27
- transaction = result.transaction
28
- options = response_options(transaction).update(
29
- # For error responses we want to have the CVV code
30
- cvv_result: transaction&.cvv_response_code
31
- )
32
- new(false, error_message(result), result.params, options)
33
- end
34
-
35
- def response_options(transaction)
36
- # Some error responses do not have a transaction
37
- return {} if transaction.nil?
38
-
39
- {
40
- authorization: transaction.id,
41
- avs_result: SolidusPaypalBraintree::AVSResult.build(transaction),
42
- # As we do not provide the CVV while submitting the transaction (for PCI compliance reasons),
43
- # we need to ignore the only response we get back (I = not provided).
44
- # Otherwise Solidus thinks this payment is risky.
45
- cvv_result: nil
46
- }
47
- end
48
-
49
- def error_message(result)
50
- if result.errors.any?
51
- result.errors.map { |e| "#{e.message} (#{e.code})" }.join(" ")
52
- else
53
- transaction_error_message(result.transaction)
54
- end
55
- end
56
-
57
- # Human readable error message for transaction responses
58
- def transaction_error_message(transaction)
59
- case transaction.status
60
- when 'gateway_rejected'
61
- I18n.t(transaction.gateway_rejection_reason,
62
- scope: 'solidus_paypal_braintree.gateway_rejection_reasons',
63
- default: "#{transaction.status.humanize} #{transaction.gateway_rejection_reason.humanize}")
64
- when 'processor_declined'
65
- I18n.t(transaction.processor_response_code,
66
- scope: 'solidus_paypal_braintree.processor_response_codes',
67
- default: "#{transaction.processor_response_text} (#{transaction.processor_response_code})")
68
- when 'settlement_declined'
69
- I18n.t(transaction.processor_settlement_response_code,
70
- scope: 'solidus_paypal_braintree.processor_settlement_response_codes',
71
- default: "#{transaction.processor_settlement_response_text} (#{transaction.processor_settlement_response_code})") # rubocop:disable Layout/LineLength
72
- else
73
- I18n.t(transaction.status,
74
- scope: 'solidus_paypal_braintree.transaction_statuses',
75
- default: transaction.status.humanize)
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,135 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'solidus_paypal_braintree/request_protection'
4
-
5
- module SolidusPaypalBraintree
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: "SolidusPaypalBraintree::Customer", optional: true
25
-
26
- validates :payment_type, inclusion: [PAYPAL, APPLE_PAY, VENMO, CREDIT_CARD]
27
-
28
- before_save :clear_paypal_funding_source, unless: :paypal?
29
-
30
- scope(:with_payment_profile, -> { joins(:customer) })
31
- scope(:credit_card, -> { where(payment_type: CREDIT_CARD) })
32
-
33
- delegate :bin, :last_4, :card_type, :expiration_month, :expiration_year, :email,
34
- :username, :source_description, to: :braintree_payment_method, allow_nil: true
35
-
36
- # Aliases to match Spree::CreditCard's interface
37
- alias_method :last_digits, :last_4
38
- alias_method :month, :expiration_month
39
- alias_method :year, :expiration_year
40
- alias_method :cc_type, :card_type
41
-
42
- # we are not currenctly supporting an "imported" flag
43
- def imported
44
- false
45
- end
46
-
47
- def actions
48
- %w[capture void credit]
49
- end
50
-
51
- def can_capture?(payment)
52
- payment.pending? || payment.checkout?
53
- end
54
-
55
- def can_void?(payment)
56
- return false unless payment.response_code
57
-
58
- transaction = protected_request do
59
- braintree_client.transaction.find(payment.response_code)
60
- end
61
- Gateway::VOIDABLE_STATUSES.include?(transaction.status)
62
- rescue ActiveMerchant::ConnectionError
63
- false
64
- end
65
-
66
- def can_credit?(payment)
67
- payment.completed? && payment.credit_allowed > 0
68
- end
69
-
70
- def friendly_payment_type
71
- I18n.t(payment_type.underscore, scope: "solidus_paypal_braintree.payment_type")
72
- end
73
-
74
- def apple_pay?
75
- payment_type == APPLE_PAY
76
- end
77
-
78
- def paypal?
79
- payment_type == PAYPAL
80
- end
81
-
82
- def venmo?
83
- payment_type == VENMO
84
- end
85
-
86
- def reusable?
87
- token.present?
88
- end
89
-
90
- def credit_card?
91
- payment_type == CREDIT_CARD
92
- end
93
-
94
- def display_number
95
- if paypal?
96
- email
97
- elsif venmo?
98
- username
99
- else
100
- "XXXX-XXXX-XXXX-#{last_digits.to_s.rjust(4, 'X')}"
101
- end
102
- end
103
-
104
- def display_paypal_funding_source
105
- I18n.t(paypal_funding_source,
106
- scope: 'solidus_paypal_braintree.paypal_funding_sources',
107
- default: paypal_funding_source)
108
- end
109
-
110
- def display_payment_type
111
- "#{I18n.t('solidus_paypal_braintree.payment_type.label')}: #{friendly_payment_type}"
112
- end
113
-
114
- private
115
-
116
- def braintree_payment_method
117
- return unless braintree_client
118
-
119
- @braintree_payment_method ||= protected_request do
120
- braintree_client.payment_method.find(token)
121
- end
122
- rescue ActiveMerchant::ConnectionError, ArgumentError => e
123
- Rails.logger.warn("#{e}: token unknown or missing for #{inspect}")
124
- nil
125
- end
126
-
127
- def braintree_client
128
- @braintree_client ||= payment_method.try(:braintree)
129
- end
130
-
131
- def clear_paypal_funding_source
132
- self.paypal_funding_source = nil
133
- end
134
- end
135
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_model'
4
-
5
- module SolidusPaypalBraintree
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? SolidusPaypalBraintree::Gateway
18
- errors.add(:payment_method, 'Must be braintree')
19
- end
20
- if address && !address.valid?
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