afterpay-sdk 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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