workarea-afterpay 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
  4. data/.github/ISSUE_TEMPLATE/documentation-request.md +17 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  6. data/.gitignore +22 -0
  7. data/CHANGELOG.md +161 -0
  8. data/CODE_OF_CONDUCT.md +3 -0
  9. data/CONTRIBUTING.md +3 -0
  10. data/Gemfile +17 -0
  11. data/LICENSE +52 -0
  12. data/README.md +131 -0
  13. data/Rakefile +60 -0
  14. data/app/assets/fonts/workarea/storefront/raleway/raleway_800.woff +0 -0
  15. data/app/assets/fonts/workarea/storefront/raleway/raleway_800.woff2 +0 -0
  16. data/app/assets/fonts/workarea/storefront/raleway/raleway_regular.woff +0 -0
  17. data/app/assets/fonts/workarea/storefront/raleway/raleway_regular.woff2 +0 -0
  18. data/app/assets/images/workarea/storefront/afterpay/afterpay_logo.png +0 -0
  19. data/app/assets/images/workarea/storefront/afterpay/aus/dialog_header_image.png +0 -0
  20. data/app/assets/images/workarea/storefront/afterpay/aus/step_1.png +0 -0
  21. data/app/assets/images/workarea/storefront/afterpay/aus/step_2.png +0 -0
  22. data/app/assets/images/workarea/storefront/afterpay/aus/step_3.png +0 -0
  23. data/app/assets/images/workarea/storefront/afterpay/us/complete_checkout.svg +1 -0
  24. data/app/assets/images/workarea/storefront/afterpay/us/four_equal_payments.svg +1 -0
  25. data/app/assets/images/workarea/storefront/afterpay/us/select_installments.svg +1 -0
  26. data/app/assets/javascripts/workarea/storefront/afterpay/config.js.erb +17 -0
  27. data/app/assets/javascripts/workarea/storefront/afterpay/modules/afterpay_redirect.js +30 -0
  28. data/app/assets/stylesheets/workarea/storefront/components/_afterpay_dialog.scss +158 -0
  29. data/app/controllers/workarea/admin/afterpay/configurations_controller.rb +33 -0
  30. data/app/controllers/workarea/storefront/afterpay_controller.rb +124 -0
  31. data/app/controllers/workarea/storefront/afterpay_dialog_controller.rb +7 -0
  32. data/app/controllers/workarea/storefront/checkout/place_order_controller.decorator +11 -0
  33. data/app/helpers/workarea/storefront/afterpay_helper.rb +16 -0
  34. data/app/models/workarea/afterpay/configuration.rb +16 -0
  35. data/app/models/workarea/order.decorator +17 -0
  36. data/app/models/workarea/payment/afterpay_payment_gateway.rb +16 -0
  37. data/app/models/workarea/payment/authorize/afterpay.rb +7 -0
  38. data/app/models/workarea/payment/capture/afterpay.rb +35 -0
  39. data/app/models/workarea/payment/purchase/afterpay.rb +7 -0
  40. data/app/models/workarea/payment/refund/afterpay.rb +52 -0
  41. data/app/models/workarea/payment/tender/afterpay.rb +18 -0
  42. data/app/models/workarea/payment.decorator +42 -0
  43. data/app/services/workarea/afterpay/order_builder.rb +107 -0
  44. data/app/view_models/workarea/admin/afterpay_configuration_view_model.rb +36 -0
  45. data/app/view_models/workarea/storefront/afterpay_configuration.rb +25 -0
  46. data/app/view_models/workarea/storefront/afterpay_view_model.rb +89 -0
  47. data/app/view_models/workarea/storefront/cart_view_model.decorator +12 -0
  48. data/app/view_models/workarea/storefront/checkout/payment_view_model.decorator +32 -0
  49. data/app/view_models/workarea/storefront/product_view_model.decorator +50 -0
  50. data/app/views/workarea/admin/afterpay/configurations/edit.html.haml +59 -0
  51. data/app/views/workarea/admin/orders/tenders/_afterpay.html.haml +2 -0
  52. data/app/views/workarea/admin/shared/_afterpay_configuration_link.html.haml +1 -0
  53. data/app/views/workarea/storefront/afterpay_dialog/show.html.haml +40 -0
  54. data/app/views/workarea/storefront/afterpay_dialog/show_aus.html.haml +40 -0
  55. data/app/views/workarea/storefront/carts/_afterpay.html.haml +9 -0
  56. data/app/views/workarea/storefront/checkouts/_afterpay_payment.html.haml +17 -0
  57. data/app/views/workarea/storefront/order_mailer/tenders/_afterpay.html.haml +4 -0
  58. data/app/views/workarea/storefront/order_mailer/tenders/_afterpay.text.erb +1 -0
  59. data/app/views/workarea/storefront/orders/tenders/_afterpay.html.haml +5 -0
  60. data/app/views/workarea/storefront/products/_afterpay_pricing.html.haml +3 -0
  61. data/app/views/workarea/storefront/style_guides/components/_afterpay_dialog.html.haml +92 -0
  62. data/bin/rails +19 -0
  63. data/bin/test +5 -0
  64. data/config/initializers/appends.rb +34 -0
  65. data/config/initializers/workarea.rb +17 -0
  66. data/config/locales/en.yml +107 -0
  67. data/config/routes.rb +15 -0
  68. data/four_equal_paymnets.svg +1 -0
  69. data/lib/workarea/afterpay/bogus_gateway.rb +211 -0
  70. data/lib/workarea/afterpay/engine.rb +14 -0
  71. data/lib/workarea/afterpay/gateway.rb +121 -0
  72. data/lib/workarea/afterpay/response.rb +17 -0
  73. data/lib/workarea/afterpay/version.rb +5 -0
  74. data/lib/workarea/afterpay.rb +59 -0
  75. data/test/dummy/Rakefile +6 -0
  76. data/test/dummy/bin/bundle +3 -0
  77. data/test/dummy/bin/rails +4 -0
  78. data/test/dummy/bin/rake +4 -0
  79. data/test/dummy/bin/setup +30 -0
  80. data/test/dummy/bin/update +26 -0
  81. data/test/dummy/bin/yarn +11 -0
  82. data/test/dummy/config/application.rb +30 -0
  83. data/test/dummy/config/boot.rb +5 -0
  84. data/test/dummy/config/cable.yml +10 -0
  85. data/test/dummy/config/environment.rb +5 -0
  86. data/test/dummy/config/environments/development.rb +51 -0
  87. data/test/dummy/config/environments/production.rb +88 -0
  88. data/test/dummy/config/environments/test.rb +44 -0
  89. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  90. data/test/dummy/config/initializers/assets.rb +14 -0
  91. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  92. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  93. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  94. data/test/dummy/config/initializers/inflections.rb +16 -0
  95. data/test/dummy/config/initializers/mime_types.rb +4 -0
  96. data/test/dummy/config/initializers/new_framework_defaults.rb +18 -0
  97. data/test/dummy/config/initializers/session_store.rb +3 -0
  98. data/test/dummy/config/initializers/workarea.rb +5 -0
  99. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  100. data/test/dummy/config/locales/en.yml +33 -0
  101. data/test/dummy/config/puma.rb +56 -0
  102. data/test/dummy/config/routes.rb +5 -0
  103. data/test/dummy/config/secrets.yml +32 -0
  104. data/test/dummy/config/spring.rb +6 -0
  105. data/test/dummy/config.ru +5 -0
  106. data/test/dummy/db/seeds.rb +2 -0
  107. data/test/dummy/log/.keep +0 -0
  108. data/test/integration/workarea/storefront/afterpay_integration_test.rb +242 -0
  109. data/test/models/workarea/afterpay_order_test.rb +10 -0
  110. data/test/models/workarea/payment/afterpay_payment_integration_test.rb +67 -0
  111. data/test/services/workarea/afterpay/order_builder_test.rb +88 -0
  112. data/test/system/workarea/storefront/afterpay_product_test.rb +51 -0
  113. data/test/system/workarea/storefront/cart_system_test.decorator +83 -0
  114. data/test/system/workarea/storefront/logged_in_checkout_system_test.decorator +10 -0
  115. data/test/teaspoon_env.rb +6 -0
  116. data/test/test_helper.rb +10 -0
  117. data/test/view_models/workarea/storefront/afterpay_view_model_test.rb +100 -0
  118. data/test/view_models/workarea/storefront/product_view_model_afterpay_test.rb +78 -0
  119. data/workarea-afterpay.gemspec +23 -0
  120. metadata +189 -0
