workarea-afterpay 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +20 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.gitignore +22 -0
- data/CHANGELOG.md +161 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/CONTRIBUTING.md +3 -0
- data/Gemfile +17 -0
- data/LICENSE +52 -0
- data/README.md +131 -0
- data/Rakefile +60 -0
- data/app/assets/fonts/workarea/storefront/raleway/raleway_800.woff +0 -0
- data/app/assets/fonts/workarea/storefront/raleway/raleway_800.woff2 +0 -0
- data/app/assets/fonts/workarea/storefront/raleway/raleway_regular.woff +0 -0
- data/app/assets/fonts/workarea/storefront/raleway/raleway_regular.woff2 +0 -0
- data/app/assets/images/workarea/storefront/afterpay/afterpay_logo.png +0 -0
- data/app/assets/images/workarea/storefront/afterpay/aus/dialog_header_image.png +0 -0
- data/app/assets/images/workarea/storefront/afterpay/aus/step_1.png +0 -0
- data/app/assets/images/workarea/storefront/afterpay/aus/step_2.png +0 -0
- data/app/assets/images/workarea/storefront/afterpay/aus/step_3.png +0 -0
- data/app/assets/images/workarea/storefront/afterpay/us/complete_checkout.svg +1 -0
- data/app/assets/images/workarea/storefront/afterpay/us/four_equal_payments.svg +1 -0
- data/app/assets/images/workarea/storefront/afterpay/us/select_installments.svg +1 -0
- data/app/assets/javascripts/workarea/storefront/afterpay/config.js.erb +17 -0
- data/app/assets/javascripts/workarea/storefront/afterpay/modules/afterpay_redirect.js +30 -0
- data/app/assets/stylesheets/workarea/storefront/components/_afterpay_dialog.scss +158 -0
- data/app/controllers/workarea/admin/afterpay/configurations_controller.rb +33 -0
- data/app/controllers/workarea/storefront/afterpay_controller.rb +124 -0
- data/app/controllers/workarea/storefront/afterpay_dialog_controller.rb +7 -0
- data/app/controllers/workarea/storefront/checkout/place_order_controller.decorator +11 -0
- data/app/helpers/workarea/storefront/afterpay_helper.rb +16 -0
- data/app/models/workarea/afterpay/configuration.rb +16 -0
- data/app/models/workarea/order.decorator +17 -0
- data/app/models/workarea/payment/afterpay_payment_gateway.rb +16 -0
- data/app/models/workarea/payment/authorize/afterpay.rb +7 -0
- data/app/models/workarea/payment/capture/afterpay.rb +35 -0
- data/app/models/workarea/payment/purchase/afterpay.rb +7 -0
- data/app/models/workarea/payment/refund/afterpay.rb +52 -0
- data/app/models/workarea/payment/tender/afterpay.rb +18 -0
- data/app/models/workarea/payment.decorator +42 -0
- data/app/services/workarea/afterpay/order_builder.rb +107 -0
- data/app/view_models/workarea/admin/afterpay_configuration_view_model.rb +36 -0
- data/app/view_models/workarea/storefront/afterpay_configuration.rb +25 -0
- data/app/view_models/workarea/storefront/afterpay_view_model.rb +89 -0
- data/app/view_models/workarea/storefront/cart_view_model.decorator +12 -0
- data/app/view_models/workarea/storefront/checkout/payment_view_model.decorator +32 -0
- data/app/view_models/workarea/storefront/product_view_model.decorator +50 -0
- data/app/views/workarea/admin/afterpay/configurations/edit.html.haml +59 -0
- data/app/views/workarea/admin/orders/tenders/_afterpay.html.haml +2 -0
- data/app/views/workarea/admin/shared/_afterpay_configuration_link.html.haml +1 -0
- data/app/views/workarea/storefront/afterpay_dialog/show.html.haml +40 -0
- data/app/views/workarea/storefront/afterpay_dialog/show_aus.html.haml +40 -0
- data/app/views/workarea/storefront/carts/_afterpay.html.haml +9 -0
- data/app/views/workarea/storefront/checkouts/_afterpay_payment.html.haml +17 -0
- data/app/views/workarea/storefront/order_mailer/tenders/_afterpay.html.haml +4 -0
- data/app/views/workarea/storefront/order_mailer/tenders/_afterpay.text.erb +1 -0
- data/app/views/workarea/storefront/orders/tenders/_afterpay.html.haml +5 -0
- data/app/views/workarea/storefront/products/_afterpay_pricing.html.haml +3 -0
- data/app/views/workarea/storefront/style_guides/components/_afterpay_dialog.html.haml +92 -0
- data/bin/rails +19 -0
- data/bin/test +5 -0
- data/config/initializers/appends.rb +34 -0
- data/config/initializers/workarea.rb +17 -0
- data/config/locales/en.yml +107 -0
- data/config/routes.rb +15 -0
- data/four_equal_paymnets.svg +1 -0
- data/lib/workarea/afterpay/bogus_gateway.rb +211 -0
- data/lib/workarea/afterpay/engine.rb +14 -0
- data/lib/workarea/afterpay/gateway.rb +121 -0
- data/lib/workarea/afterpay/response.rb +17 -0
- data/lib/workarea/afterpay/version.rb +5 -0
- data/lib/workarea/afterpay.rb +59 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +30 -0
- data/test/dummy/bin/update +26 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config/application.rb +30 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/cable.yml +10 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +51 -0
- data/test/dummy/config/environments/production.rb +88 -0
- data/test/dummy/config/environments/test.rb +44 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/new_framework_defaults.rb +18 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/workarea.rb +5 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +56 -0
- data/test/dummy/config/routes.rb +5 -0
- data/test/dummy/config/secrets.yml +32 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/seeds.rb +2 -0
- data/test/dummy/log/.keep +0 -0
- data/test/integration/workarea/storefront/afterpay_integration_test.rb +242 -0
- data/test/models/workarea/afterpay_order_test.rb +10 -0
- data/test/models/workarea/payment/afterpay_payment_integration_test.rb +67 -0
- data/test/services/workarea/afterpay/order_builder_test.rb +88 -0
- data/test/system/workarea/storefront/afterpay_product_test.rb +51 -0
- data/test/system/workarea/storefront/cart_system_test.decorator +83 -0
- data/test/system/workarea/storefront/logged_in_checkout_system_test.decorator +10 -0
- data/test/teaspoon_env.rb +6 -0
- data/test/test_helper.rb +10 -0
- data/test/view_models/workarea/storefront/afterpay_view_model_test.rb +100 -0
- data/test/view_models/workarea/storefront/product_view_model_afterpay_test.rb +78 -0
- data/workarea-afterpay.gemspec +23 -0
- metadata +189 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
/*------------------------------------*\
|
2
|
+
#AFTERPAY-DIALOG
|
3
|
+
\*------------------------------------*/
|
4
|
+
|
5
|
+
$afterpay-navy: #074578 !default;
|
6
|
+
$afterpay-blue: #16a0d5 !default;
|
7
|
+
$afterpay-aqua: #05cdd5 !default;
|
8
|
+
$afterpay-gray: #2d3134 !default;
|
9
|
+
$afterpay-magenta: #ec3b91 !default;
|
10
|
+
$afterpay-off-gray: #606a72 !default;
|
11
|
+
|
12
|
+
$afterpay-dialog-max-width: 700px !default;
|
13
|
+
$afterpay-dialog-spacing: $spacing-unit * 3 !default;
|
14
|
+
$afterpay-dialog-background-color: $white !default;
|
15
|
+
|
16
|
+
@font-face {
|
17
|
+
font-family: 'Raleway';
|
18
|
+
font-style: normal;
|
19
|
+
font-weight: 400;
|
20
|
+
src: font-url('workarea/storefront/raleway/raleway_regular.woff2') format('woff2'),
|
21
|
+
font-url('workarea/storefront/raleway/raleway_regular.woff') format('woff');
|
22
|
+
}
|
23
|
+
|
24
|
+
@font-face {
|
25
|
+
font-family: 'Raleway';
|
26
|
+
font-style: normal;
|
27
|
+
font-weight: 800;
|
28
|
+
src: font-url('workarea/storefront/raleway/raleway_800.woff2') format('woff2'),
|
29
|
+
font-url('workarea/storefront/raleway/raleway_800.woff') format('woff');
|
30
|
+
}
|
31
|
+
|
32
|
+
.afterpay-dialog {
|
33
|
+
max-width: $afterpay-dialog-max-width;
|
34
|
+
color: $afterpay-off-gray;
|
35
|
+
font-family: 'Raleway', 'Lucida Grande', Helvetica, sans-serif;
|
36
|
+
background-color: $afterpay-dialog-background-color;
|
37
|
+
|
38
|
+
.style-guide__example-block & {
|
39
|
+
margin: 0 auto;
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
.afterpay-dialog--aus {}
|
44
|
+
|
45
|
+
.afterpay-dialog__header {
|
46
|
+
position: relative;
|
47
|
+
padding: $afterpay-dialog-spacing $afterpay-dialog-spacing 0;
|
48
|
+
background-color: #fafbfc;
|
49
|
+
text-align: center;
|
50
|
+
|
51
|
+
.afterpay-dialog--aus & {
|
52
|
+
text-align: left
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
.afterpay-dialog__logo {
|
57
|
+
padding-bottom: $afterpay-dialog-spacing;
|
58
|
+
}
|
59
|
+
|
60
|
+
.afterpay-dialog__subheading {
|
61
|
+
@extend %inline-list;
|
62
|
+
}
|
63
|
+
|
64
|
+
.afterpay-dialog__subheading-part {
|
65
|
+
line-height: 1.2;
|
66
|
+
|
67
|
+
.afterpay-dialog--aus &:last-of-type {
|
68
|
+
display: block;
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
.afterpay-dialog__description {
|
73
|
+
padding-bottom: $afterpay-dialog-spacing;
|
74
|
+
|
75
|
+
@include respond-to($medium-breakpoint) {
|
76
|
+
padding-bottom: $afterpay-dialog-spacing * 2;
|
77
|
+
}
|
78
|
+
|
79
|
+
.afterpay-dialog--aus & {
|
80
|
+
width: 75%;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
.afterpay-dialog__header-image {
|
85
|
+
width: 100%;
|
86
|
+
|
87
|
+
}
|
88
|
+
|
89
|
+
.afterpay-dialog__heading {
|
90
|
+
color: $afterpay-blue;
|
91
|
+
font-weight: bold;
|
92
|
+
line-height: 1;
|
93
|
+
|
94
|
+
.afterpay-dialog__header & {
|
95
|
+
|
96
|
+
}
|
97
|
+
|
98
|
+
.afterpay-dialog__step & {
|
99
|
+
|
100
|
+
.afterpay-dialog--aus & {
|
101
|
+
color: $afterpay-gray;
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
.afterpay-dialog__footer & {
|
106
|
+
color: $afterpay-gray;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
.afterpay-dialog__steps {
|
111
|
+
margin: $afterpay-dialog-spacing 0;
|
112
|
+
padding: 0 $afterpay-dialog-spacing;
|
113
|
+
}
|
114
|
+
|
115
|
+
.afterpay-dialog__step {
|
116
|
+
padding: $afterpay-dialog-spacing / 2;
|
117
|
+
|
118
|
+
.afterpay-dialog--aus & {
|
119
|
+
text-align: center;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
.afterpay-dialog__step-icon {
|
124
|
+
display: block;
|
125
|
+
height: 100px;
|
126
|
+
margin: 0 auto $afterpay-dialog-spacing;
|
127
|
+
|
128
|
+
.afterpay-dialog--aus & {
|
129
|
+
height: 74px;
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
.afterpay-dialog__step-body {
|
134
|
+
}
|
135
|
+
|
136
|
+
.afterpay-dialog__footer {
|
137
|
+
padding: 0 $afterpay-dialog-spacing $afterpay-dialog-spacing;
|
138
|
+
}
|
139
|
+
|
140
|
+
.afterpay-dialog__requirements {
|
141
|
+
padding: 0;
|
142
|
+
|
143
|
+
.afterpay-dialog--aus & {
|
144
|
+
margin: 0;
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
.afterpay-dialog__requirement {
|
149
|
+
.afterpay-dialog--aus & {
|
150
|
+
display: inline-block;
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
.afterpay-dialog__terms {}
|
155
|
+
|
156
|
+
.afterpay-dialog__copyright {
|
157
|
+
text-align: center;
|
158
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Admin
|
3
|
+
module Afterpay
|
4
|
+
class ConfigurationsController < Admin::ApplicationController
|
5
|
+
required_permissions :settings
|
6
|
+
|
7
|
+
def edit
|
8
|
+
@configuration = configuration
|
9
|
+
end
|
10
|
+
|
11
|
+
def update
|
12
|
+
if configuration.update_attributes(configuration_params)
|
13
|
+
redirect_to admin.edit_afterpay_configuration_path, flash: { success: t('workarea.admin.afterpay_configuration.edit.flash_messages.updated') }
|
14
|
+
else
|
15
|
+
flash[:error] = t('workarea.admin.afterpay_configuration.edit.flash_messages.save_error')
|
16
|
+
@configuration = configuration
|
17
|
+
render :edit, status: :unprocessable_entity
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def configuration
|
23
|
+
model = Workarea::Afterpay::Configuration.current
|
24
|
+
Workarea::Admin::AfterpayConfigurationViewModel.new(model)
|
25
|
+
end
|
26
|
+
|
27
|
+
def configuration_params
|
28
|
+
params.permit(:enabled, :display_on_cart, :display_on_pdp)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Storefront::AfterpayController < Storefront::ApplicationController
|
3
|
+
include Storefront::CurrentCheckout
|
4
|
+
|
5
|
+
before_action :validate_checkout
|
6
|
+
|
7
|
+
def start
|
8
|
+
self.current_order = current_checkout.order
|
9
|
+
|
10
|
+
Pricing.perform(current_order, current_shipping)
|
11
|
+
|
12
|
+
check_inventory || (return)
|
13
|
+
# get a token for the current order
|
14
|
+
order_payload = Afterpay::OrderBuilder.new(current_order).build
|
15
|
+
response = gateway.create_order(order_payload)
|
16
|
+
|
17
|
+
if !response.success?
|
18
|
+
flash[:error] = t('workarea.storefront.afterpay.payment_error')
|
19
|
+
redirect_to(checkout_payment_path) && (return)
|
20
|
+
end
|
21
|
+
|
22
|
+
token = response.body["token"]
|
23
|
+
|
24
|
+
payment = Workarea::Payment.find(current_order.id)
|
25
|
+
|
26
|
+
payment.clear_credit_card
|
27
|
+
|
28
|
+
payment.set_afterpay(token: token)
|
29
|
+
|
30
|
+
# set the token on the order so we can more easily look up the order
|
31
|
+
# when returning from afterpay
|
32
|
+
current_order.update_attributes!(afterpay_token: token)
|
33
|
+
|
34
|
+
redirect_to checkout_payment_path
|
35
|
+
end
|
36
|
+
|
37
|
+
def complete
|
38
|
+
self.current_order = Order.where(afterpay_token: params[:orderToken]).first
|
39
|
+
payment = current_checkout.payment
|
40
|
+
|
41
|
+
if params[:status] != "SUCCESS"
|
42
|
+
flash[:error] = t('workarea.storefront.afterpay.payment_error')
|
43
|
+
payment.clear_afterpay
|
44
|
+
redirect_to(checkout_payment_path) && (return)
|
45
|
+
end
|
46
|
+
|
47
|
+
current_order.user_id = current_user.try(:id)
|
48
|
+
check_inventory || (return)
|
49
|
+
|
50
|
+
shipping = current_shipping
|
51
|
+
|
52
|
+
Pricing.perform(current_order, shipping)
|
53
|
+
payment.adjust_tender_amounts(current_order.total_price)
|
54
|
+
|
55
|
+
ap_order_details = gateway.get_order(params[:orderToken])
|
56
|
+
tender = payment.afterpay
|
57
|
+
|
58
|
+
unless (ap_order_details.body["totalAmount"]["amount"].to_m == tender.amount.to_m && current_checkout.complete?)
|
59
|
+
flash[:error] = t('workarea.storefront.afterpay.payment_error')
|
60
|
+
payment.clear_afterpay
|
61
|
+
redirect_to(checkout_payment_path) && (return)
|
62
|
+
end
|
63
|
+
|
64
|
+
payment.afterpay.update_attributes!(ready_to_capture: true)
|
65
|
+
|
66
|
+
# place the order.
|
67
|
+
if current_checkout.place_order
|
68
|
+
completed_place_order
|
69
|
+
else
|
70
|
+
incomplete_place_order
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def cancel
|
75
|
+
self.current_order = Order.where(afterpay_token: params[:orderToken]).first
|
76
|
+
|
77
|
+
current_order.user_id = current_user.try(:id)
|
78
|
+
|
79
|
+
payment = current_checkout.payment
|
80
|
+
payment.clear_afterpay
|
81
|
+
|
82
|
+
flash[:success] = t('workarea.storefront.afterpay.cancel_message')
|
83
|
+
|
84
|
+
redirect_to checkout_payment_path
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def gateway
|
90
|
+
Afterpay.gateway(self.current_order.afterpay_location)
|
91
|
+
end
|
92
|
+
|
93
|
+
def completed_place_order
|
94
|
+
Storefront::OrderMailer.confirmation(current_order.id).deliver_later
|
95
|
+
self.completed_order = current_order
|
96
|
+
clear_current_order
|
97
|
+
|
98
|
+
flash[:success] = t('workarea.storefront.flash_messages.order_placed')
|
99
|
+
redirect_to finished_checkout_destination
|
100
|
+
end
|
101
|
+
|
102
|
+
def incomplete_place_order
|
103
|
+
if current_checkout.shipping.try(:errors).present?
|
104
|
+
flash[:error] = current_checkout.shipping.errors.to_a.to_sentence
|
105
|
+
redirect_to checkout_shipping_path
|
106
|
+
else
|
107
|
+
flash[:error] = t('workarea.storefront.afterpay.payment_error')
|
108
|
+
|
109
|
+
payment = current_checkout.payment
|
110
|
+
payment.clear_afterpay
|
111
|
+
|
112
|
+
redirect_to checkout_payment_path
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def finished_checkout_destination
|
117
|
+
if current_admin.present? && current_admin.orders_access?
|
118
|
+
admin.order_path(completed_order)
|
119
|
+
else
|
120
|
+
checkout_confirmation_path
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Storefront::Checkout::PlaceOrderController, with: 'afterpay' do
|
3
|
+
def place_order
|
4
|
+
if params[:payment] == 'afterpay' && !current_checkout.payment.afterpay?
|
5
|
+
redirect_to start_afterpay_path and return
|
6
|
+
else
|
7
|
+
super
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Storefront
|
3
|
+
module AfterpayHelper
|
4
|
+
# get the relevant learn more link from
|
5
|
+
# a price.
|
6
|
+
#
|
7
|
+
# @return String
|
8
|
+
def learn_more_link(price)
|
9
|
+
currency = price.currency.iso_code.to_sym
|
10
|
+
location = Workarea::Afterpay.config[:currency_country_map][currency].to_sym.downcase
|
11
|
+
return unless location.present?
|
12
|
+
afterpay_dialog_path(location: location)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Afterpay
|
3
|
+
class Configuration
|
4
|
+
include ApplicationDocument
|
5
|
+
|
6
|
+
field :enabled, type: Boolean, default: true
|
7
|
+
|
8
|
+
field :display_on_cart, type: Boolean, default: true
|
9
|
+
field :display_on_pdp, type: Boolean, default: true
|
10
|
+
|
11
|
+
def self.current
|
12
|
+
Workarea::Afterpay::Configuration.first || Workarea::Afterpay::Configuration.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Order, with: :afterpay do
|
3
|
+
decorated do
|
4
|
+
field :afterpay_token, type: String
|
5
|
+
|
6
|
+
index({ afterpay_token: 1}, { background: true })
|
7
|
+
end
|
8
|
+
|
9
|
+
def afterpay_location
|
10
|
+
currency = total_price.currency.iso_code
|
11
|
+
location = Afterpay.config[:currency_country_map][currency.to_sym]
|
12
|
+
return unless location.present?
|
13
|
+
|
14
|
+
location.to_sym.downcase
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Payment
|
3
|
+
module AfterpayPaymentGateway
|
4
|
+
# Examines the tenders currency and selects the
|
5
|
+
# correct credentials to use in the gateway
|
6
|
+
#
|
7
|
+
# @return Workarea::Afterpay::Gateway
|
8
|
+
def gateway
|
9
|
+
currency = tender.amount.currency.iso_code
|
10
|
+
location = Afterpay.config[:currency_country_map][currency.to_sym]
|
11
|
+
|
12
|
+
Afterpay.gateway(location.to_sym.downcase)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Payment
|
3
|
+
class Capture
|
4
|
+
class Afterpay
|
5
|
+
include OperationImplementation
|
6
|
+
include CreditCardOperation
|
7
|
+
include AfterpayPaymentGateway
|
8
|
+
|
9
|
+
def complete!
|
10
|
+
response = gateway.capture(tender.token, tender.payment.id)
|
11
|
+
if response.success?
|
12
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
13
|
+
true,
|
14
|
+
I18n.t(
|
15
|
+
'workarea.afterpay.capture',
|
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
|
+
# No op - no cancel functionality available.
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Payment
|
3
|
+
class Refund
|
4
|
+
class Afterpay
|
5
|
+
include OperationImplementation
|
6
|
+
include CreditCardOperation
|
7
|
+
|
8
|
+
def complete!
|
9
|
+
request_id = SecureRandom.uuid
|
10
|
+
|
11
|
+
response = gateway.refund(afterpay_order_id, transaction.amount, request_id)
|
12
|
+
|
13
|
+
if response.success?
|
14
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
15
|
+
true,
|
16
|
+
I18n.t(
|
17
|
+
'workarea.afterpay.refund',
|
18
|
+
amount: transaction.amount
|
19
|
+
),
|
20
|
+
response.body
|
21
|
+
)
|
22
|
+
else
|
23
|
+
transaction.response = ActiveMerchant::Billing::Response.new(
|
24
|
+
false,
|
25
|
+
I18n.t('workarea.afterpay.refund_failure'),
|
26
|
+
response.body
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cancel!
|
32
|
+
# No op - no cancel functionality available.
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def gateway
|
38
|
+
currency = transaction.amount.currency.iso_code
|
39
|
+
location = Workarea::Afterpay.config[:currency_country_map][currency.to_sym]
|
40
|
+
|
41
|
+
location = location.to_sym.downcase
|
42
|
+
|
43
|
+
Workarea::Afterpay.gateway(location)
|
44
|
+
end
|
45
|
+
|
46
|
+
def afterpay_order_id
|
47
|
+
transaction.reference.response.params["id"]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Workarea
|
2
|
+
class Payment
|
3
|
+
class Tender
|
4
|
+
class Afterpay < Tender
|
5
|
+
field :token, type: String
|
6
|
+
field :ready_to_capture, type: Boolean, default: false
|
7
|
+
|
8
|
+
def slug
|
9
|
+
:afterpay
|
10
|
+
end
|
11
|
+
|
12
|
+
def installment_price
|
13
|
+
self.amount / Workarea.config.afterpay[:installment_count]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Workarea
|
2
|
+
decorate Payment, with: :afterpay do
|
3
|
+
decorated do
|
4
|
+
embeds_one :afterpay, class_name: 'Workarea::Payment::Tender::Afterpay'
|
5
|
+
end
|
6
|
+
|
7
|
+
def afterpay?
|
8
|
+
afterpay.present?
|
9
|
+
end
|
10
|
+
|
11
|
+
def set_afterpay(attrs)
|
12
|
+
build_afterpay unless afterpay
|
13
|
+
afterpay.attributes = attrs.slice(
|
14
|
+
:token
|
15
|
+
)
|
16
|
+
|
17
|
+
save
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear_afterpay
|
21
|
+
self.afterpay = nil
|
22
|
+
save
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_credit_card(*)
|
26
|
+
self.afterpay = nil
|
27
|
+
super
|
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
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Workarea
|
2
|
+
module Afterpay
|
3
|
+
class OrderBuilder
|
4
|
+
|
5
|
+
attr_reader :order
|
6
|
+
|
7
|
+
# @param ::Workarea::Order
|
8
|
+
def initialize(order)
|
9
|
+
@order = Workarea::Storefront::OrderViewModel.new(order)
|
10
|
+
end
|
11
|
+
|
12
|
+
def build
|
13
|
+
{
|
14
|
+
totalAmount: {
|
15
|
+
amount: order.order_balance.to_s,
|
16
|
+
currency: currency_code,
|
17
|
+
},
|
18
|
+
merchantReference: order.id,
|
19
|
+
consumer: {
|
20
|
+
phoneNumber: shipping.address.phone_number,
|
21
|
+
givenNames: shipping.address.first_name,
|
22
|
+
surname: shipping.address.last_name,
|
23
|
+
email: order.email
|
24
|
+
},
|
25
|
+
billing: address(payment.address),
|
26
|
+
shipping: address(shipping.address),
|
27
|
+
items: items,
|
28
|
+
discounts: discounts,
|
29
|
+
merchant: {
|
30
|
+
redirectConfirmUrl: confirm_url,
|
31
|
+
redirectCancelUrl: cancel_url
|
32
|
+
},
|
33
|
+
taxAmount: {
|
34
|
+
amount: order.tax_total.to_s,
|
35
|
+
currency: currency_code
|
36
|
+
},
|
37
|
+
shippingAmount: {
|
38
|
+
amount: order.shipping_total.to_s,
|
39
|
+
currency: currency_code
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def currency_code
|
47
|
+
@currency_code = order.total_price.currency.iso_code
|
48
|
+
end
|
49
|
+
|
50
|
+
def shipping
|
51
|
+
@shipping = Workarea::Shipping.find_by_order(order.id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def payment
|
55
|
+
@payment = Workarea::Payment.find(order.id)
|
56
|
+
end
|
57
|
+
|
58
|
+
def address(address_obj)
|
59
|
+
{
|
60
|
+
name: "#{address_obj.first_name} #{address_obj.last_name}",
|
61
|
+
suburb: address_obj.city,
|
62
|
+
line1: address_obj.street,
|
63
|
+
state: address_obj.region,
|
64
|
+
postcode: address_obj.postal_code,
|
65
|
+
countryCode: address_obj.country.alpha2,
|
66
|
+
phoneNumber: address_obj.phone_number
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def items
|
71
|
+
order.items.map do |oi|
|
72
|
+
product = Workarea::Catalog::Product.find_by_sku(oi.sku)
|
73
|
+
{
|
74
|
+
name: product.name,
|
75
|
+
sku: oi.sku,
|
76
|
+
quantity: oi.quantity,
|
77
|
+
price: {
|
78
|
+
amount: oi.original_unit_price.to_s,
|
79
|
+
currency: currency_code
|
80
|
+
}
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def discounts
|
86
|
+
discounts = order.price_adjustments.select { |p| p.discount? }
|
87
|
+
discounts.map do |d|
|
88
|
+
{
|
89
|
+
displayName: d.description,
|
90
|
+
amount: {
|
91
|
+
amount: d.amount.abs.to_s,
|
92
|
+
currency: currency_code
|
93
|
+
}
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def confirm_url
|
99
|
+
Storefront::Engine.routes.url_helpers.complete_afterpay_url(host: Workarea.config.host)
|
100
|
+
end
|
101
|
+
|
102
|
+
def cancel_url
|
103
|
+
Storefront::Engine.routes.url_helpers.cancel_afterpay_url(host: Workarea.config.host)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|