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,239 @@
1
+ /**
2
+ * Braintree client interface
3
+ * @external "braintree.Client"
4
+ * @see {@link https://braintree.github.io/braintree-web/current/Client.html|Braintree Client Docs}
5
+ **/
6
+
7
+ /**
8
+ * Braintree paypal interface
9
+ * @external "braintree.PayPal"
10
+ * @see {@link https://braintree.github.io/braintree-web/current/PayPal.html|Braintree Paypal Docs}
11
+ **/
12
+
13
+ /**
14
+ * Braintree paypal interface
15
+ * @external "braintree.ApplePay"
16
+ * @see {@link https://braintree.github.io/braintree-web/current/ApplePay.html|Braintree Apple Pay Docs}
17
+ **/
18
+
19
+ /**
20
+ * Braintree dataCollector interface
21
+ * @external "braintree.DataCollector"
22
+ * @see {@link https://braintree.github.io/braintree-web/current/DataCollector.html|Braintree DataCollector Docs}
23
+ **/
24
+
25
+ /**
26
+ * jQuery.Deferred interface
27
+ *
28
+ * We use this for our promises because ES6 promises are non standard, and because jquery 1/2
29
+ * promises do not play nicely with them.
30
+ * @external "jQuery.Deferred"
31
+ * @see {@link https://api.jquery.com/category/deferred-object/|jQuery Deferred Documentation}
32
+ **/
33
+
34
+ /**
35
+ * Represents a wrapper around the braintree js library.
36
+ *
37
+ * This class is responsible for fetching tokens from a solidus store and using them
38
+ * to manage a braintree client. It takes a number of options as capabilities for the client
39
+ * depending on if you want to use use the data collector or paypal.
40
+ *
41
+ * We use this class mostly to hide the token operations for users.
42
+ *
43
+ * After creating the class, a call should be made to initialize before using it.
44
+ * @see initialize
45
+ *
46
+ * @constructor
47
+ * @param {Object} config Initalization options for the client
48
+ * @param {Boolean} config.useDataCollector Use data collector capabilities for the braintree client
49
+ * @param {Boolean} config.usePaypal Use Paypal capabilities for the braintree client
50
+ * @param {requestCallback} config.readyCallback A callback to be invoked when the client is ready to go.
51
+ * @param {Number} config.paymentMethodId A number indicating a specific payment method to be preferrred.
52
+ *
53
+ **/
54
+ SolidusBraintree.Client = function(config) {
55
+ this.paymentMethodId = config.paymentMethodId;
56
+ this.readyCallback = config.readyCallback;
57
+ this.useDataCollector = config.useDataCollector;
58
+ this.usePaypal = config.usePaypal;
59
+ this.useApplepay = config.useApplepay;
60
+ this.useVenmo = config.useVenmo;
61
+ this.flow = config.flow;
62
+ this.venmoNewTabSupported = config.newBrowserTabSupported
63
+ this.useThreeDSecure = config.useThreeDSecure;
64
+
65
+ this._braintreeInstance = null;
66
+ this._dataCollectorInstance = null;
67
+ this._paypalInstance = null;
68
+ this._venmoInstance = null;
69
+ this._threeDSecureInstance = null;
70
+ };
71
+
72
+ /**
73
+ * Fetches a client token from the backend and initializes the braintree client.
74
+ * @returns {external:"jQuery.Deferred"} Promise to be invoked after initialization is complete
75
+ **/
76
+ SolidusBraintree.Client.prototype.initialize = function() {
77
+ var initializationPromise = this._fetchToken().
78
+ then(this._createBraintreeInstance.bind(this));
79
+
80
+ if (this.useDataCollector) {
81
+ initializationPromise = initializationPromise.then(this._createDataCollector.bind(this));
82
+ }
83
+
84
+ if (this.usePaypal) {
85
+ initializationPromise = initializationPromise.then(this._createPaypal.bind(this));
86
+ }
87
+
88
+ if (this.useApplepay) {
89
+ initializationPromise = initializationPromise.then(this._createApplepay.bind(this));
90
+ }
91
+
92
+ if (this.useVenmo) {
93
+ initializationPromise = initializationPromise.then(this._createVenmo.bind(this));
94
+ }
95
+
96
+ if (this.useThreeDSecure) {
97
+ initializationPromise = initializationPromise.then(this._createThreeDSecure.bind(this));
98
+ }
99
+
100
+ return initializationPromise.then(this._invokeReadyCallback.bind(this));
101
+ };
102
+
103
+ /**
104
+ * Returns the braintree client instance
105
+ * @returns {external:"braintree.Client"} The braintree client that was initialized by this class
106
+ **/
107
+ SolidusBraintree.Client.prototype.getBraintreeInstance = function() {
108
+ return this._braintreeInstance;
109
+ };
110
+
111
+ /**
112
+ * Returns the braintree paypal instance
113
+ * @returns {external:"braintree.PayPal"} The braintree paypal that was initialized by this class
114
+ **/
115
+ SolidusBraintree.Client.prototype.getPaypalInstance = function() {
116
+ return this._paypalInstance;
117
+ };
118
+
119
+ /**
120
+ * Returns the braintree Apple Pay instance
121
+ * @returns {external:"braintree.ApplePay"} The Braintree Apple Pay that was initialized by this class
122
+ **/
123
+ SolidusBraintree.Client.prototype.getApplepayInstance = function() {
124
+ return this._applepayInstance;
125
+ };
126
+
127
+ /**
128
+ * Returns the braintree Venmo instance
129
+ * @returns {external:"braintree.Venmo"} The Braintree Venmo that was initialized by this class
130
+ **/
131
+ SolidusBraintree.Client.prototype.getVenmoInstance = function() {
132
+ return this._venmoInstance;
133
+ };
134
+
135
+ /**
136
+ * Returns the braintree dataCollector instance
137
+ * @returns {external:"braintree.DataCollector"} The braintree dataCollector that was initialized by this class
138
+ **/
139
+ SolidusBraintree.Client.prototype.getDataCollectorInstance = function() {
140
+ return this._dataCollectorInstance;
141
+ };
142
+
143
+
144
+ SolidusBraintree.Client.prototype._fetchToken = function() {
145
+ var payload = {
146
+ dataType: 'json',
147
+ type: 'POST',
148
+ url: SolidusBraintree.config.paths.clientTokens,
149
+ error: function(xhr) {
150
+ console.error("Error fetching braintree token");
151
+ }
152
+ };
153
+
154
+ if (this.paymentMethodId) {
155
+ payload.data = {
156
+ payment_method_id: this.paymentMethodId
157
+ };
158
+ }
159
+
160
+ return Spree.ajax(payload);
161
+ };
162
+
163
+ SolidusBraintree.Client.prototype._createBraintreeInstance = function(tokenResponse) {
164
+ this.paymentMethodId = tokenResponse.payment_method_id;
165
+
166
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.client.create, [{
167
+ authorization: tokenResponse.client_token
168
+ }]).then(function (clientInstance) {
169
+ this._braintreeInstance = clientInstance;
170
+ return clientInstance;
171
+ }.bind(this));
172
+ };
173
+
174
+ SolidusBraintree.Client.prototype._invokeReadyCallback = function() {
175
+ if(this.readyCallback) {
176
+ this.readyCallback(this._braintreeInstance);
177
+ }
178
+
179
+ return this;
180
+ };
181
+
182
+ SolidusBraintree.Client.prototype._createDataCollector = function() {
183
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.dataCollector.create, [{
184
+ client: this._braintreeInstance,
185
+ paypal: !!this.usePaypal
186
+ }]).then(function (dataCollectorInstance) {
187
+ this._dataCollectorInstance = dataCollectorInstance;
188
+ return dataCollectorInstance;
189
+ }.bind(this));
190
+ };
191
+
192
+ SolidusBraintree.Client.prototype._createPaypal = function() {
193
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.paypalCheckout.create, [{
194
+ client: this._braintreeInstance
195
+ }]).then(function (paypalInstance) {
196
+ this._paypalInstance = paypalInstance;
197
+ return paypalInstance;
198
+ }.bind(this), function(error) {
199
+ console.error(error.name + ':', error.message);
200
+ });
201
+ };
202
+
203
+ SolidusBraintree.Client.prototype._createApplepay = function() {
204
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.applePay.create, [{
205
+ client: this._braintreeInstance
206
+ }]).then(function (applePayInstance) {
207
+ this._applepayInstance = applePayInstance;
208
+ return applePayInstance;
209
+ }.bind(this));
210
+ };
211
+
212
+ SolidusBraintree.Client.prototype._createVenmo = function() {
213
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.venmo.create, [{
214
+ client: this._braintreeInstance,
215
+ allowDesktop: true,
216
+ paymentMethodUsage: this.flow === 'vault' ? 'multi_use' : 'single_use',
217
+ allowNewBrowserTab: this.venmoNewTabSupported
218
+ }]).then(function (venmoInstance) {
219
+ // Verify browser support before proceeding.
220
+ if (!venmoInstance.isBrowserSupported()) {
221
+ console.log('Browser does not support Venmo');
222
+ return;
223
+ }
224
+
225
+ this._venmoInstance = venmoInstance;
226
+ return venmoInstance;
227
+ }.bind(this));
228
+ };
229
+
230
+ SolidusBraintree.Client.prototype._createThreeDSecure = function() {
231
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.threeDSecure.create, [{
232
+ client: this._braintreeInstance,
233
+ version: 2
234
+ }]).then(function (threeDSecureInstance) {
235
+ this._threeDSecureInstance = threeDSecureInstance;
236
+ }.bind(this), function(error) {
237
+ console.log(error);
238
+ });
239
+ };
@@ -0,0 +1,89 @@
1
+ SolidusBraintree = {
2
+ APPLE_PAY_API_VERSION: 1,
3
+
4
+ config: {
5
+ paths: {
6
+ clientTokens: Spree.pathFor('solidus_braintree/client_token'),
7
+ transactions: Spree.pathFor('solidus_braintree/transactions')
8
+ },
9
+
10
+ // Override to provide your own error messages.
11
+ braintreeErrorHandle: function(braintreeError) {
12
+ BraintreeError.getErrorFromSlug(braintreeError.code);
13
+ SolidusBraintree.showError(error);
14
+ },
15
+
16
+ classes: {
17
+ hostedForm: function() {
18
+ return SolidusBraintree.HostedForm;
19
+ },
20
+
21
+ client: function() {
22
+ return SolidusBraintree.Client;
23
+ },
24
+
25
+ paypalButton: function() {
26
+ return SolidusBraintree.PaypalButton;
27
+ },
28
+
29
+ paypalMessaging: function() {
30
+ return SolidusBraintree.PaypalMessaging;
31
+ },
32
+
33
+ applepayButton: function() {
34
+ return SolidusBraintree.ApplepayButton;
35
+ },
36
+
37
+ venmoButton: function() {
38
+ return SolidusBraintree.VenmoButton;
39
+ }
40
+ }
41
+ },
42
+
43
+ showError: function(error) {
44
+ var $contentContainer = $("#content");
45
+ var $flash = $("<div class='flash error'>" + error + "</div>");
46
+ $contentContainer.prepend($flash);
47
+ $flash.show().delay(5000).fadeOut(500);
48
+ },
49
+
50
+ createHostedForm: function() {
51
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.hostedForm(), arguments);
52
+ },
53
+
54
+ createClient: function() {
55
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.client(), arguments);
56
+ },
57
+
58
+ createPaypalButton: function() {
59
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.paypalButton(), arguments);
60
+ },
61
+
62
+ createPaypalMessaging: function() {
63
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.paypalMessaging(), arguments);
64
+ },
65
+
66
+ createApplePayButton: function() {
67
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.applepayButton(), arguments);
68
+ },
69
+
70
+ createVenmoButton: function() {
71
+ return SolidusBraintree._factory(SolidusBraintree.config.classes.venmoButton(), arguments);
72
+ },
73
+
74
+ _factory: function(klass, args) {
75
+ var normalizedArgs = Array.prototype.slice.call(args);
76
+ return new (Function.prototype.bind.apply(klass, [null].concat(normalizedArgs)));
77
+ }
78
+ };
79
+
80
+ BraintreeError = {
81
+ DEFAULT: "Something bad happened!",
82
+
83
+ getErrorFromSlug: function(slug) {
84
+ error = BraintreeError.DEFAULT
85
+ if (slug in BraintreeError)
86
+ error = BraintreeError[slug]
87
+ return error
88
+ }
89
+ }
@@ -0,0 +1,14 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require solidus_braintree/constants
8
+ //= require solidus_braintree/promise
9
+ //= require solidus_braintree/client
10
+ //= require solidus_braintree/hosted_form
11
+ //= require solidus_braintree/paypal_button
12
+ //= require solidus_braintree/paypal_messaging
13
+ //= require solidus_braintree/apple_pay_button
14
+ //= require solidus_braintree/venmo_button
@@ -0,0 +1,46 @@
1
+ SolidusBraintree.HostedForm = function(paymentMethodId) {
2
+ this.paymentMethodId = paymentMethodId;
3
+ this.client = null;
4
+ };
5
+
6
+ SolidusBraintree.HostedForm.prototype.initialize = function() {
7
+ this.client = SolidusBraintree.createClient({
8
+ paymentMethodId: this.paymentMethodId,
9
+ useThreeDSecure: (typeof(window.threeDSecureOptions) !== 'undefined'),
10
+ });
11
+
12
+ return this.client.initialize().
13
+ then(this._createHostedFields.bind(this));
14
+ };
15
+
16
+ SolidusBraintree.HostedForm.prototype._createHostedFields = function () {
17
+ if (!this.client) {
18
+ throw new Error("Client not initialized, please call initialize first!");
19
+ }
20
+
21
+ var opts = {
22
+ _solidusClient: this.client,
23
+ client: this.client.getBraintreeInstance(),
24
+
25
+ fields: {
26
+ number: {
27
+ selector: "#card_number" + this.paymentMethodId,
28
+ placeholder: placeholder_text["number"]
29
+ },
30
+
31
+ cvv: {
32
+ selector: "#card_code" + this.paymentMethodId,
33
+ placeholder: placeholder_text["cvv"]
34
+ },
35
+
36
+ expirationDate: {
37
+ selector: "#card_expiry" + this.paymentMethodId,
38
+ placeholder: placeholder_text["expirationDate"]
39
+ }
40
+ },
41
+
42
+ styles: credit_card_fields_style
43
+ };
44
+
45
+ return SolidusBraintree.PromiseShim.convertBraintreePromise(braintree.hostedFields.create, [opts]);
46
+ };
@@ -0,0 +1,178 @@
1
+ //= require solidus_braintree/constants
2
+ /**
3
+ * Constructor for PayPal button object
4
+ * @constructor
5
+ * @param {object} element - The DOM element of your PayPal button
6
+ */
7
+ SolidusBraintree.PaypalButton = function(element, paypalOptions, options) {
8
+ this._element = element;
9
+ this._paypalOptions = paypalOptions || {};
10
+
11
+ this.locale = paypalOptions['locale'] || "en_US";
12
+ this.style = paypalOptions['style'] || {};
13
+ delete paypalOptions['locale'];
14
+ delete paypalOptions['style'];
15
+
16
+ this._options = options || {};
17
+ this._client = null;
18
+ this._environment = this._paypalOptions.environment || 'sandbox';
19
+ delete this._paypalOptions.environment;
20
+
21
+ this._buyerCountry = this._paypalOptions.buyerCountry;
22
+ delete paypalOptions['buyerCountry'];
23
+
24
+ this._enabledFunding = [];
25
+
26
+ if (paypalOptions['venmoFunding']) this._enabledFunding.push('venmo');
27
+ delete paypalOptions['venmoFunding'];
28
+
29
+ if(!this._element) {
30
+ throw new Error("Element for the paypal button must be present on the page");
31
+ }
32
+ };
33
+
34
+ /**
35
+ * Creates the PayPal session using the provided options and enables the button
36
+ *
37
+ * @param {object} options - The options passed to tokenize when constructing
38
+ * the PayPal instance
39
+ *
40
+ * See {@link https://braintree.github.io/braintree-web/3.9.0/PayPal.html#tokenize}
41
+ */
42
+ SolidusBraintree.PaypalButton.prototype.initialize = function() {
43
+ this._client = new SolidusBraintree.createClient({
44
+ useDataCollector: this._paypalOptions.useDataCollector,
45
+ usePaypal: true
46
+ });
47
+
48
+ return this._client.initialize().then(this.initializeCallback.bind(this));
49
+ };
50
+
51
+ SolidusBraintree.PaypalButton.prototype.initializeCallback = function() {
52
+ this._paymentMethodId = this._client.paymentMethodId;
53
+
54
+ var args = {
55
+ "client-id": this._environment === "sandbox" ? "sb" : null,
56
+ currency: this._paypalOptions.currency,
57
+ commit: true,
58
+ vault: this._paypalOptions.flow == "vault",
59
+ components: this.style['messaging'] == "true" && this._paypalOptions.flow != "vault" ? "buttons,messages" : "buttons",
60
+ intent: this._paypalOptions.flow == "vault" ? "tokenize" : "authorize"
61
+ };
62
+
63
+ if (this._environment === "sandbox" && this._buyerCountry) {
64
+ args["buyer-country"] = this._buyerCountry
65
+ }
66
+ if (this._enabledFunding.length !== 0) {
67
+ args["enable-funding"] = this._enabledFunding.join(',');
68
+ }
69
+
70
+ this._client.getPaypalInstance().loadPayPalSDK(args).then(() => {
71
+ var create_method = this._paypalOptions.flow == "vault" ? "createBillingAgreement" : "createOrder"
72
+
73
+ var render_config = {
74
+ style: this.style,
75
+ onClick: (data) => { SolidusBraintree.fundingSource = data.fundingSource },
76
+ [create_method]: function () {
77
+ return this._client.getPaypalInstance().createPayment(this._paypalOptions);
78
+ }.bind(this),
79
+ onApprove: function (data, actions) {
80
+ return this._client.getPaypalInstance().tokenizePayment(data, this._tokenizeCallback.bind(this));
81
+ }.bind(this)
82
+ };
83
+
84
+ paypal.Buttons(render_config).render(this._element);
85
+ })
86
+ };
87
+
88
+ /**
89
+ * Default callback function for when tokenization completes
90
+ *
91
+ * @param {object|null} tokenizeErr - The error returned by Braintree on failure
92
+ * @param {object} payload - The payload returned by Braintree on success
93
+ */
94
+ SolidusBraintree.PaypalButton.prototype._tokenizeCallback = function(tokenizeErr, payload) {
95
+ if (tokenizeErr) {
96
+ SolidusBraintree.config.braintreeErrorHandle(tokenizeErr);
97
+ return;
98
+ }
99
+
100
+ var params = this._transactionParams(payload);
101
+
102
+ return Spree.ajax({
103
+ url: SolidusBraintree.config.paths.transactions,
104
+ type: 'POST',
105
+ dataType: 'json',
106
+ data: params,
107
+ success: function(response) {
108
+ window.location.href = response.redirectUrl;
109
+ },
110
+ error: function(xhr) {
111
+ var errorText = BraintreeError.DEFAULT;
112
+
113
+ if (xhr.responseJSON && xhr.responseJSON.errors) {
114
+ var errors = [];
115
+ $.each(xhr.responseJSON.errors, function(key, values) {
116
+ $.each(values, function(index, value) {
117
+ errors.push(key + " " + value)
118
+ });
119
+ });
120
+
121
+ if (errors.length > 0)
122
+ errorText = errors.join(", ");
123
+ }
124
+
125
+ console.error("Error submitting transaction: " + errorText);
126
+ SolidusBraintree.showError(errorText);
127
+ },
128
+ });
129
+ };
130
+
131
+ /**
132
+ * Builds the transaction parameters to submit to Solidus for the given
133
+ * payload returned by Braintree
134
+ *
135
+ * @param {object} payload - The payload returned by Braintree after tokenization
136
+ */
137
+ SolidusBraintree.PaypalButton.prototype._transactionParams = function(payload) {
138
+ return {
139
+ "payment_method_id" : this._paymentMethodId,
140
+ "options": this._options,
141
+ "transaction" : {
142
+ "email" : payload.details.email,
143
+ "phone" : payload.details.phone,
144
+ "nonce" : payload.nonce,
145
+ "payment_type" : payload.type,
146
+ "paypal_funding_source": SolidusBraintree.fundingSource,
147
+ "address_attributes" : this._addressParams(payload)
148
+ }
149
+ };
150
+ };
151
+
152
+ /**
153
+ * Builds the address parameters to submit to Solidus using the payload
154
+ * returned by Braintree
155
+ *
156
+ * @param {object} payload - The payload returned by Braintree after tokenization
157
+ */
158
+ SolidusBraintree.PaypalButton.prototype._addressParams = function(payload) {
159
+ var name;
160
+ var payload_address = payload.details.shippingAddress || payload.details.billingAddress;
161
+ if (!payload_address) return {};
162
+
163
+ if (payload_address.recipientName) {
164
+ name = payload_address.recipientName
165
+ } else {
166
+ name = payload.details.firstName + " " + payload.details.lastName;
167
+ }
168
+
169
+ return {
170
+ "name" : name,
171
+ "address_line_1" : payload_address.line1,
172
+ "address_line_2" : payload_address.line2,
173
+ "city" : payload_address.city,
174
+ "state_code" : payload_address.state,
175
+ "zip" : payload_address.postalCode,
176
+ "country_code" : payload_address.countryCode
177
+ };
178
+ };
@@ -0,0 +1,22 @@
1
+ //= require solidus_braintree/constants
2
+
3
+ SolidusBraintree.PaypalMessaging = function(paypalOptions) {
4
+ this._paypalOptions = paypalOptions || {};
5
+
6
+ this._client = null;
7
+ };
8
+
9
+ SolidusBraintree.PaypalMessaging.prototype.initialize = function() {
10
+ this._client = new SolidusBraintree.createClient({usePaypal: true});
11
+
12
+ return this._client.initialize().then(this.initializeCallback.bind(this));
13
+ };
14
+
15
+ SolidusBraintree.PaypalMessaging.prototype.initializeCallback = function() {
16
+ this._paymentMethodId = this._client.paymentMethodId;
17
+
18
+ this._client.getPaypalInstance().loadPayPalSDK({
19
+ currency: this._paypalOptions.currency,
20
+ components: "messages"
21
+ })
22
+ };
@@ -0,0 +1,20 @@
1
+ SolidusBraintree.PromiseShim = {
2
+ convertBraintreePromise: function(fn, args, context) {
3
+ var jqPromise = $.Deferred();
4
+
5
+ args = args || [];
6
+ context = context || this;
7
+
8
+ args = args.concat(function(error, data) {
9
+ if (error) {
10
+ jqPromise.reject(error);
11
+ } else {
12
+ jqPromise.resolve(data);
13
+ }
14
+ });
15
+
16
+ fn.apply(context, args);
17
+
18
+ return jqPromise.promise();
19
+ }
20
+ }
@@ -0,0 +1,86 @@
1
+ //= require solidus_braintree/constants
2
+ /**
3
+ * Constructor for Venmo button object
4
+ * @constructor
5
+ * @param {object} element - The DOM element of your Venmo button
6
+ */
7
+ SolidusBraintree.VenmoButton = function(element, venmoOptions) {
8
+ this._element = element;
9
+ this._client = null;
10
+ this._venmoOptions = venmoOptions || {};
11
+
12
+ if(!this._element) {
13
+ throw new Error("Element for the Venmo button must be present on the page");
14
+ }
15
+ };
16
+
17
+ /**
18
+ * Creates the Venmo session using the provided options and enables the button
19
+ *
20
+ * @param {object} options - The options passed to tokenize when constructing
21
+ * the Venmo instance
22
+ *
23
+ * See {@link https://braintree.github.io/braintree-web/3.84.0/module-braintree-web_venmo.html#.create}
24
+ */
25
+ SolidusBraintree.VenmoButton.prototype.initialize = function() {
26
+ this._client = new SolidusBraintree.createClient({
27
+ useVenmo: true,
28
+ newBrowserTabSupported: this._venmoOptions.newBrowserTabSupported,
29
+ flow: this._venmoOptions.flow
30
+ });
31
+
32
+ return this._client.initialize().then(this.initializeCallback.bind(this));
33
+ };
34
+
35
+ SolidusBraintree.VenmoButton.prototype.initializeCallback = function() {
36
+ this._venmoInstance = this._client.getVenmoInstance();
37
+
38
+ this._element.classList.add('visible');
39
+
40
+ // Check if tokenization results already exist. This occurs when your
41
+ // checkout page is relaunched in a new tab.
42
+ if (!this._venmoOptions.newBrowserTabSupported && this._venmoInstance.hasTokenizationResult()) {
43
+ this.tokenize();
44
+ }
45
+
46
+ this._element.addEventListener('click', function(event) {
47
+ event.preventDefault();
48
+ this._element.disabled = true;
49
+ this.initializeVenmoSession();
50
+ }.bind(this), false);
51
+ };
52
+
53
+ SolidusBraintree.VenmoButton.prototype.initializeVenmoSession = function() {
54
+ this.tokenize();
55
+ };
56
+
57
+ SolidusBraintree.VenmoButton.prototype.tokenize = function() {
58
+ var venmoButton = this._element;
59
+ this._venmoInstance.tokenize().then(handleVenmoSuccess).catch(handleVenmoError).then(function () {
60
+ venmoButton.removeAttribute('disabled');
61
+ });
62
+ };
63
+
64
+ function handleVenmoSuccess(payload) {
65
+ var $paymentForm = $("#checkout_form_payment");
66
+ var $nonceField = $("#venmo_payment_method_nonce", $paymentForm);
67
+
68
+ // Disable hostedFields' and enable Venmo's inputs as they use the same fields.
69
+ // Otherwise, they will clash. (Disabled inputs are not used on form submission)
70
+ $('.hosted-fields input').each(function(_index, input) {
71
+ input.disabled = true;
72
+ });
73
+ $('.venmo-fields input').each(function(_index, input) {
74
+ input.removeAttribute('disabled');
75
+ });
76
+
77
+ // remove hostedFields submit listener, otherwise empty credit card errors occur
78
+ $paymentForm.off('submit');
79
+
80
+ $nonceField.val(payload.nonce);
81
+ $paymentForm.submit();
82
+ }
83
+
84
+ function handleVenmoError(error) {
85
+ SolidusBraintree.config.braintreeErrorHandle(error);
86
+ }