spree_emerchantpay_genesis 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +52 -0
  3. data/LICENSE +21 -0
  4. data/README.md +269 -0
  5. data/Rakefile +50 -0
  6. data/app/assets/images/spree/emerchantpay_logo.png +0 -0
  7. data/app/assets/javascripts/spree/emerchantpay_threeds.js +126 -0
  8. data/app/assets/javascripts/spree/frontend/card.min.js +3 -0
  9. data/app/assets/stylesheets/spree/emerchantpay_threeds.css +40 -0
  10. data/app/assets/stylesheets/spree/frontend/card.css +35 -0
  11. data/app/controllers/spree/api/v2/storefront/checkout_controller_decorator.rb +66 -0
  12. data/app/controllers/spree/api/v2/storefront/emerchantpay_notification_controller.rb +47 -0
  13. data/app/controllers/spree/api/v2/storefront/emerchantpay_threeds_controller.rb +61 -0
  14. data/app/controllers/spree/emerchantpay_threeds_controller.rb +31 -0
  15. data/app/helpers/spree/admin/payment_methods_helper.rb +110 -0
  16. data/app/helpers/spree_emerchantpay_genesis/mappers/genesis.rb +332 -0
  17. data/app/helpers/spree_emerchantpay_genesis/mappers/order.rb +70 -0
  18. data/app/helpers/spree_emerchantpay_genesis/mappers/transaction.rb +45 -0
  19. data/app/helpers/spree_emerchantpay_genesis/threeds_helper.rb +116 -0
  20. data/app/helpers/spree_emerchantpay_genesis/transaction_helper.rb +200 -0
  21. data/app/models/spree/gateway/emerchantpay_direct.rb +66 -0
  22. data/app/models/spree/payment_decorator.rb +13 -0
  23. data/app/models/spree/payment_processing_decorator.rb +28 -0
  24. data/app/models/spree_emerchantpay_genesis/base/data.rb +33 -0
  25. data/app/models/spree_emerchantpay_genesis/base/gateway.rb +65 -0
  26. data/app/models/spree_emerchantpay_genesis/data/address.rb +23 -0
  27. data/app/models/spree_emerchantpay_genesis/data/provider.rb +23 -0
  28. data/app/models/spree_emerchantpay_genesis/data/user.rb +32 -0
  29. data/app/models/spree_emerchantpay_genesis/db/application_record.rb +10 -0
  30. data/app/models/spree_emerchantpay_genesis/db/emerchantpay_payment.rb +35 -0
  31. data/app/models/spree_emerchantpay_genesis/genesis_provider.rb +203 -0
  32. data/app/repositories/spree_emerchantpay_genesis/emerchantpay_payments_repository.rb +120 -0
  33. data/app/repositories/spree_emerchantpay_genesis/spree_order_repository.rb +51 -0
  34. data/app/repositories/spree_emerchantpay_genesis/spree_payments_repository.rb +58 -0
  35. data/app/services/spree/payments/create_decorator.rb +63 -0
  36. data/app/services/spree_emerchantpay_genesis/base/payment_service.rb +83 -0
  37. data/app/services/spree_emerchantpay_genesis/notifications/service_handler.rb +33 -0
  38. data/app/services/spree_emerchantpay_genesis/sources/create_credit_card.rb +47 -0
  39. data/app/services/spree_emerchantpay_genesis/threeds/callback.rb +62 -0
  40. data/app/services/spree_emerchantpay_genesis/threeds/method_continue.rb +57 -0
  41. data/app/views/layouts/spree/method_continue.html.erb +17 -0
  42. data/app/views/spree/admin/payments/source_views/_emerchantpay_direct.html.erb +80 -0
  43. data/app/views/spree/checkout/payment/_emerchantpay_direct.html.erb +51 -0
  44. data/app/views/spree/emerchantpay_threeds/method_continue.html.erb +26 -0
  45. data/config/locales/en.yml +45 -0
  46. data/config/routes.rb +24 -0
  47. data/db/migrate/20230927072418_add_emerchantpay_payments.rb +34 -0
  48. data/db/migrate/20231128153140_add_callback_status_to_emerchantpay_payments.rb +8 -0
  49. data/lib/generators/spree_emerchantpay_genesis/install/install_generator.rb +37 -0
  50. data/lib/spree_emerchantpay_genesis/engine.rb +38 -0
  51. data/lib/spree_emerchantpay_genesis/factories.rb +11 -0
  52. data/lib/spree_emerchantpay_genesis/version.rb +7 -0
  53. data/lib/spree_emerchantpay_genesis.rb +8 -0
  54. metadata +421 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f3f946f18fcb121abeb5227927b4d8480f73aedfd549cca1eae4a069a322b34b
