solidus_braintree 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +12 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github_changelog_generator +2 -0
  5. data/.gitignore +20 -11
  6. data/.rspec +1 -1
  7. data/.rubocop.yml +79 -0
  8. data/CHANGELOG.md +178 -18
  9. data/Gemfile +31 -23
  10. data/LICENSE +26 -0
  11. data/README.md +387 -26
  12. data/Rakefile +4 -19
  13. data/app/assets/config/solidus_braintree_manifest.js +1 -0
  14. data/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_280x48.svg +19 -0
  15. data/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_320x48.svg +19 -0
  16. data/app/assets/images/solidus_braintree/venmo/venmo_active_blue_button_375x48.svg +19 -0
  17. data/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_280x48.svg +19 -0
  18. data/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_320x48.svg +19 -0
  19. data/app/assets/images/solidus_braintree/venmo/venmo_active_white_button_375x48.svg +19 -0
  20. data/app/assets/images/solidus_braintree/venmo/venmo_blue_acceptance_mark.svg +15 -0
  21. data/app/assets/images/solidus_braintree/venmo/venmo_blue_button_280x48.svg +19 -0
  22. data/app/assets/images/solidus_braintree/venmo/venmo_blue_button_320x48.svg +19 -0
  23. data/app/assets/images/solidus_braintree/venmo/venmo_blue_button_375x48.svg +19 -0
  24. data/app/assets/images/solidus_braintree/venmo/venmo_blue_logo.svg +18 -0
  25. data/app/assets/images/solidus_braintree/venmo/venmo_white_acceptance_mark.svg +20 -0
  26. data/app/assets/images/solidus_braintree/venmo/venmo_white_button_280x48.svg +19 -0
  27. data/app/assets/images/solidus_braintree/venmo/venmo_white_button_320x48.svg +19 -0
  28. data/app/assets/images/solidus_braintree/venmo/venmo_white_button_375x48.svg +19 -0
  29. data/app/assets/images/solidus_braintree/venmo/venmo_white_logo.svg +18 -0
  30. data/app/assets/javascripts/solidus_braintree/apple_pay_button.js +179 -0
  31. data/app/assets/javascripts/solidus_braintree/checkout.js +108 -0
  32. data/app/assets/javascripts/solidus_braintree/client.js +239 -0
  33. data/app/assets/javascripts/solidus_braintree/constants.js +89 -0
  34. data/app/assets/javascripts/solidus_braintree/frontend.js +14 -0
  35. data/app/assets/javascripts/solidus_braintree/hosted_form.js +46 -0
  36. data/app/assets/javascripts/solidus_braintree/paypal_button.js +178 -0
  37. data/app/assets/javascripts/solidus_braintree/paypal_messaging.js +22 -0
  38. data/app/assets/javascripts/solidus_braintree/promise.js +20 -0
  39. data/app/assets/javascripts/solidus_braintree/venmo_button.js +86 -0
  40. data/app/assets/javascripts/spree/backend/solidus_braintree.js +96 -0
  41. data/app/assets/javascripts/spree/frontend/paypal_button.js +34 -0
  42. data/app/assets/javascripts/spree/frontend/solidus_braintree.js +1 -0
  43. data/app/assets/stylesheets/spree/backend/solidus_braintree.scss +28 -0
  44. data/app/assets/stylesheets/spree/frontend/solidus_braintree.scss +51 -0
  45. data/app/decorators/controllers/solidus_braintree/admin_payments_controller_decorator.rb +11 -0
  46. data/app/decorators/controllers/solidus_braintree/checkout_controller_decorator.rb +11 -0
  47. data/app/decorators/controllers/solidus_braintree/client_tokens_controller.rb +41 -0
  48. data/app/decorators/controllers/solidus_braintree/orders_controller_decorator.rb +11 -0
  49. data/app/decorators/models/solidus_braintree/spree/store_decorator.rb +20 -0
  50. data/app/decorators/models/solidus_braintree/spree/user_decorator.rb +13 -0
  51. data/app/helpers/solidus_braintree/braintree_admin_helper.rb +23 -0
  52. data/app/helpers/solidus_braintree/braintree_checkout_helper.rb +60 -0
  53. data/app/models/application_record.rb +5 -0
  54. data/app/models/solidus_braintree/address.rb +64 -0
  55. data/app/models/solidus_braintree/avs_result.rb +69 -0
  56. data/app/models/solidus_braintree/configuration.rb +39 -0
  57. data/app/models/solidus_braintree/customer.rb +8 -0
  58. data/app/models/solidus_braintree/gateway.rb +433 -0
  59. data/app/models/solidus_braintree/response.rb +80 -0
  60. data/app/models/solidus_braintree/source.rb +135 -0
  61. data/app/models/solidus_braintree/transaction.rb +31 -0
  62. data/app/models/solidus_braintree/transaction_address.rb +88 -0
  63. data/app/models/solidus_braintree/transaction_import.rb +98 -0
  64. data/app/overrides/spree/payments/payment/add_paypal_funding_source_to_payment.rb +9 -0
  65. data/app/views/spree/api/payments/source_views/_braintree.json.jbuilder +1 -1
  66. data/app/views/spree/checkout/existing_payment/_braintree.html.erb +10 -0
  67. data/app/views/spree/shared/_apple_pay_button.html.erb +27 -0
  68. data/app/views/spree/shared/_braintree_errors.html.erb +16 -0
  69. data/app/views/spree/shared/_braintree_head_scripts.html.erb +26 -0
  70. data/app/views/spree/shared/_braintree_hosted_fields.html.erb +43 -0
  71. data/app/views/spree/shared/_paypal_cart_button.html.erb +38 -0
  72. data/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
  73. data/app/views/spree/shared/_venmo_button.html.erb +33 -0
  74. data/bin/console +4 -1
  75. data/bin/rails +5 -5
  76. data/bin/rails-engine +13 -0
  77. data/bin/rails-sandbox +16 -0
  78. data/bin/rake +7 -0
  79. data/bin/sandbox +103 -0
  80. data/bin/setup +5 -4
  81. data/config/locales/en.yml +94 -2
  82. data/config/locales/it.yml +56 -0
  83. data/config/routes.rb +12 -3
  84. data/db/migrate/20160830061749_create_solidus_paypal_braintree_sources.rb +16 -0
  85. data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +13 -0
  86. data/db/migrate/20161114231422_create_solidus_paypal_braintree_configurations.rb +11 -0
  87. data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +7 -0
  88. data/db/migrate/20170203191030_add_credit_card_to_braintree_configuration.rb +6 -0
  89. data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +38 -0
  90. data/db/migrate/20170508085402_add_not_null_constraint_to_sources_payment_type.rb +14 -0
  91. data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
  92. data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
  93. data/db/migrate/20211222170950_add_paypal_funding_source_to_solidus_paypal_braintree_sources.rb +5 -0
  94. data/db/migrate/20220104150301_add_venmo_to_braintree_configuration.rb +5 -0
  95. data/db/migrate/20230109080950_rename_solidus_paypal_braintree_source_type.rb +31 -0
  96. data/lib/controllers/backend/solidus_braintree/configurations_controller.rb +48 -0
  97. data/lib/controllers/frontend/solidus_braintree/checkouts_controller.rb +31 -0
  98. data/lib/controllers/frontend/solidus_braintree/transactions_controller.rb +67 -0
  99. data/lib/generators/solidus_braintree/install/install_generator.rb +54 -18
  100. data/lib/generators/solidus_braintree/install/templates/initializer.rb +6 -0
  101. data/lib/solidus_braintree/country_mapper.rb +37 -0
  102. data/lib/solidus_braintree/engine.rb +55 -10
  103. data/lib/solidus_braintree/extension_configuration.rb +23 -0
  104. data/lib/solidus_braintree/request_protection.rb +21 -0
  105. data/lib/solidus_braintree/testing_support/factories.rb +53 -0
  106. data/lib/solidus_braintree/version.rb +3 -1
  107. data/lib/solidus_braintree.rb +14 -2
  108. data/lib/solidus_paypal_braintree.rb +6 -0
  109. data/lib/views/backend/solidus_braintree/configurations/list.html.erb +63 -0
  110. data/lib/views/backend/spree/admin/payments/source_forms/_braintree.html.erb +16 -0
  111. data/lib/views/backend/spree/admin/payments/source_views/_braintree.html.erb +39 -0
  112. data/lib/views/backend/spree/admin/shared/preference_fields/_preference_select.html.erb +13 -0
  113. data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_braintree.html.erb +16 -0
  114. data/lib/views/backend_v2.4/spree/admin/shared/preference_fields/_hash.html.erb +12 -0
  115. data/lib/views/frontend/solidus_braintree/payments/_payment.html.erb +12 -0
  116. data/lib/views/frontend/spree/checkout/payment/_braintree.html.erb +23 -0
  117. data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +32 -0
  118. data/solidus_braintree.gemspec +39 -38
  119. data/spec/controllers/solidus_braintree/checkouts_controller_spec.rb +99 -0
  120. data/spec/controllers/solidus_braintree/client_tokens_controller_spec.rb +55 -0
  121. data/spec/controllers/solidus_braintree/configurations_controller_spec.rb +73 -0
  122. data/spec/controllers/solidus_braintree/transactions_controller_spec.rb +183 -0
  123. data/spec/features/backend/configuration_spec.rb +23 -0
  124. data/spec/features/backend/new_payment_spec.rb +137 -0
  125. data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +191 -0
  126. data/spec/features/frontend/paypal_checkout_spec.rb +166 -0
  127. data/spec/features/frontend/venmo_checkout_spec.rb +194 -0
  128. data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
  129. data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
  130. data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
  131. data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
  132. data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
  133. data/spec/fixtures/cassettes/braintree/token.yml +63 -0
  134. data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
  135. data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
  136. data/spec/fixtures/cassettes/checkout/update.yml +71 -0
  137. data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +171 -0
  138. data/spec/fixtures/cassettes/checkout/valid_venmo_transaction.yml +599 -0
  139. data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
  140. data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
  141. data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
  142. data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
  143. data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
  144. data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
  145. data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
  146. data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
  147. data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
  148. data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
  149. data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
  150. data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
  151. data/spec/fixtures/cassettes/gateway/customer.yml +79 -0
  152. data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
  153. data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
  154. data/spec/fixtures/cassettes/gateway/void.yml +137 -0
  155. data/spec/fixtures/cassettes/source/bin.yml +295 -0
  156. data/spec/fixtures/cassettes/source/card_type.yml +267 -0
  157. data/spec/fixtures/cassettes/source/last4.yml +267 -0
  158. data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
  159. data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
  160. data/spec/fixtures/views/spree/orders/edit.html.erb +50 -0
  161. data/spec/helpers/solidus_braintree/braintree_admin_helper_spec.rb +17 -0
  162. data/spec/helpers/solidus_braintree/braintree_checkout_helper_spec.rb +70 -0
  163. data/spec/models/solidus_braintree/address_spec.rb +71 -0
  164. data/spec/models/solidus_braintree/avs_result_spec.rb +317 -0
  165. data/spec/models/solidus_braintree/gateway_spec.rb +742 -0
  166. data/spec/models/solidus_braintree/response_spec.rb +280 -0
  167. data/spec/models/solidus_braintree/source_spec.rb +539 -0
  168. data/spec/models/solidus_braintree/transaction_address_spec.rb +235 -0
  169. data/spec/models/solidus_braintree/transaction_import_spec.rb +302 -0
  170. data/spec/models/solidus_braintree/transaction_spec.rb +86 -0
  171. data/spec/models/spree/store_spec.rb +14 -0
  172. data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
  173. data/spec/spec_helper.rb +32 -0
  174. data/spec/support/capybara.rb +7 -0
  175. data/spec/support/gateway_helpers.rb +29 -0
  176. data/spec/support/order_ready_for_payment.rb +37 -0
  177. data/spec/support/vcr.rb +42 -0
  178. data/spec/support/views.rb +1 -0
  179. metadata +276 -224
  180. data/LICENSE.txt +0 -21
  181. data/app/controllers/spree/api/braintree_client_token_controller.rb +0 -13
  182. data/app/decorators/lib/solidus_braintree/spree/permitted_attributes_decorator.rb +0 -9
  183. data/app/decorators/models/solidus_braintree/spree/credit_card_decorator.rb +0 -11
  184. data/app/decorators/models/solidus_braintree/spree/payment_decorator.rb +0 -10
  185. data/app/helpers/braintree_view_helpers.rb +0 -20
  186. data/app/models/concerns/solidus_braintree/add_name_validation_concern.rb +0 -8
  187. data/app/models/concerns/solidus_braintree/inject_device_data_concern.rb +0 -18
  188. data/app/models/concerns/solidus_braintree/payment_braintree_nonce_concern.rb +0 -8
  189. data/app/models/concerns/solidus_braintree/permitted_attributes_concern.rb +0 -11
  190. data/app/models/concerns/solidus_braintree/skip_require_card_numbers_concern.rb +0 -14
  191. data/app/models/concerns/solidus_braintree/use_data_field_concern.rb +0 -23
  192. data/app/models/solidus/gateway/braintree_gateway.rb +0 -306
  193. data/app/overrides/spree/checkout/_confirm/braintree_security.html.erb.deface +0 -9
  194. data/app/views/spree/admin/payments/source_forms/_braintree.html.erb +0 -38
  195. data/app/views/spree/admin/payments/source_views/_braintree.html.erb +0 -30
  196. data/app/views/spree/checkout/payment/_braintree.html.erb +0 -55
  197. data/app/views/spree/checkout/payment/_braintree_initialization.html.erb +0 -12
  198. data/config/initializers/braintree.rb +0 -3
  199. data/db/migrate/20150910170527_add_data_to_credit_card.rb +0 -5
  200. data/db/migrate/20160426221931_add_braintree_device_data_to_order.rb +0 -5
  201. data/lib/assets/javascripts/spree/backend/braintree/solidus_braintree.js +0 -59
  202. data/lib/assets/javascripts/spree/frontend/braintree/solidus_braintree.js +0 -144
  203. data/lib/assets/javascripts/vendor/braintree.js +0 -8
  204. data/lib/assets/stylesheets/spree/frontend/solidus_braintree.scss +0 -26
