afterpay-sdk 2.0.0
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.
- checksums.yaml +7 -0
- data/.env.sample +2 -0
- data/.github/workflows/gempush.yml +30 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +33 -0
- data/.travis.yml +10 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +89 -0
- data/LICENSE.txt +21 -0
- data/README.md +284 -0
- data/Rakefile +6 -0
- data/afterpay-sdk.gemspec +45 -0
- data/bin/bundle +105 -0
- data/bin/console +20 -0
- data/bin/setup +8 -0
- data/configure.rb +19 -0
- data/lib/afterpay-sdk.rb +6 -0
- data/lib/afterpay.rb +48 -0
- data/lib/afterpay/address.rb +49 -0
- data/lib/afterpay/client.rb +72 -0
- data/lib/afterpay/config.rb +23 -0
- data/lib/afterpay/consumer.rb +35 -0
- data/lib/afterpay/discount.rb +28 -0
- data/lib/afterpay/error.rb +16 -0
- data/lib/afterpay/item.rb +54 -0
- data/lib/afterpay/order.rb +117 -0
- data/lib/afterpay/payment.rb +164 -0
- data/lib/afterpay/payment_event.rb +16 -0
- data/lib/afterpay/refund.rb +31 -0
- data/lib/afterpay/shipping_courier.rb +14 -0
- data/lib/afterpay/utils/money.rb +26 -0
- data/lib/afterpay/version.rb +5 -0
- metadata +215 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
class Discount
|
5
|
+
attr_accessor :name, :amount
|
6
|
+
|
7
|
+
def initialize(name:, amount:)
|
8
|
+
@name = name
|
9
|
+
@amount = amount
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
{
|
14
|
+
displayName: name,
|
15
|
+
amount: Utils::Money.api_hash(amount)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.from_response(response)
|
20
|
+
return nil if response.nil?
|
21
|
+
|
22
|
+
new(
|
23
|
+
name: response[:display_name],
|
24
|
+
amount: Utils::Money.from_response(response[:amount])
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
# Error class with accessor to methods
|
5
|
+
# Afterpay error returns the same format containing
|
6
|
+
# `errorId`, `errorCode`, `message`
|
7
|
+
class Error
|
8
|
+
attr_accessor :code, :id, :message
|
9
|
+
|
10
|
+
def initialize(response)
|
11
|
+
@id = response[:errorId]
|
12
|
+
@code = response[:errorCode]
|
13
|
+
@message = response[:message]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "money"
|
4
|
+
|
5
|
+
module Afterpay
|
6
|
+
class Item
|
7
|
+
attr_accessor :name, :sku, :quantity, :page_url, :image_url, :price, :categories, :estimated_shipment_date
|
8
|
+
|
9
|
+
# rubocop:disable Metrics/ParameterLists
|
10
|
+
def initialize(name:, price:, page_url:, image_url:, categories:, estimated_shipment_date:, sku: nil, quantity: 1)
|
11
|
+
@name = name
|
12
|
+
@sku = sku
|
13
|
+
@quantity = quantity
|
14
|
+
@price = price
|
15
|
+
@page_url = page_url
|
16
|
+
@image_url = image_url
|
17
|
+
@categories = categories
|
18
|
+
@estimated_shipment_date = estimated_shipment_date
|
19
|
+
end
|
20
|
+
# rubocop:enable Metrics/ParameterLists
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
{
|
24
|
+
name: name,
|
25
|
+
sku: sku,
|
26
|
+
quantity: quantity,
|
27
|
+
price: {
|
28
|
+
amount: price.amount.to_f,
|
29
|
+
currency: price.currency.iso_code
|
30
|
+
},
|
31
|
+
page_url: page_url,
|
32
|
+
image_url: image_url,
|
33
|
+
categories: categories,
|
34
|
+
estimated_shipment_date: estimated_shipment_date
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Builds Item from response
|
39
|
+
def self.from_response(response)
|
40
|
+
return nil if response.nil?
|
41
|
+
|
42
|
+
new(
|
43
|
+
name: response[:name],
|
44
|
+
sku: response[:sku],
|
45
|
+
quantity: response[:quantity],
|
46
|
+
price: Utils::Money.from_response(response[:price]),
|
47
|
+
page_url: response[:pageUrl],
|
48
|
+
image_url: response[:imageUrl],
|
49
|
+
categories: response[:categories],
|
50
|
+
estimated_shipment_date: response[:estimatedShipmentDate]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
# The Order object for creating an order to `/v2/checkouts`
|
5
|
+
class Order
|
6
|
+
attr_accessor :total, :consumer, :items, :shipping, :tax, :discounts,
|
7
|
+
:billing, :shipping_address, :billing_address, :reference,
|
8
|
+
:payment_type, :success_url, :cancel_url, :redirect_checkout_url
|
9
|
+
|
10
|
+
attr_reader :expiry, :token, :error
|
11
|
+
|
12
|
+
# Finds Order from Afterpay API
|
13
|
+
# @param token [String]
|
14
|
+
# @return [Order]
|
15
|
+
def self.find(token)
|
16
|
+
request = Afterpay.client.get("/v2/checkouts/#{token}")
|
17
|
+
|
18
|
+
Order.from_response(request.body)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Builds Order from response
|
22
|
+
# @param response [Hash] response params from API
|
23
|
+
# @return [Order]
|
24
|
+
def self.from_response(response)
|
25
|
+
return nil if response.nil?
|
26
|
+
|
27
|
+
new(
|
28
|
+
total: Utils::Money.from_response(response[:total]),
|
29
|
+
consumer: Consumer.from_response(response[:consumer]),
|
30
|
+
items: response[:items].map { |item| Item.from_response(item) },
|
31
|
+
billing_address: Address.from_response(response[:billing]),
|
32
|
+
shipping_address: Address.from_response(response[:shipping]),
|
33
|
+
discounts: response[:discounts].map { |discount| Discount.from_response(discount) },
|
34
|
+
tax: Utils::Money.from_response(response[:taxAmount]),
|
35
|
+
shipping: Utils::Money.from_response(response[:shippingAmount])
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Helper function to create an Order and calls #create
|
40
|
+
#
|
41
|
+
# @param (see #initialize)
|
42
|
+
# @return [Order::Response] containing token, error, valid?
|
43
|
+
def self.create(*args)
|
44
|
+
new(*args).create
|
45
|
+
end
|
46
|
+
|
47
|
+
# Initializes an Order object
|
48
|
+
#
|
49
|
+
# @overload initialize(total:, items:, consumer:, success_url:, cancel_url:, payment_type:)
|
50
|
+
# @param total [Money] a Money object
|
51
|
+
# @param items [Array<Afterpay::Item>] receives items for order
|
52
|
+
# @param consumer [Afterpay::Consume] the consumer for the order
|
53
|
+
# @param success_url [String] the path to redirect on successful payment
|
54
|
+
# @param cancel_url [String] the path to redirect on failed payment
|
55
|
+
# @param payment_type [String] Payment type defaults to {Config#type}
|
56
|
+
# @param shipping [Money] optional the billing Address
|
57
|
+
# @param discounts [Array<Afterpay::Discount>] optional discounts
|
58
|
+
# @param billing_address [<Afterpay::Address>] optional the billing Address
|
59
|
+
# @param shipping_address [<Afterpay::Address>] optional the shipping Address
|
60
|
+
def initialize(attributes = {})
|
61
|
+
attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
|
62
|
+
@apayment_type ||= Afterpay.config.type
|
63
|
+
@token ||= nil
|
64
|
+
@expiry ||= nil
|
65
|
+
@error = nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
69
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
70
|
+
|
71
|
+
# Builds structure to API specs
|
72
|
+
def to_hash
|
73
|
+
data = {
|
74
|
+
amount: Utils::Money.api_hash(total),
|
75
|
+
consumer: consumer.to_hash,
|
76
|
+
merchant: {
|
77
|
+
redirectConfirmUrl: success_url,
|
78
|
+
redirectCancelUrl: cancel_url
|
79
|
+
},
|
80
|
+
merchantReference: reference,
|
81
|
+
taxAmount: tax,
|
82
|
+
paymentType: payment_type
|
83
|
+
}
|
84
|
+
data[items] = items.map(&:to_hash) if items
|
85
|
+
data[:taxAmount] = Utils::Money.api_hash(tax) if tax
|
86
|
+
data[:shippingAmount] = Utils::Money.api_hash(shipping) if shipping
|
87
|
+
data[:discounts] = discounts.map(&:to_hash) if discounts
|
88
|
+
data[:billing] = billing_address.to_hash if billing_address
|
89
|
+
data[:shipping] = shipping_address.to_hash if shipping_address
|
90
|
+
data
|
91
|
+
end
|
92
|
+
|
93
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
94
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
95
|
+
|
96
|
+
# Sends the create request to Afterpay server
|
97
|
+
# @return [Response]
|
98
|
+
def create
|
99
|
+
request = Afterpay.client.post("/v2/checkouts") do |req|
|
100
|
+
req.body = to_hash
|
101
|
+
end
|
102
|
+
response = request.body
|
103
|
+
if request.success?
|
104
|
+
@expiry = response[:expires]
|
105
|
+
@token = response[:token]
|
106
|
+
@redirect_checkout_url = response[:redirectCheckoutUrl]
|
107
|
+
else
|
108
|
+
@error = Error.new(response)
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def success?
|
114
|
+
!@token.nil?
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Metrics/ClassLength
|
4
|
+
|
5
|
+
module Afterpay
|
6
|
+
# They Payment object
|
7
|
+
class Payment
|
8
|
+
attr_accessor :id, :token, :status, :created, :original_amount, :open_to_capture_amount,
|
9
|
+
:payment_state, :merchant_reference, :refunds, :order, :events, :error
|
10
|
+
|
11
|
+
# Initialize Payment from response
|
12
|
+
def initialize(attributes)
|
13
|
+
@id = attributes[:id].to_i
|
14
|
+
@token = attributes[:token]
|
15
|
+
@status = attributes[:status]
|
16
|
+
@created = attributes[:created]
|
17
|
+
@original_amount = Utils::Money.from_response(attributes[:originalAmount])
|
18
|
+
@open_to_capture_amount = Utils::Money.from_response(attributes[:openToCaptureAmount])
|
19
|
+
@payment_state = attributes[:paymentState]
|
20
|
+
@merchant_reference = attributes[:merchantReference]
|
21
|
+
@refunds = attributes[:refunds]
|
22
|
+
@order = Order.from_response(attributes[:orderDetails])
|
23
|
+
@events = attributes[:events]
|
24
|
+
@error = Error.new(attributes) if attributes[:errorId]
|
25
|
+
end
|
26
|
+
|
27
|
+
def success?
|
28
|
+
@status == "APPROVED"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Executes the Payment
|
32
|
+
#
|
33
|
+
# @param token [String] the Order token
|
34
|
+
# @param reference [String] the reference for payment
|
35
|
+
# @return [Payment] the Payment object
|
36
|
+
def self.execute(token:, reference:)
|
37
|
+
request = Afterpay.client.post("/v2/payments/capture") do |req|
|
38
|
+
req.body = {
|
39
|
+
token: token,
|
40
|
+
merchantRefernce: reference
|
41
|
+
}
|
42
|
+
end
|
43
|
+
new(request.body)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.execute_auth(request_id:, token:, merchant_reference:)
|
47
|
+
request = Afterpay.client.post("/v2/payments/auth") do |req|
|
48
|
+
req.body = {
|
49
|
+
requestId: request_id,
|
50
|
+
token: token,
|
51
|
+
merchantReference: merchant_reference
|
52
|
+
}
|
53
|
+
end
|
54
|
+
new(request.body)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.execute_deferred_payment(request_id:, reference:, amount:,
|
58
|
+
payment_event_merchant_reference:, order_id:)
|
59
|
+
request = Afterpay.client.post("/v2/payments/#{order_id}/capture") do |req|
|
60
|
+
req.body = {
|
61
|
+
requestId: request_id,
|
62
|
+
merchantRefernce: reference,
|
63
|
+
amount: Utils::Money.api_hash(amount),
|
64
|
+
paymentEventMerchantReference: payment_event_merchant_reference
|
65
|
+
}
|
66
|
+
end
|
67
|
+
new(request.body)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.execute_void(request_id:, order_id:, amount:)
|
71
|
+
request = Afterpay.client.post("/v2/payments/#{order_id}/void") do |req|
|
72
|
+
req.body = {
|
73
|
+
requestId: request_id,
|
74
|
+
amount: Utils::Money.api_hash(amount)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
new(request.body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.update_shipping_courier(order_id:, shipped_at:, name:, tracking:, priority:)
|
81
|
+
request = Afterpay.client.put("/v2/payments/#{order_id}/courier") do |req|
|
82
|
+
req.body = {
|
83
|
+
shippedAt: shipped_at,
|
84
|
+
name: name,
|
85
|
+
tracking: tracking,
|
86
|
+
priority: priority
|
87
|
+
}
|
88
|
+
end
|
89
|
+
new(request.body)
|
90
|
+
end
|
91
|
+
|
92
|
+
# This endpoint retrieves an individual payment along with its order details.
|
93
|
+
def self.get_payment_by_order_id(order_id:)
|
94
|
+
request = Afterpay.client.get("/v2/payments/#{order_id}")
|
95
|
+
new(request.body)
|
96
|
+
end
|
97
|
+
|
98
|
+
# This endpoint retrieves an individual payment along with its order details.
|
99
|
+
def self.get_payment_by_token(token:)
|
100
|
+
request = Afterpay.client.get("/v2/payments/token:#{token}")
|
101
|
+
new(request.body)
|
102
|
+
end
|
103
|
+
|
104
|
+
# This end point is for merchants that creates merchant side's order id after
|
105
|
+
# AfterPay order id creation. The merchants should call immediately after the
|
106
|
+
# AfterPay order is created in order to properly update with their order id
|
107
|
+
# that can be tracked.
|
108
|
+
def self.update_payment_by_order_id(order_id:, merchant_reference:)
|
109
|
+
request = Afterpay.client.put("/v2/payments/#{order_id}") do |req|
|
110
|
+
req.body = {
|
111
|
+
# The merchant's new order id to replace with
|
112
|
+
merchantReference: merchant_reference
|
113
|
+
}
|
114
|
+
end
|
115
|
+
request.body
|
116
|
+
end
|
117
|
+
|
118
|
+
# This endpoint performs a reversal of the checkout that is used to initiate
|
119
|
+
# the Afterpay payment process. This will cancel the order asynchronously as
|
120
|
+
# soon as it is created without the need of an additional call to the void endpoint.
|
121
|
+
# In order for a payment to be eligible, the order must be in an Auth-Approved or
|
122
|
+
# Captured state and must be issued within 10 minutes of the order being created.
|
123
|
+
# token paramater is the token of the checkout to be reversed (voided).
|
124
|
+
def self.reverse_payment_by_token(token:)
|
125
|
+
request = Afterpay.client.post("/v2/payments/token:#{token}/reversal") do |req|
|
126
|
+
req.body = {}
|
127
|
+
end
|
128
|
+
request.status
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.create_url(url, type, values)
|
132
|
+
if values.size.positive?
|
133
|
+
values.each do |value|
|
134
|
+
url += "&#{type}=#{value}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
url
|
138
|
+
end
|
139
|
+
|
140
|
+
# rubocop:disable Metrics/ParameterLists
|
141
|
+
|
142
|
+
# This endpoint retrieves a collection of payments along with their order details.
|
143
|
+
def self.list_payments(to_created_date:, from_created_date:, limit:, offset:, tokens:, ids:,
|
144
|
+
merchant_ref:, statuses:, order_by:, asc:)
|
145
|
+
url = "/v2/payments?"
|
146
|
+
url += "toCreatedDate=#{to_created_date.gsub('+', '%2b')}" if to_created_date
|
147
|
+
url += "&fromCreatedDate=#{from_created_date.gsub('+', '%2b')}" if from_created_date
|
148
|
+
url += "&limit=#{limit}" if limit
|
149
|
+
url += "&offset=#{offset}" if offset
|
150
|
+
url += "&orderBy=#{order_by}" if order_by
|
151
|
+
url += "&ascending=#{asc}" if asc
|
152
|
+
url += create_url(url, "ids", ids)
|
153
|
+
url += create_url(url, "tokens", tokens)
|
154
|
+
url += create_url(url, "merchantReferences", merchant_ref)
|
155
|
+
url += create_url(url, "statuses", statuses)
|
156
|
+
request = Afterpay.client.get(url)
|
157
|
+
request.body
|
158
|
+
end
|
159
|
+
|
160
|
+
# rubocop:enable Metrics/ParameterLists
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
class PaymentEvent
|
5
|
+
attr_accessor :id, :created, :expires, :type, :amount, :payment_event_merchant_reference
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
@id = attributes[:id]
|
9
|
+
@created = attributes[:created]
|
10
|
+
@expires = attributes[:expires]
|
11
|
+
@type = attributes[:expires]
|
12
|
+
@amount = Utils::Money.from_response(attributes[:amount])
|
13
|
+
@payment_event_merchant_reference = attributes[:paymentEventMerchantReference]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
class Refund
|
5
|
+
attr_accessor :request_id, :amount, :merchant_reference, :refund_id, :refunded_at,
|
6
|
+
:refund_merchant_reference, :error
|
7
|
+
|
8
|
+
def initialize(attributes)
|
9
|
+
@request_id = attributes[:requestId]
|
10
|
+
@amount = attributes[:amount]
|
11
|
+
@merchant_reference = attributes[:merchantReference]
|
12
|
+
@refund_id = attributes[:refundId]
|
13
|
+
@refunded_at = attributes[:refundAt]
|
14
|
+
@refund_merchant_reference = attributes[:refundMerchantReference]
|
15
|
+
@error = Error.new(attributes) if attributes[:errorId]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.execute(request_id:, order_id:, amount:, merchant_reference:,
|
19
|
+
refund_merchant_reference:)
|
20
|
+
request = Afterpay.client.post("/v2/payments/#{order_id}/refund") do |req|
|
21
|
+
req.body = {
|
22
|
+
requestId: request_id,
|
23
|
+
amount: Utils::Money.api_hash(amount),
|
24
|
+
merchantReference: merchant_reference,
|
25
|
+
refundMerchantReference: refund_merchant_reference
|
26
|
+
}
|
27
|
+
end
|
28
|
+
new(request.body)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterpay
|
4
|
+
class ShippingCourier
|
5
|
+
attr_accessor :shipped_at, :name, :tracking, :priority
|
6
|
+
|
7
|
+
def initialize(shipped_at:, name:, tracking:, priority:)
|
8
|
+
@shipped_at = shipped_at
|
9
|
+
@name = name
|
10
|
+
@tracking = tracking
|
11
|
+
@priority = priority
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|