4
+ data.tar.gz: 675f4efd4755f39eca482ef49dcb4c75555eb98e8cd89217cd2b713f90074d59
5
+ SHA512:
6
+ metadata.gz: eda4500c230e18a04b44eb67613c7911666e27730dd8f9eafc19fe6e43d636673479b3f47c9762465ee839ccbff026acb2e4b6dad5cde36f0b7fd07c2fff216f
7
+ data.tar.gz: 958cdcf91ac6ce0cd617e14d17c8cc09bde302fb829197e1b74c77ff9829f8e961b5847c419710b2372149baf1b1e72eb4956b0ce7ffaaa7e94121304af46895
data/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ 0.1.3
2
+ -----
3
+ **Features**
4
+
5
+ * Added tests to the library
6
+ * Added Appraisals for Spree 4.4 and Spree main GitHub Branch
7
+ * Added `spree_emerchantpay_genesis` to [RubyGems](https://rubygems.org/gems/spree_emerchantpay_genesis)
8
+
9
+ **Fixes**:
10
+
11
+ * Fixed 3DSv2 parameters handling
12
+ * Fixed engine installation without Spree Rails Frontend
13
+ * Fixed minor issues
14
+
15
+ 0.1.2
16
+ -----
17
+
18
+ **Features**:
19
+
20
+ * Updated project license to MIT
21
+ * Added support for the following transaction types:
22
+ * Authorize 3D
23
+ * Sale 3D
24
+ * Added 3DSv2 parameters support to the 3D payments
25
+ * Added support for 3DSv2 payment flow
26
+ * Added `emerchantpay_payment` inside Spree API V2 Create Payment response containing payment state and redirect_url
27
+ * Added Gateway Notifications handling used for asynchronous payments
28
+ * Updated Genesis Ruby SDK to version 0.1.3
29
+
30
+ 0.1.1
31
+ -----
32
+
33
+ **Features**:
34
+
35
+ * Added support for the following reference payment actions via Genesis Gateway:
36
+ * Capture
37
+ * Void
38
+ * Refund
39
+
40
+ 0.1.0
41
+ -----
42
+
43
+ **Features**:
44
+
45
+ * Added initial code base for emerchantpay Gateway Module for Spree payment method
46
+ * Added EmerchantpayDirect Spree Gateway
47
+ * Added Spree Payment Creation decoration
48
+ * Added Spree Payment Methods settings decorator
49
+ * Added Payment Source View decorator
50
+ * Added Spree V2 Checkout controller decorator
51
+ * Added Emerchantpay Payments database migration
52
+ * Added Spree Payment Processing decorator
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 emerchantpay ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,269 @@
1
+ # emerchantpay Genesis Gateway for Spree
2
+ This is a Payment Module for Spree eCommerce that gives you the ability to process payments through emerchantpay's Payment Gateway - Genesis.
3
+
4
+ # Requirements
5
+ * Spree Core 4.x (Tested up to 4.4.0)
6
+ * Spree Backend 4.x (Tested up to 4.4.0)
7
+ * Spree FrontEnd - Optional (Tested up to 4.4.0)
8
+ * Ruby >= 2.7
9
+ * Ruby on Rails >= 6.1.4
10
+ * [GenesisRuby v0.1.3](https://github.com/GenesisGateway/genesis_ruby/releases/tag/0.1.3)
11
+ * PCI-certified server in order to use emerchantpay Direct
12
+
13
+ ## Installation
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'spree_emerchantpay_genesis'
18
+ ```
19
+
20
+ And then execute:
21
+ ```bash
22
+ bundle
23
+ ```
24
+
25
+ Or install it yourself as:
26
+ ```bash
27
+ gem install spree_emerchantpay_genesis
28
+ ```
29
+
30
+ Copy & run migrations
31
+ ```bash
32
+ bundle exec rails g spree_emerchantpay_genesis:install
33
+ ```
34
+
35
+ Restart your server
36
+
37
+ ## Create Payment Method
38
+ * Sign in to Spree Admin BackEnd
39
+ * Navigate to Configurations -> Payment Methods -> New Payment Method
40
+ * For Provider `Choose Spree::Gateway::Emerchantpay*`
41
+ * Fill in Name, Description and Stores
42
+ * Click Create
43
+
44
+ # Usage
45
+
46
+ ## Configuration
47
+ * Navigate to the `Spree::Gateway::Emerchantpay*` payment method in the Configurations
48
+ * Fill in Username, Password and Token
49
+ * Fill in `hostname` used for the generation of the notification webhook. In most cases, the hostname should be the hostname of the Spree backend.
50
+ * Fill in `return_success_url` and `return_failure_url`. Those endpoints will be returned to the `create_payment` response
51
+ * If you add `|:ORDER:|` pattern in the URLs. The pattern will be replaced with the Spree Order Number
52
+
53
+ ## Spree Storefront API V2
54
+
55
+ 1. Create Cart
56
+
57
+ ```bash
58
+ curl --request 'POST' \
59
+ --url 'http://127.0.0.1:4000/api/v2/storefront/cart'
60
+ ```
61
+
62
+ Response:
63
+
64
+ ```text
65
+ {
66
+ "data":{
67
+ "id":"59",
68
+ "type":"cart",
69
+ "attributes":{
70
+ "number":"R702094375",
71
+ ...
72
+ "currency":"USD",
73
+ "state":"cart",
74
+ "token":"EsDjq1oXEgKI6kuujgfvFw1694531383712",
75
+ ...
76
+ },
77
+ ...
78
+ }
79
+ }
80
+ ```
81
+
82
+ 2. Add Items
83
+ <details>
84
+ <summary>List Products</summary>
85
+
86
+ ```bash
87
+ curl --request 'GET' \
88
+ --url 'https://demo.spreecommerce.org/api/v2/storefront/products' \
89
+ --header 'Accept: application/vnd.api+json'
90
+ ```
91
+
92
+ </details>
93
+
94
+ ```bash
95
+ curl --request 'POST' \
96
+ --url 'http://localhost:4000/api/v2/storefront/cart/add_item' \
97
+ --header 'Accept: application/vnd.api+json' \
98
+ --header 'X-Spree-Order-Token: EsDjq1oXEgKI6kuujgfvFw1694531383712' \
99
+ --header 'Content-Type: application/vnd.api+json' \
100
+ --data '{
101
+ "variant_id": "130",
102
+ "quantity": "1"
103
+ }'
104
+ ```
105
+
106
+ 3. Add Shipping
107
+ <details>
108
+ <summary>List Shipping Rates</summary>
109
+
110
+ ```bash
111
+ curl --request 'GET' \
112
+ --url 'https://demo.spreecommerce.org/api/v2/storefront/checkout/shipping_rates' \
113
+ --header 'Accept: application/vnd.api+json' \
114
+ --header 'X-Spree-Order-Token: EsDjq1oXEgKI6kuujgfvFw1694531383712'
115
+ ```
116
+
117
+ </details>
118
+
119
+ ```bash
120
+ curl --request 'PATCH' \
121
+ --url 'http://localhost:4000/api/v2/storefront/checkout/select_shipping_method' \
122
+ --header 'Accept: application/vnd.api+json' \
123
+ --header 'X-Spree-Order-Token: EsDjq1oXEgKI6kuujgfvFw1694531383712' \
124
+ --header 'Content-Type: application/vnd.api+json' \
125
+ --data '{
126
+ "shipping_method_id": "1"
127
+ }'
128
+ ```
129
+
130
+ 4. Update Order
131
+ ```bash
132
+ curl --request 'PATCH' \
133
+ --header 'Accept: application/vnd.api+json' \
134
+ --header 'X-Spree-Order-Token: EsDjq1oXEgKI6kuujgfvFw1694531383712' \
135
+ --header 'Content-Type: application/vnd.api+json' \
136
+ --url 'http://localhost:4000/api/v2/storefront/checkout' \
137
+ --data '{
138
+ "order": {
139
+ "email": "john.smith@example.com",
140
+ "bill_address_attributes": {
141
+ "firstname": "John",
142
+ "lastname": "Smith",
143
+ "address1": "1 Area",
144
+ "city": "Louisville",
145
+ "phone": "01234567891",
146
+ "zipcode": "40202",
147
+ "country_iso": "US",
148
+ "state_id": 500
149
+ },
150
+ "ship_address_attributes": {
151
+ "firstname": "John",
152
+ "lastname": "Smith",
153
+ "address1": "1 Area",
154
+ "city": "Louisville",
155
+ "phone": "01234567891",
156
+ "zipcode": "40202",
157
+ "country_iso": "US",
158
+ "state_id": 500
159
+ },
160
+ "payments_attributes":[
161
+ {
162
+ "payment_method_id":"5"
163
+ }
164
+ ]
165
+ },
166
+ "payment_source": {
167
+ "5": {
168
+ "name": "John Smith",
169
+ "number": "4200000000000000",
170
+ "month": 1,
171
+ "year": 2040,
172
+ "verification_value": "123",
173
+ "cc_type": "visa"
174
+ }
175
+ }
176
+ }'
177
+ ```
178
+ 5. Create Payment
179
+ **CAUTION** Create Payment endpoint will Complete the order! Call this endpoint in order to finish the order!
180
+ Accept Header, Java Enabled, Language, Color Depth, Screen height, Screen Width, Time Zone Offset, User Agent parameters must be retrieved from the customer browser. More info [here](https://emerchantpay.github.io/gateway-api-docs/?shell#3ds-v2-request-params).
181
+
182
+ ```bash
183
+ curl --request 'POST' \
184
+ --header 'Accept: application/vnd.api+json' \
185
+ --header 'X-Spree-Order-Token: EsDjq1oXEgKI6kuujgfvFw1694531383712' \
186
+ --header 'Content-Type: application/vnd.api+json' \
187
+ --url 'http://localhost:4000/api/v2/storefront/checkout/create_payment' \
188
+ --data '{
189
+ "payment_method_id": "5",
190
+ "source_attributes": {
191
+ "name": "John Smith",
192
+ "number": "4200000000000000",
193
+ "month": 1,
194
+ "year": 2040,
195
+ "verification_value": "123",
196
+ "cc_type": "visa",
197
+ "accept_header": "*/*",
198
+ "java_enabled": "true",
199
+ "language": "en-GB",
200
+ "color_depth": "32",
201
+ "screen_height": "400",
202
+ "screen_width": "400",
203
+ "time_zone_offset": "+0",
204
+ "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"
205
+ }
206
+ }'
207
+ ```
208
+
209
+ Response:
210
+ **CAUTION** If a redirect URL exists in the response object you MUST redirect the customer for payment completion
211
+
212
+ Create Payment response will contain `emerchantpay_payment` object. It will contain the current status of the payment.
213
+ Redirect URL will give you the next step.
214
+
215
+ States:
216
+ * error or declined - redirect URL will be the Failure URL filled in plugin settings
217
+ * approved - redirect URL will be the Success URL filled in the plugin settings
218
+ * pending_async - redirect URL will be the 3DSecure Method Continue endpoint for the next step of the payment
219
+
220
+ ```json
221
+ {
222
+ "data": {
223
+ "id": "XXX",
224
+ "type": "cart",
225
+ "attributes": {...},
226
+ "relationships": {...},
227
+ "emerchantpay_payment": {
228
+ "state": "pending_async",
229
+ "redirect_url": "<customer-redirect-url>"
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ ## Reference Actions
236
+ **CAUTION** the following transaction actions must be executed via Spree Admin backend:
237
+ * Capture
238
+ * Refund
239
+ * Void
240
+
241
+ ## Contributing
242
+ Contribution directions go here.
243
+
244
+ ## Development
245
+
246
+ ### Running Specs
247
+
248
+ `rake test`
249
+
250
+ ### Running Linters
251
+
252
+ `rake styles`
253
+
254
+ ### Appraisals
255
+
256
+ #### Spree 4.4
257
+
258
+ `bundle exec appraisal spree-4.4 rake test`
259
+
260
+ #### Spree Master
261
+
262
+ `bundle exec appraisal spree-master rake test`
263
+
264
+ #### Configure
265
+
266
+ `bundle exec appraisal install`
267
+
268
+ ## License
269
+ The gem is available as open source under the terms of the [GPL-2.0 License](https://opensource.org/license/gpl-2-0/).
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ require 'spree/testing_support/extension_rake'
6
+
7
+ desc 'Generates a dummy app for testing'
8
+ task :test_app do
9
+ ENV['LIB_NAME'] = 'spree_emerchantpay_genesis'
10
+ Rake::Task['extension:test_app'].invoke
11
+ end
12
+
13
+ RSpec::Core::RakeTask.new(:spec)
14
+ RSpec::Core::RakeTask.new(:spec_junit) do |t|
15
+ t.rspec_opts = ['--format RspecJunitFormatter', '--out rspec_report.xml']
16
+ t.pattern = '**/*_spec.rb'
17
+ end
18
+
19
+ task :rspec, [:format] do |_task, args|
20
+ if Dir['spec/dummy'].empty?
21
+ Rake::Task[:test_app].invoke
22
+ Dir.chdir('../../')
23
+ end
24
+
25
+ case args.format
26
+ when 'junit'
27
+ Rake::Task[:spec_junit].invoke
28
+ else
29
+ Rake::Task[:spec].invoke
30
+ end
31
+ end
32
+
33
+ require 'rubocop/rake_task'
34
+ RuboCop::RakeTask.new(:rubocop_default)
35
+ RuboCop::RakeTask.new(:rubocop_progress) do |t|
36
+ t.formatters = %w(progress)
37
+ t.options = %w(--out rubocop_report.txt)
38
+ end
39
+
40
+ task :test do
41
+ Rake::Task[:rspec].invoke('')
42
+ end
43
+
44
+ task :test_junit do
45
+ Rake::Task[:rspec].invoke('junit')
46
+ end
47
+
48
+ task default: %i[test rubocop_default]
49
+ task styles: :rubocop_default
50
+ task style_progress: :rubocop_progress
@@ -0,0 +1,126 @@
1
+ (function(document) {
2
+ document.body.onload = function (){
3
+ empThreedsSecureMethod('threedsMethodForm').execute()
4
+ }
5
+ })(document);
6
+
7
+ const empThreedsSecureMethod = function(form_id) {
8
+ let interval = null
9
+ let delay = 500
10
+ let retries = 0
11
+ let max_retries = 12
12
+ let status_complete = 'completed'
13
+ let internal_error = 'internal_error'
14
+
15
+ // ThreedsForm HTML Element
16
+ const threedsForm = function() {
17
+ return document.getElementById(form_id)
18
+ }
19
+
20
+ // 3DSv2 Callback Status endpoint
21
+ const getCallbackStatusUrl = function() {
22
+ return threedsForm().dataset.statusUrl
23
+ }
24
+
25
+ // 3DSv2 Method Continue endpoint
26
+ const getMethodContinueUrl = function() {
27
+ return threedsForm().dataset.methodSecureUrl
28
+ }
29
+
30
+ const getFailureUrl = function() {
31
+ return threedsForm().dataset.failureUrl
32
+ }
33
+
34
+ // Execute 3DSv2 submission
35
+ const submitThreedsMethod = function(){
36
+ threedsForm().submit()
37
+ startLoop()
38
+ }
39
+
40
+ // 3DSv2 Secure Method Handler
41
+ const startLoop = function() {
42
+ initInterval(function() {
43
+ checkCallbackStatus(function(data) {
44
+ handleInternalError(data)
45
+
46
+ if (retries >= max_retries || data.status === status_complete) {
47
+ clearInterval(interval)
48
+
49
+ sendBackEndData(function(data) {
50
+ handleInternalError(data)
51
+
52
+ redirectTo(data.redirect_url)
53
+ })
54
+ }
55
+
56
+ retries++;
57
+ })
58
+ })
59
+ }
60
+
61
+ const handleInternalError = function(data) {
62
+ if (data.status === internal_error) {
63
+ clearInterval(interval)
64
+ redirectTo(getFailureUrl())
65
+ }
66
+ }
67
+
68
+ // Redirect to the given url
69
+ const redirectTo = function(url) {
70
+ parent.location.href = url
71
+ }
72
+
73
+ // Initialize the Interval
74
+ const initInterval = function(callback) {
75
+ interval = setInterval(callback, delay)
76
+ }
77
+
78
+ // Check Callback status
79
+ const checkCallbackStatus = function(callback) {
80
+ ajaxCall(
81
+ { method: 'GET', url: getCallbackStatusUrl(), async: false },
82
+ (data) => { callback(data) },
83
+ (status, response) => { callback({ status: internal_error }) }
84
+ )
85
+ }
86
+
87
+ function sendBackEndData(callback) {
88
+ ajaxCall(
89
+ { method: 'POST', url: getMethodContinueUrl(), async: false, form_data: new FormData(threedsForm()) },
90
+ (data) => { callback(data) },
91
+ (status, response) => { callback({ status: internal_error }) }
92
+ )
93
+ }
94
+
95
+ const ajaxCall = function(options, success, failure) {
96
+ let xhr = new XMLHttpRequest()
97
+ xhr.open(options.method, options.url, options.async)
98
+
99
+ try {
100
+ xhr.onload = function() {
101
+ if (xhr.status === 200) {
102
+ return success(parseData(xhr.responseText))
103
+ }
104
+
105
+ return failure(xhr.status, xhr.responseText)
106
+ };
107
+
108
+ xhr.onerror = () => { failure(500, 'Network Error') }
109
+
110
+ xhr.send(options.form_data)
111
+ } catch(err) {
112
+ failure(0, 'System error')
113
+ }
114
+ }
115
+
116
+ const parseData = function(data) {
117
+ try {
118
+ return JSON.parse(data)
119
+ } catch (error) {
120
+ return {}
121
+ }
122
+ }
123
+
124
+
125
+ return { execute: submitThreedsMethod }
126
+ };