@@ -0,0 +1,107 @@
1
+ ---
2
+ en:
3
+ workarea:
4
+ admin:
5
+ orders:
6
+ tenders:
7
+ afterpay:
8
+ title: Afterpay
9
+ shared:
10
+ primary_nav:
11
+ afterpay_configuration_title: Afterpay Configuration
12
+ afterpay_configuration:
13
+ edit:
14
+ flash_messages:
15
+ updated: Afterpay configuration has been updated
16
+ save_error: There was a problem updating the configuration
17
+ title: Afterpay Configuration
18
+ button: Save Afterpay Configuration
19
+ enabled: Show Afterpay?
20
+ show_on_pdp: Show on PDP?
21
+ show_on_cart: Show on Cart?
22
+ help:
23
+ enabled: Show Afterpay on the site for users who are checking out in an accepted currency. Setting this to false will turn off all Afterpay display points. Make sure that credentials for the relevant API endpoints are configured.
24
+ show_on_pdp: Show Afterpay on the product detail page.
25
+ show_on_cart: Show Afterpay on the cart page.
26
+ box_title: Setting up Afterpay Configuration
27
+ box_help_text: Use the toggle buttons to control where Afterpay is displayed.
28
+ us_limit: USD order limits
29
+ au_limit: AUD order limits
30
+ min_amount: Min Amount
31
+ max_amount: Max Amount
32
+ storefront:
33
+ orders:
34
+ tenders:
35
+ afterpay: Afterpay
36
+ carts:
37
+ afterpay: "Pay %{installment_count} interest free payments of %{installment_price} with"
38
+ checkouts:
39
+ afterpay: "%{installment_count} interest free payments of %{installment_price} every 2 weeks"
40
+ products:
41
+ afterpay: "Or %{installment_count} payments of %{installment_price} with"
42
+ afterpay:
43
+ address_error: We can not ship to this address. Please enter a new address.
44
+ cancel_message: Your Afterpay order has been cancelled. Please select another payment method.
45
+ check_out: Check Out with afterpay
46
+ checkout_submit_text: Continue to Afterpay
47
+ ineligible_order_total: "Available for orders totals between %{min} and %{max}"
48
+ on_continue: When you continue, you will be taken to the Afterpay website to complete your order.
49
+ paid_with_afterpay: Paid with Afterpay
50
+ payment_error: There was a problem processing your Afterpay payment, you have not been charged'. Please try again or select
51
+ another payment method.
52
+ afterpay: Afterpay
53
+ learn_more: Learn More
54
+ afterpay:
55
+ capture: "%{amount} has been captured"
56
+ refund: "%{amount} has been refunded"
57
+ capture_failure: "Afterpay capture has failed"
58
+ refund_failure: "Afterpay refund has failed"
59
+ tender_description: "Afterpay: %{installment_count} installments of %{installment_price}"
60
+ dialog:
61
+ aus:
62
+ copyright: © 2017 Afterpay
63
+ description: In four simple payments available instantly at checkout
64
+ enjoy_now: Enjoy Now.
65
+ footer_heading: 'All you need is:'
66
+ installments_by: Installments by
67
+ pay_later: Pay over time.
68
+ shop_now: Shop Now.
69
+ steps:
70
+ first:
71
+ heading: 1. Select Afterpay as your payment method
72
+ body: Use your existing payment card
73
+ second:
74
+ heading: 2. Complete your checkout in seconds
75
+ body: No long forms, instant approval online.
76
+ third:
77
+ heading: 3. Pay over 4 equal payments
78
+ body: Pay fortnightly, enjoy your purchase straight away!
79
+ requirements:
80
+ first: 1) A payment card
81
+ second: 2) To be over 18 years of age
82
+ third: 3) Resident of country offering Afterpay
83
+ terms: To see Afterpay's complete terms, visit
84
+ us:
85
+ copyright: © 2018 Afterpay
86
+ description: Pay in 4 equal installments every 2 weeks available at checkout
87
+ enjoy_now: Enjoy Now.
88
+ footer_heading: 'All you need is:'
89
+ installments_by: Installments by
90
+ pay_later: Pay Later
91
+ shop_now: Shop Now.
92
+ steps:
93
+ first:
94
+ heading: Select Installments by Afterpay as your payment method
95
+ body: Use your existing payment card
96
+ second:
97
+ heading: Complete your checkout in seconds
98
+ body: No long forms, approval online.
99
+ third:
100
+ heading: Pay over 4 equal payments
101
+ body: Pay every 2 weeks, enjoy your purchase right away!
102
+ requirements:
103
+ first: Eligable US-issued credit or debit card
104
+ second: To be over 18 and the legal age in your state of residence
105
+ third: A valid and verifiable email address and US mobile number
106
+ fourth: To be a resident of the US
107
+ terms: Late fees may apply. For complete terms visit
data/config/routes.rb ADDED
@@ -0,0 +1,15 @@
1
+ Workarea::Storefront::Engine.routes.draw do
2
+ get 'afterpay/start' => 'afterpay#start', as: :start_afterpay
3
+ get 'afterpay/complete/' => 'afterpay#complete', as: :complete_afterpay
4
+ get 'afterpay/cancel/' => 'afterpay#cancel', as: :cancel_afterpay
5
+ get 'afterpay_dialog' => 'afterpay_dialog#show', as: :afterpay_dialog
6
+ end
7
+
8
+
9
+ Workarea::Admin::Engine.routes.draw do
10
+ scope '(:locale)', constraints: Workarea::I18n.routes_constraint do
11
+ namespace :afterpay do
12
+ resource :configuration, only: [:edit, :update]
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100.14 91.54"><defs><style>.cls-1,.cls-2,.cls-6{fill:none;}.cls-1{clip-rule:evenodd;}.cls-3{clip-path:url(#clip-path);}.cls-4{clip-path:url(#clip-path-2);}.cls-5{fill:#8a959e;}.cls-6{stroke:#8a959e;stroke-miterlimit:10;stroke-width:2.8px;}.cls-7{clip-path:url(#clip-path-3);}</style><clipPath id="clip-path" transform="translate(-1732.02 -936)"><path class="cls-1" d="M1802.08,1000.58v-8.86h9.05a18.89,18.89,0,0,1,8-1.79,18.59,18.59,0,0,1,3,.26V956a4.22,4.22,0,0,0-4.21-4.21h-4.39v-3a2.79,2.79,0,0,0-2.78-2.78h-2.87a2.79,2.79,0,0,0-2.78,2.78v3h-46.08v-3a2.79,2.79,0,0,0-2.78-2.78h-2.87a2.79,2.79,0,0,0-2.78,2.78v3h-4.39A4.22,4.22,0,0,0,1742,956v57.37a4.22,4.22,0,0,0,4.21,4.22h55.94a18.94,18.94,0,0,1-.09-17Zm0-11.55h17.39V978.81h-17.39Zm0-12.91h17.39V965.9h-17.39Zm5.74-27.34a.09.09,0,0,1,.09-.09h2.87a.09.09,0,0,1,.08.09v8.61a.08.08,0,0,1-.08.08h-2.87a.08.08,0,0,1-.09-.08Zm-54.51,0a.09.09,0,0,1,.09-.09h2.87a.09.09,0,0,1,.09.09v8.61a.08.08,0,0,1-.09.08h-2.87a.08.08,0,0,1-.09-.08Zm-8.6,7.17a1.52,1.52,0,0,1,1.52-1.52h4.39v3a2.78,2.78,0,0,0,2.78,2.77h2.87a2.78,2.78,0,0,0,2.78-2.77v-3h46.08v3a2.78,2.78,0,0,0,2.78,2.77h2.87a2.78,2.78,0,0,0,2.78-2.77v-3H1818a1.52,1.52,0,0,1,1.52,1.52v7.26h-74.76Zm17.39,58.9h-15.87a1.53,1.53,0,0,1-1.52-1.53v-8.69h17.39Zm-17.39-12.91h17.39V991.72h-17.39Zm0-12.91h17.39V978.81h-17.39Zm0-12.91h17.39V965.9h-17.39Zm36,25.82h-11.56a1.35,1.35,0,0,0,0,2.69h11.56v10.22h-16V991.72h16Zm-16-12.91h16V978.81h-16Zm0-12.91h16V965.9h-16Zm18.65,38.73h16v-10.22h-16Zm0-12.91h16V991.72h-16Zm16-12.91h-16V978.81H1795a1.35,1.35,0,0,0,0-2.69h-11.56V965.9h16Z"/></clipPath><clipPath id="clip-path-2" transform="translate(-1732.02 -936)"><rect class="cls-2" width="2880" height="2316"/></clipPath><clipPath id="clip-path-3" transform="translate(-1732.02 -936)"><path class="cls-1" d="M1814.35,1009.31H1812l-.8,3.72h-2.69l.81-3.72H1802l.43-2.32,9.43-11.18h3l-2.36,11.18h2.36Zm-4.55-2.32.79-3.63c.24-1.06.64-2.54,1.18-4.42h-.1a16.51,16.51,0,0,1-1.58,2.18l-5,5.87Z"/></clipPath></defs><title>four_equal_paymnets</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g class="cls-3"><g class="cls-4"><rect class="cls-5" width="100.14" height="91.54"/></g></g><path class="cls-6" d="M1813.3,984.19a19.34,19.34,0,0,0-3.16-.27,19.88,19.88,0,0,0-8.45,1.88,20.16,20.16,0,0,0-9.53,9.33,20,20,0,1,0,21.14-10.94Z" transform="translate(-1732.02 -936)"/><g class="cls-7"><g class="cls-4"><rect class="cls-5" x="59.99" y="49.81" width="32.85" height="37.21"/></g></g></g></g></svg>
@@ -0,0 +1,211 @@
1
+ module Workarea
2
+ module Afterpay
3
+ class BogusGateway
4
+
5
+ def initialize(*)
6
+ end
7
+
8
+ def get_configuration
9
+ b = [
10
+ {
11
+ "type": "PAY_BY_INSTALLMENT",
12
+ "description": "Pay over time",
13
+ "minimumAmount": {
14
+ "amount": "5.00",
15
+ "currency": "USD"
16
+ },
17
+ "maximumAmount": {
18
+ "amount": "500.00",
19
+ "currency": "USD"
20
+ }
21
+ }
22
+ ]
23
+ Response.new(response(b))
24
+ end
25
+
26
+ def get_order(token, options = {})
27
+ b = {
28
+ "billing": {
29
+ "countryCode": "US",
30
+ "line1": "29 fort mott road",
31
+ "name": "Jeffrey Yucis",
32
+ "phoneNumber": "3027507743",
33
+ "postcode": "08070",
34
+ "state": "NJ",
35
+ "suburb": "pennsville"
36
+ },
37
+ "consumer": {
38
+ "email": "jyucis@gmail.com",
39
+ "givenNames": "Jeffrey",
40
+ "phoneNumber": "3027507743",
41
+ "surname": "Yucis"
42
+ },
43
+ "courier": {},
44
+ "discounts": [],
45
+ "expires": "2019-04-02T16:52:43.920Z",
46
+ "items": [
47
+ {
48
+ "name": "Small Wool Coat",
49
+ "price": {
50
+ "amount": "89.30",
51
+ "currency": "USD"
52
+ },
53
+ "quantity": 1,
54
+ "sku": "139364945-9"
55
+ }
56
+ ],
57
+ "merchant": {
58
+ "redirectCancelUrl": "http://localhost:3000/afterpay/cancel",
59
+ "redirectConfirmUrl": "http://localhost:3000/afterpay/complete"
60
+ },
61
+ "merchantReference": "63837A272F",
62
+ "paymentType": "PAY_BY_INSTALLMENT",
63
+ "shipping": {
64
+ "countryCode": "US",
65
+ "line1": "29 fort mott road",
66
+ "name": "Jeffrey Yucis",
67
+ "phoneNumber": "3027507743",
68
+ "postcode": "08070",
69
+ "state": "NJ",
70
+ "suburb": "pennsville"
71
+ },
72
+ "shippingAmount": {
73
+ "amount": "6.00",
74
+ "currency": "USD"
75
+ },
76
+ "taxAmount": {
77
+ "amount": "0.00",
78
+ "currency": "USD"
79
+ },
80
+ "token": "9tlqhfgebl6mu2g9t98rre2ia25ri2hvadc4aaimvpca1p9fma5j",
81
+ "totalAmount": {
82
+ "amount": "95.30",
83
+ "currency": "USD"
84
+ }
85
+ }
86
+ Response.new(response(b))
87
+ end
88
+
89
+ def create_order(order)
90
+ b = {
91
+ "token": "q54l9qd907m6iqqqlcrm5tpbjjsnfo47vsm59gqrfnd2rqefk9hu",
92
+ "expires": "2016-05-10T13:14:01Z"
93
+ }
94
+
95
+ Response.new(response(b))
96
+ end
97
+
98
+ def capture(token, order_id = "")
99
+ if token == "error_token"
100
+ Response.new(response(capture_error_response_body, 402))
101
+ else
102
+ Response.new(response(capture_response_body, 200))
103
+ end
104
+ end
105
+
106
+ def refund(afterpay_order_id, amount, request_id)
107
+ b = {
108
+ "requestId": "61cdad2d-8e10-42ec-a97b-8712dd7a8ca9",
109
+ "amount": {
110
+ "amount": "10.00",
111
+ "currency": "USD"
112
+ },
113
+ "merchantReference": "RF127261AD22",
114
+ "refundId": "56785678",
115
+ "refundedAt": "2015-07-10T15:01:14.123Z"
116
+ }
117
+
118
+ Response.new(response(b))
119
+ end
120
+
121
+ private
122
+
123
+ def response(body, status = 200)
124
+ response = Faraday.new do |builder|
125
+ builder.adapter :test do |stub|
126
+ stub.get("/v1/bogus") { |env| [ status, {}, body.to_json ] }
127
+ end
128
+ end
129
+ response.get("/v1/bogus")
130
+ end
131
+
132
+ def capture_error_response_body
133
+ {
134
+ "errorCode": "declined",
135
+ "errorId": "c4d64cbc3e61a26f",
136
+ "message": "Payment declined",
137
+ "httpStatusCode": 402
138
+ }
139
+ end
140
+
141
+ def capture_response_body
142
+ {
143
+ "id": "12345678",
144
+ "token": "q54l9qd907m6iqqqlcrm5tpbjjsnfo47vsm59gqrfnd2rqefk9hu",
145
+ "status": "APPROVED",
146
+ "created": "2015-07-14T10:08:14.123Z",
147
+ "totalAmount": {
148
+ "amount": "21.85",
149
+ "currency": "USD"
150
+ },
151
+ "merchantReference": "merchantOrder-1234",
152
+ "refunds": [],
153
+ "orderDetails": {
154
+ "consumer": {
155
+ "phoneNumber": "0200000000",
156
+ "givenNames": "Joe",
157
+ "surname": "Consumer",
158
+ "email": "test@afterpay.com"
159
+ },
160
+ "billing": {
161
+ "name": "Joe Consumer",
162
+ "line1": "75 Queen St",
163
+ "state": "New York",
164
+ "postcode": "10001",
165
+ "countryCode": "US",
166
+ "phoneNumber": "2120000000"
167
+ },
168
+ "shipping": {
169
+ "name": "Joe Consumer",
170
+ "line1": "75 Queen St",
171
+ "state": "New York",
172
+ "postcode": "10001",
173
+ "countryCode": "US",
174
+ "phoneNumber": "2120000000"
175
+ },
176
+ "courier": {},
177
+ "items": [
178
+ {
179
+ "name": "widget",
180
+ "sku": "12341234",
181
+ "quantity": 1,
182
+ "price": {
183
+ "amount": "10.00",
184
+ "currency": "USD"
185
+ }
186
+ }
187
+ ],
188
+ "discounts": [
189
+ {
190
+ "displayName": "10% Off Subtotal",
191
+ "amount": {
192
+ "amount": "1.00",
193
+ "currency": "USD"
194
+ }
195
+ }
196
+ ],
197
+ "shippingAmount": {
198
+ "amount": "10.00",
199
+ "currency": "USD"
200
+ },
201
+ "taxAmount": {
202
+ "amount": "2.85",
203
+ "currency": "USD"
204
+ }
205
+ },
206
+ "events": []
207
+ }
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,14 @@
1
+ require 'workarea/afterpay'
2
+
3
+ module Workarea
4
+ module Afterpay
5
+ class Engine < ::Rails::Engine
6
+ include Workarea::Plugin
7
+ isolate_namespace Workarea::Afterpay
8
+
9
+ config.to_prepare do
10
+ Storefront::ApplicationController.helper(Storefront::AfterpayHelper)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,121 @@
1
+ module Workarea
2
+ module Afterpay
3
+ class Gateway
4
+ attr_reader :merchant_id, :secret_key, :options
5
+ class_attribute :test_rest_endpoint, :production_rest_endpoint
6
+
7
+
8
+ def initialize(merchant_id, secret_key, options = {})
9
+ @merchant_id = merchant_id
10
+ @secret_key = secret_key
11
+ @options = options
12
+ end
13
+
14
+ def get_configuration
15
+ response = connection.get do |req|
16
+ req.url "/v1/configuration"
17
+ end
18
+ Afterpay::Response.new(response)
19
+ end
20
+
21
+ def get_order(token)
22
+ response = connection.get do |req|
23
+ req.url "/v1/orders/#{token}"
24
+ end
25
+
26
+ Afterpay::Response.new(response)
27
+ end
28
+
29
+ def create_order(order)
30
+ response = connection.post do |req|
31
+ req.url "/v1/orders"
32
+ req.body = order.to_json
33
+ end
34
+
35
+ Afterpay::Response.new(response)
36
+ end
37
+
38
+ def capture(token, order_id)
39
+ body = {
40
+ token: token
41
+ }
42
+ response = connection.post do |req|
43
+ req.url "/v1/payments/capture"
44
+ req.body = body.to_json
45
+ end
46
+
47
+ Afterpay::Response.new(response)
48
+ end
49
+
50
+ def refund(afterpay_order_id, amount, request_id)
51
+ body = {
52
+ requestId: request_id,
53
+ amount: {
54
+ amount: amount.to_f,
55
+ currency: amount.currency.iso_code
56
+ }
57
+ }
58
+
59
+ response = connection.post do |req|
60
+ req.url "/v1/payments/#{afterpay_order_id}/refund"
61
+ req.body = body.to_json
62
+ end
63
+ Afterpay::Response.new(response)
64
+ end
65
+
66
+ private
67
+
68
+ def connection
69
+ headers = {
70
+ "Content-Type" => "application/json",
71
+ "Accept" => "application/json",
72
+ "Authorization" => "Basic #{auth_string}",
73
+ "User-Agent" => user_agent
74
+ }
75
+
76
+ request_timeouts = {
77
+ timeout: Workarea.config.afterpay[:api_timeout],
78
+ open_timeout: Workarea.config.afterpay[:open_timeout]
79
+ }
80
+
81
+ Faraday.new(url: rest_endpoint, headers: headers, request: request_timeouts)
82
+ end
83
+
84
+ def auth_string
85
+ @auth_string = Base64.strict_encode64("#{merchant_id}:#{secret_key}")
86
+ end
87
+
88
+ def test?
89
+ (options.has_key?(:test) ? options[:test] : true)
90
+ end
91
+
92
+ def rest_endpoint
93
+ endpoint_hash = location == :us ? us_endpoints : au_endpoints
94
+
95
+ test? ? endpoint_hash[:test_rest_endpoint] : endpoint_hash[:production_rest_endpoint]
96
+ end
97
+
98
+ def au_endpoints
99
+ {
100
+ test_rest_endpoint: 'https://api-sandbox.afterpay.com',
101
+ production_rest_endpoint: 'https://api.afterpay.com'
102
+ }
103
+ end
104
+
105
+ def us_endpoints
106
+ {
107
+ test_rest_endpoint: 'https://api.us-sandbox.afterpay.com',
108
+ production_rest_endpoint: 'https://api.us.afterpay.com'
109
+ }
110
+ end
111
+
112
+ def location
113
+ options[:location]
114
+ end
115
+
116
+ def user_agent
117
+ "Afterpay Modile/1.0.0 (WORKAREA/#{Workarea::VERSION::STRING}; Merchant/#{merchant_id}) https://#{Workarea.config.host}"
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,17 @@
1
+ module Workarea
2
+ module Afterpay
3
+ class Response
4
+ def initialize(response)
5
+ @response = response
6
+ end
7
+
8
+ def success?
9
+ @response.present? && (@response.status == 201 || @response.status == 200)
10
+ end
11
+
12
+ def body
13
+ @body ||= JSON.parse(@response.body)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Workarea
2
+ module Afterpay
3
+ VERSION = '2.0.2'
4
+ end
5
+ end
@@ -0,0 +1,59 @@
1
+ require 'workarea'
2
+ require 'workarea/storefront'
3
+ require 'workarea/admin'
4
+
5
+ require 'workarea/afterpay/engine'
6
+ require 'workarea/afterpay/version'
7
+
8
+ require 'workarea/afterpay/bogus_gateway'
9
+ require 'workarea/afterpay/gateway'
10
+ require 'workarea/afterpay/response'
11
+
12
+
13
+ require "faraday"
14
+
15
+ module Workarea
16
+ module Afterpay
17
+ def self.credentials
18
+ (Rails.application.secrets.afterpay || {}).deep_symbolize_keys
19
+ end
20
+
21
+ def self.config
22
+ Workarea.config.afterpay
23
+ end
24
+
25
+ def self.merchant_id(location)
26
+ return unless credentials.present?
27
+
28
+ country = location.to_sym.downcase
29
+
30
+ return unless credentials[country].present?
31
+
32
+ credentials[country][:merchant_id]
33
+ end
34
+
35
+ def self.secret_key(location)
36
+ return unless credentials.present?
37
+
38
+ country = location.to_sym.downcase
39
+ credentials[country][:secret_key]
40
+ end
41
+
42
+ def self.test?
43
+ config[:test]
44
+ end
45
+
46
+ # Conditionally use the real gateway when secrets are present.
47
+ # Otherwise, use the bogus gateway.
48
+ #
49
+ # @return [Afterpay::Gateway]
50
+ def self.gateway(location, options = {})
51
+ if credentials.present?
52
+ options.merge!(location: location, test: test?)
53
+ Afterpay::Gateway.new(merchant_id(location), secret_key(location), options)
54
+ else
55
+ Afterpay::BogusGateway.new
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative 'config/application'
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
+ load Gem.bin_path('bundler', 'bundle')
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../config/application', __dir__)
3
+ require_relative '../config/boot'
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/boot'
3
+ require 'rake'
4
+ Rake.application.run
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a starting point to setup your application.
15
+ # Add necessary setup steps to this file.
16
+
17
+ puts '== Installing dependencies =='
18
+ system! 'gem install bundler --conservative'
19
+ system('bundle check') || system!('bundle install')
20
+
21
+ # Install JavaScript dependencies if using Yarn
22
+ # system('bin/yarn')
23
+
24
+
25
+ puts "\n== Removing old logs and tempfiles =="
26
+ system! 'bin/rails log:clear tmp:clear'
27
+
28
+ puts "\n== Restarting application server =="
29
+ system! 'bin/rails restart'
30
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+ require 'pathname'
3
+ require 'fileutils'
4
+ include FileUtils
5
+
6
+ # path to your application root.
7
+ APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
8
+
9
+ def system!(*args)
10
+ system(*args) || abort("\n== Command #{args} failed ==")
11
+ end
12
+
13
+ chdir APP_ROOT do
14
+ # This script is a way to update your development environment automatically.
15
+ # Add necessary update steps to this file.
16
+
17
+ puts '== Installing dependencies =='
18
+ system! 'gem install bundler --conservative'
19
+ system('bundle check') || system!('bundle install')
20
+
21
+ puts "\n== Removing old logs and tempfiles =="
22
+ system! 'bin/rails log:clear tmp:clear'
23
+
24
+ puts "\n== Restarting application server =="
25
+ system! 'bin/rails restart'
26
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ VENDOR_PATH = File.expand_path('..', __dir__)
3
+ Dir.chdir(VENDOR_PATH) do
4
+ begin
5
+ exec "yarnpkg #{ARGV.join(" ")}"
6
+ rescue Errno::ENOENT
7
+ $stderr.puts "Yarn executable was not detected in the system."
8
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9
+ exit 1
10
+ end
11
+ end