workarea-afterpay 2.0.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +23 -0
- data/README.md +20 -43
- data/Rakefile +4 -4
- data/app/controllers/workarea/storefront/afterpay_controller.rb +1 -1
- data/app/models/workarea/payment/authorize/afterpay.rb +54 -1
- data/app/models/workarea/payment/capture/afterpay.rb +21 -1
- data/app/models/workarea/payment/purchase/afterpay.rb +46 -1
- data/app/models/workarea/payment/refund/afterpay.rb +16 -4
- data/app/models/workarea/payment.decorator +0 -12
- data/app/services/workarea/afterpay/order_builder.rb +8 -2
- data/app/view_models/workarea/storefront/afterpay_view_model.rb +4 -18
- data/app/view_models/workarea/storefront/product_view_model.decorator +6 -9
- data/config/locales/en.yml +5 -0
- data/lib/workarea/afterpay/bogus_gateway.rb +24 -9
- data/lib/workarea/afterpay/gateway.rb +45 -8
- data/lib/workarea/afterpay/response.rb +6 -1
- data/lib/workarea/afterpay/version.rb +1 -1
- data/lib/workarea/afterpay.rb +3 -1
- data/test/integration/workarea/storefront/afterpay_integration_test.rb +6 -6
- data/test/models/workarea/payment/afterpay_payment_integration_test.rb +51 -10
- data/test/services/workarea/afterpay/order_builder_test.rb +3 -4
- data/test/view_models/workarea/storefront/afterpay_view_model_test.rb +9 -23
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d2341faa66d7dc64d2f2e35c09eb19e50d03510df064837d33e6e58b59d3e88
|
4
|
+
data.tar.gz: ae475910f6cf7a6a7cca2064be7ebdd9054cc4d56f0a88342650cbc4546ab6f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b42ac45e815c6cfba6dece3000240245bca5264adb0517492a9ede0c1113a1610455aa8b1e98374de5aebbcefc5d667aadc4ee697374af5985fae06520a19b8a
|
7
|
+
data.tar.gz: b02488f73667dcb0a76b33c1e899fb46e2617299837b46f9035e5434179c165d87cb36991c0f66c5d0fc957f03c4ec731f98d79c445469eb99ae9f0e3c37ce2d
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
Workarea Afterpay 2.1.0 (2019-11-12)
|
2
|
+
--------------------------------------------------------------------------------
|
3
|
+
|
4
|
+
* Update Afterpay API version to V2
|
5
|
+
|
6
|
+
New API version allows for a traditional auth then capture
|
7
|
+
model of handling payments. Updates also include idempotent
|
8
|
+
payment operations
|
9
|
+
Jeff Yucis
|
10
|
+
|
11
|
+
* Update Afterpay API version to V2
|
12
|
+
|
13
|
+
New API version allows for a traditional auth then capture
|
14
|
+
model of handling payments. Updates also include idempotent
|
15
|
+
payment operations
|
16
|
+
Jeff Yucis
|
17
|
+
|
18
|
+
* Update README
|
19
|
+
|
20
|
+
Matt Duffy
|
21
|
+
|
22
|
+
|
23
|
+
|
1
24
|
Workarea Afterpay 2.0.2 (2019-08-21)
|
2
25
|
--------------------------------------------------------------------------------
|
3
26
|
|
data/README.md
CHANGED
@@ -18,34 +18,30 @@ Workarea Afterpay orders use the following flow:
|
|
18
18
|
3. User clicks place order button - an api call is made to get a token.
|
19
19
|
4. If token creation is successful the token is injected into the DOM and the user is redirected to the Afterpay site.
|
20
20
|
5. User enters payment details and submits payment.
|
21
|
-
6. User is taken back to Workarea and payment is captured.
|
21
|
+
6. User is taken back to Workarea and payment is authorized or captured.
|
22
22
|
7. Order confirmation page is displayed.
|
23
23
|
|
24
|
-
|
24
|
+
Afterpay API documentation can be found here: https://docs.afterpay.com/online-api-v2-b857508478e7.html
|
25
25
|
|
26
26
|
Implementation Notes
|
27
27
|
--------------------------------------------------------------------------------
|
28
28
|
|
29
|
-
**Payment**
|
30
|
-
|
31
|
-
Afterpay requires that all payments are captured when the order is placed. All Afterpay
|
32
|
-
payments will perform a "Purchase" action instead of the default "Authorize"; meaning that Afterpay payments will be "captured" immediately. Other payment tenders will still behave as configured.
|
33
|
-
|
34
|
-
|
35
29
|
**Product Detail Pages**
|
36
30
|
|
37
31
|
This integration makes use of the ```storefront.product_pricing_details``` append point to display the Afterpay pricing on the product detail page. Some custom PDP templates may not have this append point.
|
38
32
|
|
39
33
|
If you wish for the Afterpay pricing to appear on your custom PDP template simply add the append point manually by adding the following:
|
40
34
|
|
41
|
-
|
35
|
+
```ruby
|
36
|
+
= append_partials('storefront.product_pricing_details', product: product)
|
37
|
+
```
|
42
38
|
|
43
39
|
|
44
40
|
**Testing**
|
45
41
|
|
46
42
|
The test API endpoints will be used by default. Production mode can be triggered by setting the ***test*** configuration value in an initializer to ***false***.
|
47
43
|
|
48
|
-
```
|
44
|
+
```ruby
|
49
45
|
config.afterpay.test = false
|
50
46
|
```
|
51
47
|
|
@@ -86,46 +82,27 @@ The regional US and Australian API endpoints require separate credentials. You c
|
|
86
82
|
Getting Started
|
87
83
|
--------------------------------------------------------------------------------
|
88
84
|
|
89
|
-
|
90
|
-
|
91
|
-
To access Workarea gems and source code, you must be an employee of WebLinc or a licensed retailer or partner.
|
92
|
-
|
93
|
-
Workarea gems are hosted privately at https://gems.weblinc.com/.
|
94
|
-
You must have individual or team credentials to install gems from this server. Add your gems server credentials to Bundler:
|
95
|
-
|
96
|
-
bundle config gems.weblinc.com my_username:my_password
|
85
|
+
Add the gem to your application's Gemfile:
|
97
86
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# ...
|
105
|
-
gem 'workarea-afterpay', source: 'https://gems.weblinc.com'
|
106
|
-
# ...
|
107
|
-
|
108
|
-
Or use a source block:
|
109
|
-
|
110
|
-
# ...
|
111
|
-
source 'https://gems.weblinc.com' do
|
112
|
-
gem 'workarea-afterpay'
|
113
|
-
end
|
114
|
-
# ...
|
87
|
+
```ruby
|
88
|
+
# ...
|
89
|
+
gem 'workarea-afterpay'
|
90
|
+
# ...
|
91
|
+
```
|
115
92
|
|
116
93
|
Update your application's bundle.
|
117
94
|
|
118
|
-
|
119
|
-
|
95
|
+
```bash
|
96
|
+
cd path/to/application
|
97
|
+
bundle
|
98
|
+
```
|
120
99
|
|
121
|
-
Workarea
|
100
|
+
Workarea Commerce Documentation
|
122
101
|
--------------------------------------------------------------------------------
|
123
102
|
|
124
|
-
See [
|
103
|
+
See [https://developer.workarea.com](https://developer.workarea.com) for Workarea Commerce documentation.
|
125
104
|
|
126
|
-
|
105
|
+
License
|
127
106
|
--------------------------------------------------------------------------------
|
128
107
|
|
129
|
-
|
130
|
-
|
131
|
-
For licensing, contact sales@workarea.com.
|
108
|
+
Workarea Afterpay is released under the [Business Software License](LICENSE)
|
data/Rakefile
CHANGED
@@ -34,10 +34,10 @@ desc "Release version #{Workarea::Afterpay::VERSION} of the gem"
|
|
34
34
|
task :release do
|
35
35
|
host = "https://#{ENV['BUNDLE_GEMS__WEBLINC__COM']}@gems.weblinc.com"
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
Rake::Task['workarea:changelog'].execute
|
38
|
+
system 'git add CHANGELOG.md'
|
39
|
+
system 'git commit -m "Update CHANGELOG"'
|
40
|
+
system 'git push origin HEAD'
|
41
41
|
|
42
42
|
system "git tag -a v#{Workarea::Afterpay::VERSION} -m 'Tagging #{Workarea::Afterpay::VERSION}'"
|
43
43
|
system 'git push --tags'
|
@@ -55,7 +55,7 @@ module Workarea
|
|
55
55
|
ap_order_details = gateway.get_order(params[:orderToken])
|
56
56
|
tender = payment.afterpay
|
57
57
|
|
58
|
-
unless (ap_order_details.body["
|
58
|
+
unless (ap_order_details.body["amount"]["amount"].to_m == tender.amount.to_m && current_checkout.complete?)
|
59
59
|
flash[:error] = t('workarea.storefront.afterpay.payment_error')
|
60
60
|
payment.clear_afterpay
|
61
61
|
redirect_to(checkout_payment_path) && (return)
|
@@ -1,7 +1,60 @@
|
|
1
1
|
module Workarea
|
2
2
|
class Payment
|
3
3
|
module Authorize
|
4
|
-
|
4
|
+
class Afterpay
|
5
|
+
include OperationImplementation
|
6
|
+
include CreditCardOperation
|
7
|
+
include AfterpayPaymentGateway
|
8
|
+
|
9
|
+
def complete!
|
10
|
+
response = authorize
|
11
|
+
if response.success?
|
12
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
13
|
+
true,
|
14
|
+
I18n.t(
|
15
|
+
'workarea.afterpay.authorize',
|
16
|
+
amount: transaction.amount
|
17
|
+
),
|
18
|
+
response.body
|
19
|
+
)
|
20
|
+
else
|
21
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
22
|
+
false,
|
23
|
+
I18n.t('workarea.afterpay.capture_failure'),
|
24
|
+
response.body
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def cancel!
|
30
|
+
return unless transaction.success?
|
31
|
+
|
32
|
+
payment_id = transaction.response.params["id"]
|
33
|
+
response = gateway.void(payment_id)
|
34
|
+
|
35
|
+
transaction.cancellation = ActiveMerchant::Billing::Response.new(
|
36
|
+
true,
|
37
|
+
I18n.t('workarea.afterpay.void'),
|
38
|
+
response.body
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def authorize
|
45
|
+
request_id = SecureRandom.uuid
|
46
|
+
auth_response = response(request_id)
|
47
|
+
if Workarea::Afterpay::RETRY_ERROR_STATUSES.include? auth_response.status
|
48
|
+
return response(request_id)
|
49
|
+
end
|
50
|
+
|
51
|
+
auth_response
|
52
|
+
end
|
53
|
+
|
54
|
+
def response(request_id)
|
55
|
+
gateway.authorize(tender.token, tender.payment.id, request_id)
|
56
|
+
end
|
57
|
+
end
|
5
58
|
end
|
6
59
|
end
|
7
60
|
end
|
@@ -7,7 +7,7 @@ module Workarea
|
|
7
7
|
include AfterpayPaymentGateway
|
8
8
|
|
9
9
|
def complete!
|
10
|
-
response =
|
10
|
+
response = capture
|
11
11
|
if response.success?
|
12
12
|
transaction.response = ActiveMerchant::Billing::Response.new(
|
13
13
|
true,
|
@@ -29,6 +29,26 @@ module Workarea
|
|
29
29
|
def cancel!
|
30
30
|
# No op - no cancel functionality available.
|
31
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def payment_id
|
35
|
+
transaction.reference.response.params["id"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def capture
|
39
|
+
request_id = SecureRandom.uuid
|
40
|
+
capture_response = response(request_id)
|
41
|
+
|
42
|
+
if Workarea::Afterpay::RETRY_ERROR_STATUSES.include? capture_response.status
|
43
|
+
return response(request_id)
|
44
|
+
end
|
45
|
+
|
46
|
+
capture_response
|
47
|
+
end
|
48
|
+
|
49
|
+
def response(request_id)
|
50
|
+
gateway.capture(payment_id, transaction.amount, request_id)
|
51
|
+
end
|
32
52
|
end
|
33
53
|
end
|
34
54
|
end
|
@@ -1,7 +1,52 @@
|
|
1
1
|
module Workarea
|
2
2
|
class Payment
|
3
3
|
module Purchase
|
4
|
-
|
4
|
+
class Afterpay
|
5
|
+
include OperationImplementation
|
6
|
+
include CreditCardOperation
|
7
|
+
include AfterpayPaymentGateway
|
8
|
+
|
9
|
+
def complete!
|
10
|
+
response = purchase
|
11
|
+
if response.success?
|
12
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
13
|
+
true,
|
14
|
+
I18n.t(
|
15
|
+
'workarea.afterpay.purchase',
|
16
|
+
amount: transaction.amount
|
17
|
+
),
|
18
|
+
response.body
|
19
|
+
)
|
20
|
+
else
|
21
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
22
|
+
false,
|
23
|
+
I18n.t('workarea.afterpay.purchase_failure'),
|
24
|
+
response.body
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def cancel!
|
30
|
+
# No op - no cancel functionality available.
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def purchase
|
36
|
+
request_id = SecureRandom.uuid
|
37
|
+
purchase_response = response(request_id)
|
38
|
+
|
39
|
+
if Workarea::Afterpay::RETRY_ERROR_STATUSES.include? purchase_response.status
|
40
|
+
return response(request_id)
|
41
|
+
end
|
42
|
+
|
43
|
+
purchase_response
|
44
|
+
end
|
45
|
+
|
46
|
+
def response(request_id)
|
47
|
+
gateway.purchase(tender.token, request_id)
|
48
|
+
end
|
49
|
+
end
|
5
50
|
end
|
6
51
|
end
|
7
52
|
end
|
@@ -6,9 +6,7 @@ module Workarea
|
|
6
6
|
include CreditCardOperation
|
7
7
|
|
8
8
|
def complete!
|
9
|
-
|
10
|
-
|
11
|
-
response = gateway.refund(afterpay_order_id, transaction.amount, request_id)
|
9
|
+
response = refund
|
12
10
|
|
13
11
|
if response.success?
|
14
12
|
transaction.response = ActiveMerchant::Billing::Response.new(
|
@@ -33,7 +31,6 @@ module Workarea
|
|
33
31
|
end
|
34
32
|
|
35
33
|
private
|
36
|
-
|
37
34
|
def gateway
|
38
35
|
currency = transaction.amount.currency.iso_code
|
39
36
|
location = Workarea::Afterpay.config[:currency_country_map][currency.to_sym]
|
@@ -46,6 +43,21 @@ module Workarea
|
|
46
43
|
def afterpay_order_id
|
47
44
|
transaction.reference.response.params["id"]
|
48
45
|
end
|
46
|
+
|
47
|
+
def refund
|
48
|
+
request_id = SecureRandom.uuid
|
49
|
+
refund_response = response(request_id)
|
50
|
+
|
51
|
+
if Workarea::Afterpay::RETRY_ERROR_STATUSES.include? refund_response.status
|
52
|
+
return response(request_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
refund_response
|
56
|
+
end
|
57
|
+
|
58
|
+
def response(request_id)
|
59
|
+
gateway.refund(afterpay_order_id, transaction.amount, request_id)
|
60
|
+
end
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
@@ -26,17 +26,5 @@ module Workarea
|
|
26
26
|
self.afterpay = nil
|
27
27
|
super
|
28
28
|
end
|
29
|
-
|
30
|
-
def authorize!(options = {})
|
31
|
-
transactions = tenders.map { |t| t.build_transaction(action: payment_action(t)) }
|
32
|
-
perform_operation(transactions, options)
|
33
|
-
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def payment_action(tender)
|
38
|
-
tender.slug == :afterpay ? 'purchase' : 'authorize'
|
39
|
-
end
|
40
|
-
|
41
29
|
end
|
42
30
|
end
|
@@ -2,6 +2,11 @@ module Workarea
|
|
2
2
|
module Afterpay
|
3
3
|
class OrderBuilder
|
4
4
|
|
5
|
+
module ProductUrl
|
6
|
+
include Workarea::I18n::DefaultUrlOptions
|
7
|
+
include Storefront::Engine.routes.url_helpers
|
8
|
+
extend self
|
9
|
+
end
|
5
10
|
attr_reader :order
|
6
11
|
|
7
12
|
# @param ::Workarea::Order
|
@@ -11,7 +16,7 @@ module Workarea
|
|
11
16
|
|
12
17
|
def build
|
13
18
|
{
|
14
|
-
|
19
|
+
amount: {
|
15
20
|
amount: order.order_balance.to_s,
|
16
21
|
currency: currency_code,
|
17
22
|
},
|
@@ -58,7 +63,7 @@ module Workarea
|
|
58
63
|
def address(address_obj)
|
59
64
|
{
|
60
65
|
name: "#{address_obj.first_name} #{address_obj.last_name}",
|
61
|
-
|
66
|
+
area1: address_obj.city,
|
62
67
|
line1: address_obj.street,
|
63
68
|
state: address_obj.region,
|
64
69
|
postcode: address_obj.postal_code,
|
@@ -74,6 +79,7 @@ module Workarea
|
|
74
79
|
name: product.name,
|
75
80
|
sku: oi.sku,
|
76
81
|
quantity: oi.quantity,
|
82
|
+
pageUrl: ProductUrl.product_url(id: oi.product.to_param, host: Workarea.config.host),
|
77
83
|
price: {
|
78
84
|
amount: oi.original_unit_price.to_s,
|
79
85
|
currency: currency_code
|
@@ -5,7 +5,7 @@ module Workarea
|
|
5
5
|
# Orders must be between the min and max order total to qualify.
|
6
6
|
def order_total_in_range?
|
7
7
|
return unless afterpay_configuration.present?
|
8
|
-
|
8
|
+
order.order_balance >= min_price && order.order_balance <= max_price
|
9
9
|
end
|
10
10
|
|
11
11
|
# Show if admin settings are enabled, there are configuration options returned
|
@@ -25,26 +25,18 @@ module Workarea
|
|
25
25
|
show? && afterpay_settings.display_on_pdp?
|
26
26
|
end
|
27
27
|
|
28
|
-
# TODO - handle what to display if there are multiple
|
29
|
-
# eligible display options. Docs do not seem to
|
30
|
-
# indicate there would ever be more than one.
|
31
|
-
def display_option
|
32
|
-
eligible_options.first
|
33
|
-
end
|
34
28
|
|
35
29
|
def installment_price
|
36
30
|
order.order_balance / Workarea.config.afterpay[:installment_count]
|
37
31
|
end
|
38
32
|
|
39
33
|
def min_price
|
40
|
-
|
41
|
-
|
42
|
-
config[:minimumAmount][:amount].to_m
|
34
|
+
return 0.to_m unless afterpay_configuration[:minimumAmount].present?
|
35
|
+
afterpay_configuration[:minimumAmount][:amount].to_m
|
43
36
|
end
|
44
37
|
|
45
38
|
def max_price
|
46
|
-
|
47
|
-
config[:maximumAmount][:amount].to_m
|
39
|
+
afterpay_configuration[:maximumAmount][:amount].to_m
|
48
40
|
end
|
49
41
|
|
50
42
|
def afterpay_country
|
@@ -52,12 +44,6 @@ module Workarea
|
|
52
44
|
end
|
53
45
|
|
54
46
|
private
|
55
|
-
def eligible_options
|
56
|
-
ap_options = afterpay_configuration.select do |ap|
|
57
|
-
order.order_balance >= min_price && order.order_balance <= max_price
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
47
|
def afterpay_configuration
|
62
48
|
options[:afterpay_configuration]
|
63
49
|
end
|
@@ -9,9 +9,8 @@ module Workarea
|
|
9
9
|
return unless afterpay_settings.enabled? && afterpay_settings.display_on_pdp?
|
10
10
|
return unless afterpay_location_configuration.present?
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
12
|
+
pricing.sell_min_price >= min_price && pricing.sell_min_price <= max_price
|
13
|
+
|
15
14
|
end
|
16
15
|
|
17
16
|
def installment_price
|
@@ -21,14 +20,12 @@ module Workarea
|
|
21
20
|
private
|
22
21
|
|
23
22
|
def min_price
|
24
|
-
|
25
|
-
|
26
|
-
config[:minimumAmount][:amount].to_m
|
23
|
+
return 0.to_m unless afterpay_location_configuration[:minimumAmount].present?
|
24
|
+
afterpay_location_configuration[:minimumAmount][:amount].to_m
|
27
25
|
end
|
28
26
|
|
29
27
|
def max_price
|
30
|
-
|
31
|
-
config[:maximumAmount][:amount].to_m
|
28
|
+
afterpay_location_configuration[:maximumAmount][:amount].to_m
|
32
29
|
end
|
33
30
|
|
34
31
|
def afterpay_location
|
@@ -40,7 +37,7 @@ module Workarea
|
|
40
37
|
end
|
41
38
|
|
42
39
|
def afterpay_location_configuration
|
43
|
-
afterpay_configuration(afterpay_location)
|
40
|
+
afterpay_configuration(afterpay_location).with_indifferent_access
|
44
41
|
end
|
45
42
|
|
46
43
|
def afterpay_settings
|
data/config/locales/en.yml
CHANGED
@@ -52,9 +52,14 @@ en:
|
|
52
52
|
afterpay: Afterpay
|
53
53
|
learn_more: Learn More
|
54
54
|
afterpay:
|
55
|
+
authorize: "%{amount} has been authorized"
|
55
56
|
capture: "%{amount} has been captured"
|
57
|
+
purchase: "%{amount} has been purchased"
|
56
58
|
refund: "%{amount} has been refunded"
|
59
|
+
refund: "Aftperay Transaction has been voided"
|
60
|
+
authorize_failure: "Afterpay authorize has failed"
|
57
61
|
capture_failure: "Afterpay capture has failed"
|
62
|
+
purchase_failure: "Afterpay purchase has failed"
|
58
63
|
refund_failure: "Afterpay refund has failed"
|
59
64
|
tender_description: "Afterpay: %{installment_count} installments of %{installment_price}"
|
60
65
|
dialog:
|
@@ -6,10 +6,7 @@ module Workarea
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def get_configuration
|
9
|
-
b =
|
10
|
-
{
|
11
|
-
"type": "PAY_BY_INSTALLMENT",
|
12
|
-
"description": "Pay over time",
|
9
|
+
b = {
|
13
10
|
"minimumAmount": {
|
14
11
|
"amount": "5.00",
|
15
12
|
"currency": "USD"
|
@@ -19,7 +16,7 @@ module Workarea
|
|
19
16
|
"currency": "USD"
|
20
17
|
}
|
21
18
|
}
|
22
|
-
|
19
|
+
|
23
20
|
Response.new(response(b))
|
24
21
|
end
|
25
22
|
|
@@ -78,7 +75,7 @@ module Workarea
|
|
78
75
|
"currency": "USD"
|
79
76
|
},
|
80
77
|
"token": "9tlqhfgebl6mu2g9t98rre2ia25ri2hvadc4aaimvpca1p9fma5j",
|
81
|
-
"
|
78
|
+
"amount": {
|
82
79
|
"amount": "95.30",
|
83
80
|
"currency": "USD"
|
84
81
|
}
|
@@ -95,11 +92,25 @@ module Workarea
|
|
95
92
|
Response.new(response(b))
|
96
93
|
end
|
97
94
|
|
98
|
-
def capture(
|
95
|
+
def capture(payment_id, amount, request_id)
|
96
|
+
Response.new(response(payment_response_body, 200))
|
97
|
+
end
|
98
|
+
|
99
|
+
def authorize(token, order_id = "", request_id)
|
99
100
|
if token == "error_token"
|
100
101
|
Response.new(response(capture_error_response_body, 402))
|
102
|
+
elsif token == "timeout_token"
|
103
|
+
Response.new(response(nil, 502))
|
101
104
|
else
|
102
|
-
Response.new(response(
|
105
|
+
Response.new(response(payment_response_body, 200))
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def purchase(token, order_id = "", request_id)
|
110
|
+
if token == "error_token"
|
111
|
+
Response.new(response(capture_error_response_body, 402))
|
112
|
+
else
|
113
|
+
Response.new(response(payment_response_body, 200))
|
103
114
|
end
|
104
115
|
end
|
105
116
|
|
@@ -118,6 +129,10 @@ module Workarea
|
|
118
129
|
Response.new(response(b))
|
119
130
|
end
|
120
131
|
|
132
|
+
def void(payment_id)
|
133
|
+
Response.new(response(payment_response_body))
|
134
|
+
end
|
135
|
+
|
121
136
|
private
|
122
137
|
|
123
138
|
def response(body, status = 200)
|
@@ -138,7 +153,7 @@ module Workarea
|
|
138
153
|
}
|
139
154
|
end
|
140
155
|
|
141
|
-
def
|
156
|
+
def payment_response_body
|
142
157
|
{
|
143
158
|
"id": "12345678",
|
144
159
|
"token": "q54l9qd907m6iqqqlcrm5tpbjjsnfo47vsm59gqrfnd2rqefk9hu",
|
@@ -13,14 +13,14 @@ module Workarea
|
|
13
13
|
|
14
14
|
def get_configuration
|
15
15
|
response = connection.get do |req|
|
16
|
-
req.url "/
|
16
|
+
req.url "/v2/configuration"
|
17
17
|
end
|
18
18
|
Afterpay::Response.new(response)
|
19
19
|
end
|
20
20
|
|
21
21
|
def get_order(token)
|
22
|
-
|
23
|
-
req.url "/
|
22
|
+
response = connection.get do |req|
|
23
|
+
req.url "/v2/checkouts/#{token}"
|
24
24
|
end
|
25
25
|
|
26
26
|
Afterpay::Response.new(response)
|
@@ -28,19 +28,56 @@ module Workarea
|
|
28
28
|
|
29
29
|
def create_order(order)
|
30
30
|
response = connection.post do |req|
|
31
|
-
req.url "/
|
31
|
+
req.url "/v2/checkouts"
|
32
32
|
req.body = order.to_json
|
33
33
|
end
|
34
|
+
Afterpay::Response.new(response)
|
35
|
+
end
|
36
|
+
|
37
|
+
def authorize(token, order_id, request_id)
|
38
|
+
body = {
|
39
|
+
token: token,
|
40
|
+
request_id: request_id
|
41
|
+
}
|
42
|
+
response = connection.post do |req|
|
43
|
+
req.url "/v2/payments/auth"
|
44
|
+
req.body = body.to_json
|
45
|
+
end
|
46
|
+
|
47
|
+
Afterpay::Response.new(response)
|
48
|
+
end
|
49
|
+
|
50
|
+
def capture(payment_id, amount, request_id)
|
51
|
+
body = {
|
52
|
+
amount: {
|
53
|
+
amount: amount.to_f,
|
54
|
+
currency: amount.currency.iso_code
|
55
|
+
},
|
56
|
+
request_id: request_id
|
57
|
+
}
|
58
|
+
response = connection.post do |req|
|
59
|
+
req.url "/v2/payments/#{payment_id}/capture"
|
60
|
+
req.body = body.to_json
|
61
|
+
end
|
62
|
+
|
63
|
+
Afterpay::Response.new(response)
|
64
|
+
end
|
65
|
+
|
66
|
+
def void(payment_id)
|
67
|
+
response = connection.post do |req|
|
68
|
+
req.url "/v2/payments/#{payment_id}/void"
|
69
|
+
end
|
34
70
|
|
35
71
|
Afterpay::Response.new(response)
|
36
72
|
end
|
37
73
|
|
38
|
-
def
|
74
|
+
def purchase(token, request_id)
|
39
75
|
body = {
|
40
|
-
token: token
|
76
|
+
token: token,
|
77
|
+
request_id: request_id
|
41
78
|
}
|
42
79
|
response = connection.post do |req|
|
43
|
-
req.url "/
|
80
|
+
req.url "/v2/payments/capture"
|
44
81
|
req.body = body.to_json
|
45
82
|
end
|
46
83
|
|
@@ -57,7 +94,7 @@ module Workarea
|
|
57
94
|
}
|
58
95
|
|
59
96
|
response = connection.post do |req|
|
60
|
-
req.url "/
|
97
|
+
req.url "/v2/payments/#{afterpay_order_id}/refund"
|
61
98
|
req.body = body.to_json
|
62
99
|
end
|
63
100
|
Afterpay::Response.new(response)
|
data/lib/workarea/afterpay.rb
CHANGED
@@ -135,7 +135,7 @@ module Workarea
|
|
135
135
|
transactions = payment.tenders.first.transactions
|
136
136
|
assert_equal(1, transactions.size)
|
137
137
|
assert(transactions.first.success?)
|
138
|
-
assert_equal('
|
138
|
+
assert_equal('authorize', transactions.first.action)
|
139
139
|
end
|
140
140
|
|
141
141
|
|
@@ -183,7 +183,7 @@ module Workarea
|
|
183
183
|
|
184
184
|
ap_tender = payment.tenders.detect { |t| t.slug == :afterpay }
|
185
185
|
assert(ap_tender.transactions.first.success?)
|
186
|
-
assert_equal('
|
186
|
+
assert_equal('authorize', ap_tender.transactions.first.action)
|
187
187
|
|
188
188
|
sc_tender = payment.tenders.detect { |t| t.slug == :store_credit }
|
189
189
|
assert(sc_tender.transactions.first.success?)
|
@@ -221,21 +221,21 @@ module Workarea
|
|
221
221
|
|
222
222
|
def get_order_response(amount)
|
223
223
|
b = {
|
224
|
-
"
|
224
|
+
"amount": {
|
225
225
|
"amount": "#{amount}",
|
226
226
|
"currency": "USD"
|
227
|
+
}
|
227
228
|
}
|
228
|
-
}
|
229
229
|
Workarea::Afterpay::Response.new(response(b))
|
230
230
|
end
|
231
231
|
|
232
232
|
def response(body, status = 200)
|
233
233
|
response = Faraday.new do |builder|
|
234
234
|
builder.adapter :test do |stub|
|
235
|
-
stub.get("/
|
235
|
+
stub.get("/v2/bogus") { |env| [ status, {}, body.to_json ] }
|
236
236
|
end
|
237
237
|
end
|
238
|
-
response.get("/
|
238
|
+
response.get("/v2/bogus")
|
239
239
|
end
|
240
240
|
end
|
241
241
|
end
|
@@ -3,15 +3,22 @@ require 'test_helper'
|
|
3
3
|
module Workarea
|
4
4
|
class AfterpayPaymentIntegrationTest < Workarea::TestCase
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
|
6
|
+
def test_auth_capture
|
7
|
+
transaction = tender.build_transaction(action: 'authorize')
|
8
|
+
Payment::Purchase::Afterpay.new(tender, transaction).complete!
|
9
|
+
|
10
|
+
assert(transaction.success?)
|
9
11
|
transaction.save!
|
10
12
|
|
11
|
-
|
12
|
-
operation.complete!
|
13
|
+
assert(tender.token.present?)
|
13
14
|
|
14
|
-
|
15
|
+
capture = Payment::Capture.new(payment: payment)
|
16
|
+
capture.allocate_amounts!(total: 5.to_m)
|
17
|
+
assert(capture.valid?)
|
18
|
+
capture.complete!
|
19
|
+
|
20
|
+
capture_transaction = payment.transactions.detect(&:captures)
|
21
|
+
assert(capture_transaction.valid?)
|
15
22
|
end
|
16
23
|
|
17
24
|
def test_auth
|
@@ -26,11 +33,32 @@ module Workarea
|
|
26
33
|
assert(transaction.success?)
|
27
34
|
end
|
28
35
|
|
29
|
-
|
36
|
+
def test_auth_void
|
37
|
+
transaction = tender.build_transaction(action: 'authorize')
|
38
|
+
operation = Payment::Authorize::Afterpay.new(tender, transaction)
|
39
|
+
operation.complete!
|
40
|
+
assert(transaction.success?, 'expected transaction to be successful')
|
41
|
+
transaction.save!
|
30
42
|
|
31
|
-
|
32
|
-
|
33
|
-
|
43
|
+
assert(tender.token.present?)
|
44
|
+
operation.cancel!
|
45
|
+
void = transaction.cancellation
|
46
|
+
|
47
|
+
assert(void.success?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_timeout_auth
|
51
|
+
transaction = timeout_tender.build_transaction(action: 'authorize')
|
52
|
+
operation = Payment::Authorize::Afterpay.new(timeout_tender, transaction)
|
53
|
+
operation.complete!
|
54
|
+
refute(transaction.success?, 'expected transaction to be a failure')
|
55
|
+
transaction.save!
|
56
|
+
|
57
|
+
assert(tender.token.present?)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
private
|
34
62
|
|
35
63
|
def payment
|
36
64
|
@payment ||=
|
@@ -63,5 +91,18 @@ module Workarea
|
|
63
91
|
payment.afterpay
|
64
92
|
end
|
65
93
|
end
|
94
|
+
|
95
|
+
def timeout_tender
|
96
|
+
@tender ||=
|
97
|
+
begin
|
98
|
+
payment.set_address(first_name: 'Ben', last_name: 'Crouse')
|
99
|
+
|
100
|
+
payment.build_afterpay(
|
101
|
+
token: 'timeout_token'
|
102
|
+
)
|
103
|
+
|
104
|
+
payment.afterpay
|
105
|
+
end
|
106
|
+
end
|
66
107
|
end
|
67
108
|
end
|
@@ -19,7 +19,7 @@ module Workarea
|
|
19
19
|
payment.reload
|
20
20
|
order_hash = Workarea::Afterpay::OrderBuilder.new(order).build
|
21
21
|
|
22
|
-
assert_equal(9.00, order_hash[:
|
22
|
+
assert_equal(9.00, order_hash[:amount][:amount].to_f)
|
23
23
|
assert_equal(1.00, order_hash[:shippingAmount][:amount].to_f)
|
24
24
|
assert_equal(0.00, order_hash[:taxAmount][:amount].to_f)
|
25
25
|
assert_equal(order.id, order_hash[:merchantReference])
|
@@ -31,8 +31,8 @@ module Workarea
|
|
31
31
|
assert_equal(1, order_hash[:discounts].size)
|
32
32
|
assert_equal("Order Total Discount", order_hash[:discounts].first[:displayName])
|
33
33
|
|
34
|
-
assert_equal("Philadelphia", order_hash[:shipping][:
|
35
|
-
assert_equal("Wilmington", order_hash[:billing][:
|
34
|
+
assert_equal("Philadelphia", order_hash[:shipping][:area1])
|
35
|
+
assert_equal("Wilmington", order_hash[:billing][:area1])
|
36
36
|
|
37
37
|
assert_equal(1, order_hash[:items].size)
|
38
38
|
assert_equal("SKU", order_hash[:items].first[:sku])
|
@@ -80,7 +80,6 @@ module Workarea
|
|
80
80
|
shipping_service: shipping_service.name,
|
81
81
|
)
|
82
82
|
|
83
|
-
|
84
83
|
order
|
85
84
|
end
|
86
85
|
end
|
@@ -61,16 +61,6 @@ module Workarea
|
|
61
61
|
refute(view_model.afterpay_country.present?)
|
62
62
|
end
|
63
63
|
|
64
|
-
def test_display_options
|
65
|
-
@order.total_price = 25.00
|
66
|
-
|
67
|
-
checkout = Workarea::Checkout.new(@order, @user)
|
68
|
-
view_model = Workarea::Storefront::AfterpayViewModel.new(checkout, { afterpay_configuration: afterpay_options, order: @order })
|
69
|
-
|
70
|
-
assert_equal("PAY_BY_INSTALLMENT", view_model.display_option[:type])
|
71
|
-
assert_equal("Pay over time", view_model.display_option[:description])
|
72
|
-
end
|
73
|
-
|
74
64
|
def test_installment_price
|
75
65
|
@order.total_price = 100.00
|
76
66
|
view_model = Workarea::Storefront::AfterpayViewModel.new(nil, { afterpay_configuration: afterpay_options, order: @order })
|
@@ -80,20 +70,16 @@ module Workarea
|
|
80
70
|
|
81
71
|
private
|
82
72
|
def afterpay_options
|
83
|
-
|
84
|
-
{
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
"maximumAmount": {
|
92
|
-
"amount": "30.00",
|
93
|
-
"currency": "USD"
|
94
|
-
}
|
73
|
+
{
|
74
|
+
"minimumAmount": {
|
75
|
+
"amount": "20.00",
|
76
|
+
"currency": "USD"
|
77
|
+
},
|
78
|
+
"maximumAmount": {
|
79
|
+
"amount": "30.00",
|
80
|
+
"currency": "USD"
|
95
81
|
}
|
96
|
-
|
82
|
+
}
|
97
83
|
end
|
98
84
|
end
|
99
85
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: workarea-afterpay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Yucis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: workarea
|
@@ -182,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
182
|
- !ruby/object:Gem::Version
|
183
183
|
version: '0'
|
184
184
|
requirements: []
|
185
|
-
rubygems_version: 3.0.
|
185
|
+
rubygems_version: 3.0.6
|
186
186
|
signing_key:
|
187
187
|
specification_version: 4
|
188
188
|
summary: Workarea Commerce Platform Afterpay integration
|