@@ -0,0 +1,96 @@
1
+ //= require solidus_braintree/constants
2
+ //= require solidus_braintree/client
3
+ //= require solidus_braintree/promise
4
+ //= require solidus_braintree/hosted_form
5
+
6
+ $(function() {
7
+ var $paymentForm = $("#new_payment"),
8
+ $hostedFields = $("[data-braintree-hosted-fields]"),
9
+ hostedFieldsInstance = null;
10
+
11
+ function onError (err) {
12
+ var msg = err.name + ": " + err.message;
13
+ show_flash("error", msg);
14
+ }
15
+
16
+ function showForm(id) {
17
+ $("#card_form" + id).show();
18
+ }
19
+
20
+ function hideForm(id) {
21
+ $("#card_form" + id).hide();
22
+ }
23
+
24
+ function addFormHook(braintreeForm, errorCallback) {
25
+ var shouldSubmit = false;
26
+
27
+ function submit(payload) {
28
+ shouldSubmit = true;
29
+
30
+ $("#payment_method_nonce", braintreeForm.hostedFields).val(payload.nonce);
31
+ $paymentForm.submit();
32
+ }
33
+
34
+ return function(hostedFields) {
35
+ $paymentForm.on("submit", function(e) {
36
+ if ($hostedFields.is(":visible") && !shouldSubmit) {
37
+ e.preventDefault();
38
+
39
+ hostedFields.tokenize(function(err, payload) {
40
+ if (err) {
41
+ errorCallback(err);
42
+ } else {
43
+ submit(payload);
44
+ }
45
+ });
46
+ }
47
+ });
48
+ };
49
+ }
50
+
51
+ function initFields($container, id) {
52
+ function setHostedFieldsInstance(instance) {
53
+ hostedFieldsInstance = instance;
54
+ return instance;
55
+ }
56
+
57
+ if (hostedFieldsInstance === null) {
58
+ braintreeForm = new SolidusBraintree.createHostedForm(id);
59
+ braintreeForm.initialize().
60
+ then(setHostedFieldsInstance).
61
+ then(addFormHook(braintreeForm, onError)).
62
+ fail(onError);
63
+ }
64
+ }
65
+
66
+ // exit early if we're not looking at the New Payment form, or if no
67
+ // SolidusBraintree payment methods have been configured.
68
+ if (!$paymentForm.length || !$hostedFields.length) { return; }
69
+
70
+ $.when(
71
+ $.getScript("https://js.braintreegateway.com/web/3.84.0/js/client.min.js"),
72
+ $.getScript("https://js.braintreegateway.com/web/3.84.0/js/hosted-fields.min.js")
73
+ ).done(function() {
74
+ $hostedFields.each(function() {
75
+ var $this = $(this),
76
+ $radios = $("[name=card]", $this),
77
+ id = $this.data("payment-method-id");
78
+
79
+ // If we have previous cards, init fields on change of radio button
80
+ if ($radios.length) {
81
+ $radios.on("change", function() {
82
+ if ($(this).val() == 'new') {
83
+ showForm(id);
84
+ initFields($this, id);
85
+ } else {
86
+ hideForm(id);
87
+ }
88
+ });
89
+ } else {
90
+ // If we don't have previous cards, init fields immediately
91
+ initFields($this, id);
92
+ showForm(id);
93
+ }
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,34 @@
1
+ //= require solidus_braintree/paypal_button
2
+
3
+ // This is the PayPal button on the cart page
4
+ $(document).ready(function() {
5
+ if (document.getElementById("empty-cart")) {
6
+ $.when(
7
+ $.getScript("https://js.braintreegateway.com/3.84.0/js/client.min.js"),
8
+ $.getScript("https://js.braintreegateway.com/3.84.0/js/paypal-checkout.min.js"),
9
+ $.getScript("https://js.braintreegateway.com/3.84.0/js/data-collector.min.js")
10
+ ).done(function() {
11
+ $("#content").append('<div id="paypal-button"/>');
12
+ $('<script/>').attr({
13
+ 'data-version-4' : "true"
14
+ }).
15
+ load(function() {
16
+ var paypalOptions = {
17
+ flow: 'vault',
18
+ enableShippingAddress: true
19
+ }
20
+ var options = {
21
+ restart_checkout: true
22
+ }
23
+ var button = new SolidusBraintree.createPaypalButton(
24
+ document.querySelector("#paypal-button"),
25
+ paypalOptions,
26
+ options
27
+ );
28
+ return button.initialize();
29
+ }).
30
+ insertAfter("#content").
31
+ attr('src', 'https://www.paypalobjects.com/api/checkout.js');
32
+ });
33
+ }
34
+ });
@@ -0,0 +1 @@
1
+ //= require solidus_braintree/frontend
@@ -0,0 +1,28 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
5
+
6
+ // Set Bootstrap variables if they're not already defined
7
+ // e.g if we're on Solidus < v1.4
8
+ $input-border-color: #cee1f4 !default;
9
+ $border-radius: 3px !default;
10
+ $input-color: #5498DA !default;
11
+ $brand-danger: #C60F13 !default;
12
+ $brand-success: #9FC820 !default;
13
+
14
+ .braintree-hosted-fields .input {
15
+ border: 1px solid $input-border-color;
16
+ border-radius: $border-radius;
17
+ color: $input-color;
18
+ height: 30px;
19
+ padding: 7px 10px;
20
+
21
+ &.braintree-hosted-fields-invalid {
22
+ border-color: $brand-danger;
23
+ }
24
+
25
+ &.braintree-hosted-fields-valid {
26
+ border-color: $brand-success;
27
+ }
28
+ }
@@ -0,0 +1,51 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/frontend/all.css'
4
+ */
5
+
6
+ .braintree-hosted-fields .input {
7
+ border: 1px solid #d9d9db;
8
+ border-radius: 3px;
9
+ color: #5498DA;
10
+ height: 30px;
11
+ padding: 5px 10px;
12
+ }
13
+
14
+ .paypal-button-widget .paypal-button:hover {
15
+ background: transparent;
16
+ }
17
+
18
+ .apple-pay-button {
19
+ -webkit-appearance: -apple-pay-button;
20
+ -apple-pay-button-type: buy;
21
+ visibility: hidden;
22
+ display: inline-block;
23
+ min-height: 30px;
24
+ border: 1px solid black;
25
+ background-image: -webkit-named-image(apple-pay-logo-black);
26
+ background-size: 100% calc(60% + 2px);
27
+ background-repeat: no-repeat;
28
+ background-color: white;
29
+ background-position: 50% 50%;
30
+ border-radius: 5px;
31
+ padding: 0;
32
+ margin: 5px auto;
33
+ transition: background-color .15s;
34
+ width: auto;
35
+ }
36
+
37
+ .apple-pay-button.visible {
38
+ visibility: visible;
39
+ }
40
+
41
+ .venmo-button {
42
+ visibility: hidden;
43
+ border: none;
44
+ height: 48px;
45
+ background-color: transparent;
46
+ background-repeat: no-repeat;
47
+ }
48
+
49
+ .venmo-button.visible {
50
+ visibility: visible;
51
+ }
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module AdminPaymentsControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusBraintree::BraintreeAdminHelper
7
+ end
8
+
9
+ ::Spree::Admin::PaymentsController.prepend(self)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module CheckoutControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusBraintree::BraintreeCheckoutHelper
7
+ end
8
+
9
+ ::Spree::CheckoutController.prepend(self) if SolidusSupport.frontend_available?
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ class ClientTokensController < ::Spree::Api::BaseController
5
+ skip_before_action :authenticate_user
6
+
7
+ before_action :load_gateway
8
+
9
+ def create
10
+ token = @gateway.generate_token
11
+ if token
12
+ render json: { client_token: token, payment_method_id: @gateway.id }
13
+ else
14
+ render json: { error: Gateway::TOKEN_GENERATION_DISABLED_MESSAGE }, status: :unprocessable_entity
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def load_gateway
21
+ if params[:payment_method_id]
22
+ @gateway = ::SolidusBraintree::Gateway.find(params[:payment_method_id])
23
+ else
24
+ store_payment_methods_scope =
25
+ if current_store.payment_methods.empty?
26
+ ::SolidusBraintree::Gateway.all
27
+ else
28
+ ::SolidusBraintree::Gateway.where(id: current_store.payment_method_ids)
29
+ end
30
+ @gateway = ::SolidusBraintree::Gateway.where(active: true).merge(store_payment_methods_scope).first!
31
+ end
32
+ end
33
+
34
+ def generate_token
35
+ @gateway.generate_token
36
+ rescue ::SolidusBraintree::Gateway::TokenGenerationDisabledError => e
37
+ Rails.logger.error e
38
+ nil
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module OrdersControllerDecorator
5
+ def self.prepended(base)
6
+ base.helper ::SolidusBraintree::BraintreeCheckoutHelper
7
+ end
8
+
9
+ ::Spree::OrdersController.prepend(self) if SolidusSupport.frontend_available?
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module Spree
5
+ module StoreDecorator
6
+ def self.prepended(base)
7
+ base.has_one :braintree_configuration, class_name: "SolidusBraintree::Configuration", dependent: :destroy
8
+ base.before_create :build_default_configuration
9
+ end
10
+
11
+ private
12
+
13
+ def build_default_configuration
14
+ build_braintree_configuration unless braintree_configuration
15
+ end
16
+
17
+ ::Spree::Store.prepend self
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module Spree
5
+ module UserDecorator
6
+ def self.prepended(base)
7
+ base.has_one :braintree_customer, class_name: 'SolidusBraintree::Customer', inverse_of: :user
8
+ end
9
+
10
+ ::Spree.user_class.prepend self
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module BraintreeAdminHelper
5
+ # Returns a link to the Braintree web UI for the given Braintree payment
6
+ def braintree_transaction_link(payment)
7
+ environment = payment.payment_method.preferred_environment == 'sandbox' ? 'sandbox' : 'www'
8
+ merchant_id = payment.payment_method.preferred_merchant_id
9
+ response_code = payment.response_code
10
+
11
+ return if response_code.blank?
12
+ return response_code if merchant_id.blank?
13
+
14
+ link_to(
15
+ response_code,
16
+ "https://#{environment}.braintreegateway.com/merchants/#{merchant_id}/transactions/#{response_code}",
17
+ title: 'Show payment on Braintree',
18
+ target: '_blank',
19
+ rel: 'noopener'
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ module BraintreeCheckoutHelper
5
+ def braintree_3ds_options_for(order)
6
+ ship_address = SolidusBraintree::Address.new(order.ship_address)
7
+ bill_address = SolidusBraintree::Address.new(order.bill_address)
8
+ {
9
+ nonce: nil, # populated after tokenization
10
+ bin: nil, # populated after tokenization
11
+ onLookupComplete: nil, # populated after tokenization
12
+ amount: order.total,
13
+ email: order.email,
14
+ billingAddress: {
15
+ givenName: bill_address.firstname,
16
+ surname: bill_address.lastname,
17
+ phoneNumber: bill_address.phone,
18
+ streetAddress: bill_address.address1,
19
+ extendedAddress: bill_address.address2,
20
+ locality: bill_address.city,
21
+ region: bill_address.state&.abbr,
22
+ postalCode: bill_address.zipcode,
23
+ countryCodeAlpha2: bill_address.country&.iso,
24
+ },
25
+ additionalInformation: {
26
+ shippingGivenName: ship_address.firstname,
27
+ shippingSurname: ship_address.lastname,
28
+ shippingPhone: ship_address.phone,
29
+ shippingAddress: {
30
+ streedAddress: ship_address.address1,
31
+ extendedAddress: ship_address.address2,
32
+ locality: ship_address.city,
33
+ region: ship_address.state&.abbr,
34
+ postalCode: ship_address.zipcode,
35
+ countryCodeAlpha2: ship_address.country&.iso,
36
+ }
37
+ }
38
+ }
39
+ end
40
+
41
+ def paypal_button_preference(key, store:)
42
+ store.braintree_configuration.preferences[key]
43
+ end
44
+
45
+ def venmo_button_style(store)
46
+ configuration = store.braintree_configuration
47
+ color = configuration.preferred_venmo_button_color
48
+ width = configuration.preferred_venmo_button_width
49
+
50
+ { width: width, color: color }
51
+ end
52
+
53
+ def venmo_button_asset_url(style, active: false)
54
+ prefix = 'solidus_braintree/venmo/venmo_'
55
+ active_string = active ? 'active_' : ''
56
+ path = "#{prefix}#{active_string}#{style[:color]}_button_#{style[:width]}x48.svg"
57
+ asset_path(path)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ class Address
5
+ delegate :address1,
6
+ :address2,
7
+ :city,
8
+ :country,
9
+ :phone,
10
+ :state,
11
+ :zipcode,
12
+ to: :spree_address
13
+
14
+ def self.split_name(name)
15
+ if defined?(Spree::Address::Name)
16
+ address_name = Spree::Address::Name.new(name)
17
+ [address_name.first_name, address_name.last_name]
18
+ else
19
+ name.strip.split(' ', 2)
20
+ end
21
+ end
22
+
23
+ def initialize(spree_address)
24
+ @spree_address = spree_address
25
+ end
26
+
27
+ def to_json(*_args)
28
+ address_hash = {
29
+ line1: address1,
30
+ line2: address2,
31
+ city: city,
32
+ postalCode: zipcode,
33
+ countryCode: country.iso,
34
+ phone: phone,
35
+ recipientName: "#{firstname} #{lastname}"
36
+ }
37
+
38
+ if ::Spree::Config.address_requires_state && country.states_required
39
+ address_hash[:state] = state.name
40
+ end
41
+ address_hash.to_json
42
+ end
43
+
44
+ def firstname
45
+ if SolidusSupport.combined_first_and_last_name_in_address?
46
+ self.class.split_name(spree_address.name).first
47
+ else
48
+ spree_address.firstname
49
+ end
50
+ end
51
+
52
+ def lastname
53
+ if SolidusSupport.combined_first_and_last_name_in_address?
54
+ self.class.split_name(spree_address.name).last
55
+ else
56
+ spree_address.lastname
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :spree_address
63
+ end
64
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_merchant/billing/avs_result'
4
+
5
+ module SolidusBraintree
6
+ class AVSResult < ActiveMerchant::Billing::AVSResult
7
+ # Mapping took from ActiveMerchant::Billing::BraintreeBlueGateway
8
+ AVS_MAPPING = {
9
+ 'M' => {
10
+ 'M' => 'M',
11
+ 'N' => 'A',
12
+ 'U' => 'B',
13
+ 'I' => 'B',
14
+ 'A' => 'B'
15
+ },
16
+ 'N' => {
17
+ 'M' => 'Z',
18
+ 'N' => 'C',
19
+ 'U' => 'C',
20
+ 'I' => 'C',
21
+ 'A' => 'C'
22
+ },
23
+ 'U' => {
24
+ 'M' => 'P',
25
+ 'N' => 'N',
26
+ 'U' => 'I',
27
+ 'I' => 'I',
28
+ 'A' => 'I'
29
+ },
30
+ 'I' => {
31
+ 'M' => 'P',
32
+ 'N' => 'C',
33
+ 'U' => 'I',
34
+ 'I' => 'I',
35
+ 'A' => 'I'
36
+ },
37
+ 'A' => {
38
+ 'M' => 'P',
39
+ 'N' => 'C',
40
+ 'U' => 'I',
41
+ 'I' => 'I',
42
+ 'A' => 'I'
43
+ },
44
+ 'B' => {
45
+ 'B' => 'B'
46
+ },
47
+ nil => { nil => nil }
48
+ }.freeze
49
+
50
+ class << self
51
+ private :new
52
+
53
+ def build(transaction)
54
+ new(
55
+ code: avs_code_from(transaction),
56
+ street_match: transaction.avs_street_address_response_code,
57
+ postal_match: transaction.avs_postal_code_response_code
58
+ )
59
+ end
60
+
61
+ private
62
+
63
+ def avs_code_from(transaction)
64
+ transaction.avs_error_response_code ||
65
+ AVS_MAPPING[transaction.avs_street_address_response_code][transaction.avs_postal_code_response_code]
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ class Configuration < ::Spree::Base
5
+ PAYPAL_BUTTON_PREFERENCES = {
6
+ color: { availables: %w[gold blue silver white black], default: 'white' },
7
+ shape: { availables: %w[pill rect], default: 'rect' },
8
+ label: { availables: %w[checkout credit pay buynow paypal installment], default: 'checkout' },
9
+ tagline: { availables: %w[true false], default: 'false' },
10
+ layout: { availables: %w[horizontal vertical], default: 'horizontal' },
11
+ messaging: { availables: %w[true false], default: 'false' }
12
+ }.freeze
13
+
14
+ include ::Spree::Preferences::Persistable
15
+
16
+ belongs_to :store, class_name: 'Spree::Store', optional: false
17
+
18
+ # Preferences for Paypal button
19
+ PAYPAL_BUTTON_PREFERENCES.each do |name, desc|
20
+ preference_name = "paypal_button_#{name}".to_sym
21
+ attribute_name = "preferred_#{preference_name}".to_sym
22
+
23
+ preference preference_name, :string, default: desc[:default]
24
+
25
+ validates attribute_name, inclusion: desc[:availables]
26
+ end
27
+
28
+ preference :venmo_button_color, :preference_select, default: 'blue'
29
+ preference :venmo_button_width, :preference_select, default: '320'
30
+
31
+ def preferred_venmo_button_color_options
32
+ [["Blue", "blue"], ["White", "white"]]
33
+ end
34
+
35
+ def preferred_venmo_button_width_options
36
+ [["280", "280"], ["320", "320"], ["375", "375"]]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusBraintree
4
+ class Customer < ApplicationRecord
5
+ belongs_to :user, class_name: ::Spree::UserClassHandle.new, optional: true
6
+ has_many :sources, class_name: "SolidusBraintree::Source", inverse_of: :customer, dependent: :destroy
7
+ end
8
+ end