tinker-pay-ruby-sdk 0.1.0 → 0.6.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 +4 -4
- data/.rubocop.yml +0 -1
- data/README.md +232 -107
- data/lib/tinker/api/base_manager.rb +73 -10
- data/lib/tinker/api/payout_manager.rb +31 -0
- data/lib/tinker/api/subscription_manager.rb +122 -0
- data/lib/tinker/api/transaction_manager.rb +33 -0
- data/lib/tinker/auth/authentication_manager.rb +32 -7
- data/lib/tinker/config/configuration.rb +21 -3
- data/lib/tinker/config/endpoints.rb +10 -5
- data/lib/tinker/enum/gateway.rb +1 -0
- data/lib/tinker/enum/payment_status.rb +11 -0
- data/lib/tinker/exception/api_exception.rb +18 -2
- data/lib/tinker/exception/feature_not_supported_exception.rb +28 -0
- data/lib/tinker/http/http_client.rb +24 -4
- data/lib/tinker/model/api_meta.rb +23 -0
- data/lib/tinker/model/cancellation_result.rb +14 -0
- data/lib/tinker/model/checkout_cancellation.rb +8 -0
- data/lib/tinker/model/dto/callback_data_dto.rb +4 -8
- data/lib/tinker/model/dto/cancellation_request.rb +17 -0
- data/lib/tinker/model/dto/create_subscription_plan_request_dto.rb +32 -0
- data/lib/tinker/model/dto/create_subscription_request_dto.rb +30 -0
- data/lib/tinker/model/dto/initiate_payment_request_dto.rb +10 -2
- data/lib/tinker/model/dto/initiation_data_dto.rb +10 -11
- data/lib/tinker/model/dto/managed_checkout.rb +25 -0
- data/lib/tinker/model/dto/payout_request.rb +18 -0
- data/lib/tinker/model/dto/query_data_dto.rb +4 -8
- data/lib/tinker/model/dto/query_payment_request_dto.rb +1 -1
- data/lib/tinker/model/dto/refund_request.rb +17 -0
- data/lib/tinker/model/dto/subscription_customer_dto.rb +28 -0
- data/lib/tinker/model/lifecycle_resource.rb +28 -0
- data/lib/tinker/model/payout.rb +8 -0
- data/lib/tinker/model/payout_result.rb +14 -0
- data/lib/tinker/model/refund.rb +8 -0
- data/lib/tinker/model/refund_result.rb +15 -0
- data/lib/tinker/model/transaction.rb +2 -7
- data/lib/tinker/payments.rb +21 -4
- data/lib/tinker/version.rb +1 -1
- data/lib/tinker/webhook/dto/invoice_event_data_dto.rb +5 -4
- data/lib/tinker/webhook/dto/onboarding_event_data_dto.rb +19 -0
- data/lib/tinker/webhook/dto/payment_event_data_dto.rb +4 -8
- data/lib/tinker/webhook/dto/settlement_event_data_dto.rb +8 -5
- data/lib/tinker/webhook/dto/subscription_event_data_dto.rb +12 -6
- data/lib/tinker/webhook/webhook_event.rb +44 -3
- data/lib/tinker/webhook/webhook_handler.rb +42 -0
- data/lib/tinker.rb +19 -0
- metadata +27 -8
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'cgi'
|
|
4
|
+
|
|
5
|
+
module Tinker
|
|
6
|
+
module Api
|
|
7
|
+
class SubscriptionManager < BaseManager
|
|
8
|
+
def create_plan(request)
|
|
9
|
+
payload = request.to_hash
|
|
10
|
+
request('POST', Config::Endpoints::SUBSCRIPTION_PLANS_PATH, payload)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def list_plans
|
|
14
|
+
wrapped = request('GET', Config::Endpoints::SUBSCRIPTION_PLANS_PATH, nil)
|
|
15
|
+
wrapped['value'].is_a?(Array) ? wrapped['value'] : []
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def update_plan(plan_id, request)
|
|
19
|
+
request('PUT', plan_path(plan_id), request.to_hash)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete_plan(plan_id)
|
|
23
|
+
request('DELETE', plan_path(plan_id), nil)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def list_invoices(subscription_id: nil, page: nil, page_size: nil)
|
|
27
|
+
query = URI.encode_www_form({ subscription_id: subscription_id, page: page, page_size: page_size }.compact)
|
|
28
|
+
path = Config::Endpoints::SUBSCRIPTION_INVOICES_PATH
|
|
29
|
+
request('GET', query.empty? ? path : "#{path}?#{query}", nil)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def create(request)
|
|
33
|
+
payload = request.to_hash
|
|
34
|
+
request('POST', Config::Endpoints::SUBSCRIPTION_BASE_PATH, payload)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def list(plan_id: nil, external_customer_id: nil)
|
|
38
|
+
endpoint = Config::Endpoints::SUBSCRIPTION_BASE_PATH
|
|
39
|
+
params = []
|
|
40
|
+
params << "plan_id=#{CGI.escape(plan_id)}" if plan_id && !plan_id.strip.empty?
|
|
41
|
+
if external_customer_id && !external_customer_id.strip.empty?
|
|
42
|
+
params << "external_customer_id=#{CGI.escape(external_customer_id)}"
|
|
43
|
+
end
|
|
44
|
+
endpoint += "?#{params.join('&')}" unless params.empty?
|
|
45
|
+
|
|
46
|
+
wrapped = request('GET', endpoint, nil)
|
|
47
|
+
wrapped['value'].is_a?(Array) ? wrapped['value'] : []
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def cancel(subscription_id)
|
|
51
|
+
request('POST', "#{Config::Endpoints::SUBSCRIPTION_BASE_PATH}/#{subscription_id}/cancel", nil)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def checkout(request)
|
|
55
|
+
request('POST', 'merchant/billing/checkout', request.to_hash)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def expire_checkout(id, key)
|
|
59
|
+
request('POST', "merchant/billing/checkout/#{escape_path(id)}/expire", idempotency_key: key)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def billing_resource(id)
|
|
63
|
+
request('GET', billing_resource_path(id))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def change_quantity(id, quantity, key)
|
|
67
|
+
request('POST', "#{billing_resource_path(id)}/quantity", quantity: quantity, idempotency_key: key)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def schedule_quantity(id, quantity, key)
|
|
71
|
+
request('POST', "#{billing_resource_path(id)}/schedule", quantity: quantity, idempotency_key: key)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def clear_scheduled_quantity(id, key)
|
|
75
|
+
request('POST', "#{billing_resource_path(id)}/unschedule", idempotency_key: key)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def set_cancel_at_renewal(id, cancel, key)
|
|
79
|
+
request('POST', "#{billing_resource_path(id)}/cancel", cancel: cancel, idempotency_key: key)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def set_trial_end(id, end_time, key)
|
|
83
|
+
request('POST', "#{billing_resource_path(id)}/trial-end", trial_end: end_time.to_i, idempotency_key: key)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def end_trial(id, key)
|
|
87
|
+
request('POST', "#{billing_resource_path(id)}/end-trial", idempotency_key: key)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def billing_operation(key)
|
|
91
|
+
request('GET', "merchant/billing/operations/#{escape_path(key)}")
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def payment_method_portal(id, return_url, key)
|
|
95
|
+
request('POST', "#{billing_resource_path(id)}/payment-method", return_url: return_url, idempotency_key: key)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def billing_payment_history(id, cursor = '', snapshot = 0)
|
|
99
|
+
params = {}
|
|
100
|
+
params['starting_after'] = cursor unless cursor.empty?
|
|
101
|
+
params['snapshot'] = snapshot if snapshot.positive?
|
|
102
|
+
endpoint = "#{billing_resource_path(id)}/payment-history"
|
|
103
|
+
endpoint += "?#{URI.encode_www_form(params)}" unless params.empty?
|
|
104
|
+
request('GET', endpoint)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def plan_path(id)
|
|
110
|
+
"#{Config::Endpoints::SUBSCRIPTION_PLANS_PATH}/#{escape_path(id)}"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def billing_resource_path(id)
|
|
114
|
+
"merchant/billing/resources/#{escape_path(id)}"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def escape_path(value)
|
|
118
|
+
URI.encode_www_form_component(value).gsub('+', '%20')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -16,6 +16,39 @@ module Tinker
|
|
|
16
16
|
|
|
17
17
|
Model::Transaction.new(response)
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
def capabilities(currency: nil, country: nil)
|
|
21
|
+
query = URI.encode_www_form({ currency: currency, country: country }.compact)
|
|
22
|
+
path = Config::Endpoints::PAYMENT_CAPABILITIES_PATH
|
|
23
|
+
request('GET', query.empty? ? path : "#{path}?#{query}")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def refund(payment_id, payload, key)
|
|
27
|
+
result = request('POST', "#{payment_path(payment_id)}/refund", payload.to_hash, operation_headers(key))
|
|
28
|
+
Model::RefundResult.new(result)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def refunds(payment_id)
|
|
32
|
+
request('GET', "#{payment_path(payment_id)}/refunds")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def get_refund(payment_id, refund_id)
|
|
36
|
+
Model::Refund.new(request('GET', "#{payment_path(payment_id)}/refunds/#{resource_id(refund_id)}"))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def cancel_checkout(payment_id, payload, key)
|
|
40
|
+
result = request('POST', "#{payment_path(payment_id)}/cancellations", payload.to_hash, operation_headers(key))
|
|
41
|
+
Model::CancellationResult.new(result)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cancellations(payment_id)
|
|
45
|
+
request('GET', "#{payment_path(payment_id)}/cancellations")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_cancellation(payment_id, request_id)
|
|
49
|
+
endpoint = "#{payment_path(payment_id)}/cancellations/#{resource_id(request_id)}"
|
|
50
|
+
Model::CheckoutCancellation.new(request('GET', endpoint))
|
|
51
|
+
end
|
|
19
52
|
end
|
|
20
53
|
end
|
|
21
54
|
end
|
|
@@ -7,11 +7,14 @@ require 'json'
|
|
|
7
7
|
module Tinker
|
|
8
8
|
module Auth
|
|
9
9
|
class AuthenticationManager
|
|
10
|
+
attr_reader :last_meta
|
|
11
|
+
|
|
10
12
|
def initialize(config:, http_client:)
|
|
11
13
|
@config = config
|
|
12
14
|
@http_client = http_client
|
|
13
15
|
@token = nil
|
|
14
16
|
@expires_at = nil
|
|
17
|
+
@last_meta = nil
|
|
15
18
|
end
|
|
16
19
|
|
|
17
20
|
def token
|
|
@@ -31,27 +34,26 @@ module Tinker
|
|
|
31
34
|
def fetch_token
|
|
32
35
|
credentials = Base64.strict_encode64("#{@config.api_public_key}:#{@config.api_secret_key}")
|
|
33
36
|
|
|
34
|
-
url = Config::Endpoints::AUTH_TOKEN_URL
|
|
35
37
|
headers = {
|
|
36
38
|
'Content-Type' => 'application/x-www-form-urlencoded',
|
|
37
39
|
'Accept' => 'application/json'
|
|
38
40
|
}
|
|
39
41
|
body = "credentials=#{URI.encode_www_form_component(credentials)}"
|
|
40
42
|
|
|
41
|
-
response = @http_client.post(
|
|
43
|
+
response = @http_client.post(@config.auth_url, headers: headers, body: body)
|
|
42
44
|
result = response.json
|
|
45
|
+
auth_data = extract_auth_data(result)
|
|
43
46
|
|
|
44
47
|
if response.status_code >= 400
|
|
45
|
-
|
|
46
|
-
raise Exception::AuthenticationException.new(message, Exception::ExceptionCode::AUTHENTICATION_ERROR)
|
|
48
|
+
raise Exception::AuthenticationException.new(extract_error_message(result), Exception::ExceptionCode::AUTHENTICATION_ERROR)
|
|
47
49
|
end
|
|
48
50
|
|
|
49
|
-
unless
|
|
51
|
+
unless auth_data['token']
|
|
50
52
|
raise Exception::NetworkException.new('Invalid authentication response: token missing', Exception::ExceptionCode::AUTHENTICATION_ERROR)
|
|
51
53
|
end
|
|
52
54
|
|
|
53
|
-
@token =
|
|
54
|
-
expires_in =
|
|
55
|
+
@token = auth_data['token']
|
|
56
|
+
expires_in = auth_data['expires_in'] || 3600
|
|
55
57
|
@expires_at = Time.now.to_i + expires_in
|
|
56
58
|
|
|
57
59
|
@token
|
|
@@ -61,6 +63,29 @@ module Tinker
|
|
|
61
63
|
raise Exception::NetworkException.new("Failed to authenticate: #{e.message}",
|
|
62
64
|
Exception::ExceptionCode::AUTHENTICATION_ERROR, e)
|
|
63
65
|
end
|
|
66
|
+
|
|
67
|
+
def extract_auth_data(result)
|
|
68
|
+
if result.is_a?(Hash) && result.key?('success')
|
|
69
|
+
@last_meta = Model::ApiMeta.new(result['meta'] || {})
|
|
70
|
+
if result['success'] == false
|
|
71
|
+
raise Exception::AuthenticationException.new(extract_error_message(result), Exception::ExceptionCode::AUTHENTICATION_ERROR)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return result['data'].is_a?(Hash) ? result['data'] : {}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
result.is_a?(Hash) ? result : {}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def extract_error_message(result)
|
|
81
|
+
if result.is_a?(Hash)
|
|
82
|
+
error = result['error']
|
|
83
|
+
return error['message'] || error['code'] || 'Authentication failed' if error.is_a?(Hash)
|
|
84
|
+
|
|
85
|
+
return result['message'] || 'Authentication failed'
|
|
86
|
+
end
|
|
87
|
+
'Authentication failed'
|
|
88
|
+
end
|
|
64
89
|
end
|
|
65
90
|
end
|
|
66
91
|
end
|
|
@@ -3,17 +3,35 @@
|
|
|
3
3
|
module Tinker
|
|
4
4
|
module Config
|
|
5
5
|
class Configuration
|
|
6
|
-
attr_reader :api_public_key, :api_secret_key, :base_url
|
|
6
|
+
attr_reader :api_public_key, :api_secret_key, :base_url, :auth_url
|
|
7
7
|
|
|
8
|
-
def initialize(api_public_key:, api_secret_key:)
|
|
8
|
+
def initialize(api_public_key:, api_secret_key:, base_url: nil)
|
|
9
9
|
@api_public_key = api_public_key
|
|
10
10
|
@api_secret_key = api_secret_key
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
resolved_base_url = base_url
|
|
13
|
+
if resolved_base_url.nil? || resolved_base_url.strip.empty?
|
|
14
|
+
resolved_base_url = sandbox_credentials? ? Endpoints::SANDBOX_BASE_URL : Endpoints::PRODUCTION_BASE_URL
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
resolved_base_url = resolved_base_url.chomp('/')
|
|
18
|
+
unless resolved_base_url.end_with?(Endpoints::API_VERSION_PATH)
|
|
19
|
+
resolved_base_url = "#{resolved_base_url}#{Endpoints::API_VERSION_PATH}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@base_url = "#{resolved_base_url}/"
|
|
23
|
+
@auth_url = "#{resolved_base_url}#{Endpoints::AUTH_TOKEN_PATH}"
|
|
12
24
|
end
|
|
13
25
|
|
|
14
26
|
def api_key
|
|
15
27
|
@api_secret_key
|
|
16
28
|
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def sandbox_credentials?
|
|
33
|
+
@api_public_key.start_with?('pk_test_') || @api_secret_key.start_with?('sk_test_')
|
|
34
|
+
end
|
|
17
35
|
end
|
|
18
36
|
end
|
|
19
37
|
end
|
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
module Tinker
|
|
4
4
|
module Config
|
|
5
5
|
class Endpoints
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
PRODUCTION_BASE_URL = 'https://api.tinkerpayments.com'
|
|
7
|
+
SANDBOX_BASE_URL = 'https://sandbox-api.tinkerpayments.com'
|
|
8
|
+
API_VERSION_PATH = '/v1'
|
|
9
|
+
AUTH_TOKEN_PATH = '/auth/token'
|
|
10
|
+
PAYMENT_INITIATE_PATH = '/merchant/payment/initiate'
|
|
11
|
+
PAYMENT_QUERY_PATH = '/merchant/payment/query'
|
|
12
|
+
PAYMENT_CAPABILITIES_PATH = '/merchant/payment/capabilities'
|
|
13
|
+
SUBSCRIPTION_BASE_PATH = '/merchant/subscriptions'
|
|
14
|
+
SUBSCRIPTION_PLANS_PATH = '/merchant/subscriptions/plans'
|
|
15
|
+
SUBSCRIPTION_INVOICES_PATH = '/merchant/subscriptions/invoices'
|
|
11
16
|
end
|
|
12
17
|
end
|
|
13
18
|
end
|
data/lib/tinker/enum/gateway.rb
CHANGED
|
@@ -4,9 +4,20 @@ module Tinker
|
|
|
4
4
|
module Enum
|
|
5
5
|
class PaymentStatus
|
|
6
6
|
PENDING = 'pending'
|
|
7
|
+
AUTHORISED = 'authorised'
|
|
7
8
|
SUCCESS = 'success'
|
|
8
9
|
CANCELLED = 'cancelled'
|
|
9
10
|
FAILED = 'failed'
|
|
11
|
+
BLOCKED = 'blocked'
|
|
12
|
+
REFUNDED = 'refunded'
|
|
13
|
+
PARTIALLY_REFUNDED = 'partially_refunded'
|
|
14
|
+
EXPIRED = 'expired'
|
|
15
|
+
REVERSED = 'reversed'
|
|
16
|
+
|
|
17
|
+
def self.parse(value)
|
|
18
|
+
value = (value || PENDING).to_s
|
|
19
|
+
value == 'completed' ? SUCCESS : value
|
|
20
|
+
end
|
|
10
21
|
end
|
|
11
22
|
end
|
|
12
23
|
end
|
|
@@ -3,12 +3,28 @@
|
|
|
3
3
|
module Tinker
|
|
4
4
|
module Exception
|
|
5
5
|
class ApiException < StandardError
|
|
6
|
-
def initialize(message, code = ExceptionCode::API_ERROR)
|
|
6
|
+
def initialize(message, code = ExceptionCode::API_ERROR, http_status: nil, details: nil, headers: {})
|
|
7
7
|
super(message)
|
|
8
8
|
@code = code
|
|
9
|
+
@http_status = http_status
|
|
10
|
+
@details = details
|
|
11
|
+
extract_metadata(details, headers)
|
|
9
12
|
end
|
|
10
13
|
|
|
11
|
-
attr_reader :code
|
|
14
|
+
attr_reader :code, :http_status, :error_code, :provider_code, :request_id, :retry_after, :outcome, :details
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def extract_metadata(details, headers)
|
|
19
|
+
error = details.is_a?(Hash) && details['error'].is_a?(Hash) ? details['error'] : {}
|
|
20
|
+
normalized_headers = headers.transform_keys { |key| key.to_s.downcase }
|
|
21
|
+
@error_code = error['code']
|
|
22
|
+
@provider_code = error['provider_code']
|
|
23
|
+
@request_id = error['request_id'] || Array(normalized_headers['x-request-id']).first
|
|
24
|
+
@retry_after = Array(normalized_headers['retry-after']).first
|
|
25
|
+
# Only an explicit not_applied confirms no mutation.
|
|
26
|
+
@outcome = error['outcome']
|
|
27
|
+
end
|
|
12
28
|
end
|
|
13
29
|
end
|
|
14
30
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tinker
|
|
4
|
+
module Exception
|
|
5
|
+
class FeatureNotSupportedException < ApiException
|
|
6
|
+
CODE = 'FEATURE_NOT_SUPPORTED'
|
|
7
|
+
|
|
8
|
+
def gateway
|
|
9
|
+
error_details['gateway']
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def feature
|
|
13
|
+
error_details['feature']
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def alternatives
|
|
17
|
+
Array(error_details['alternatives'])
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def error_details
|
|
23
|
+
error = @details.is_a?(Hash) ? @details['error'] : nil
|
|
24
|
+
error.is_a?(Hash) && error['details'].is_a?(Hash) ? error['details'] : {}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -11,18 +11,38 @@ module Tinker
|
|
|
11
11
|
@timeout = 30
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
def get(url, headers: {})
|
|
15
|
+
request(url, Net::HTTP::Get, headers: headers)
|
|
16
|
+
end
|
|
17
|
+
|
|
14
18
|
def post(url, headers: {}, body: nil)
|
|
19
|
+
request(url, Net::HTTP::Post, headers: headers, body: body)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def put(url, headers: {}, body: nil)
|
|
23
|
+
request(url, Net::HTTP::Put, headers: headers, body: body)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete(url, headers: {}, body: nil)
|
|
27
|
+
request(url, Net::HTTP::Delete, headers: headers, body: body)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def request(url, request_class, headers:, body: nil)
|
|
15
33
|
uri = URI(url)
|
|
16
34
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
17
35
|
http.use_ssl = uri.scheme == 'https'
|
|
18
36
|
http.read_timeout = @timeout
|
|
19
37
|
http.open_timeout = @timeout
|
|
20
38
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
39
|
+
path = uri.path
|
|
40
|
+
path += "?#{uri.query}" if uri.query
|
|
41
|
+
req = request_class.new(path)
|
|
42
|
+
headers.each { |key, value| req[key] = value }
|
|
43
|
+
req.body = body if body
|
|
24
44
|
|
|
25
|
-
response = http.request(
|
|
45
|
+
response = http.request(req)
|
|
26
46
|
Response.new(response.code.to_i, response.body, response.to_hash)
|
|
27
47
|
rescue StandardError => e
|
|
28
48
|
raise Exception::NetworkException.new("Network error: #{e.message}", Exception::ExceptionCode::NETWORK_ERROR, e)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tinker
|
|
4
|
+
module Model
|
|
5
|
+
class ApiMeta
|
|
6
|
+
attr_reader :request_id, :timestamp, :environment
|
|
7
|
+
|
|
8
|
+
def initialize(meta = {})
|
|
9
|
+
@request_id = meta['request_id']
|
|
10
|
+
@timestamp = meta['timestamp']
|
|
11
|
+
@environment = meta['environment']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_hash
|
|
15
|
+
{
|
|
16
|
+
request_id: @request_id,
|
|
17
|
+
timestamp: @timestamp,
|
|
18
|
+
environment: @environment
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tinker
|
|
4
|
+
module Model
|
|
5
|
+
class CancellationResult
|
|
6
|
+
attr_reader :cancellation, :replayed
|
|
7
|
+
|
|
8
|
+
def initialize(data)
|
|
9
|
+
@cancellation = CheckoutCancellation.new(data.fetch('cancellation'))
|
|
10
|
+
@replayed = data['replayed'] == true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -4,22 +4,18 @@ module Tinker
|
|
|
4
4
|
module Model
|
|
5
5
|
module Dto
|
|
6
6
|
class CallbackDataDto
|
|
7
|
-
attr_reader :id, :status, :reference, :amount, :currency, :paid_at, :created_at, :channel
|
|
7
|
+
attr_reader :id, :status, :reference, :amount, :currency, :paid_at, :created_at, :channel, :country
|
|
8
8
|
|
|
9
9
|
def initialize(data)
|
|
10
10
|
@id = data['id']
|
|
11
|
-
|
|
12
|
-
@status = begin
|
|
13
|
-
Tinker::Enum::PaymentStatus.const_get(status_value.upcase)
|
|
14
|
-
rescue NameError
|
|
15
|
-
Tinker::Enum::PaymentStatus::PENDING
|
|
16
|
-
end
|
|
11
|
+
@status = Tinker::Enum::PaymentStatus.parse(data['status'])
|
|
17
12
|
@reference = data['reference']
|
|
18
13
|
@amount = data['amount'].to_f
|
|
19
14
|
@currency = data['currency']
|
|
20
15
|
@paid_at = data['paid_at']
|
|
21
16
|
@created_at = data['created_at']
|
|
22
17
|
@channel = data['channel']
|
|
18
|
+
@country = data['country']
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
def to_hash
|
|
@@ -32,7 +28,7 @@ module Tinker
|
|
|
32
28
|
paid_at: @paid_at,
|
|
33
29
|
created_at: @created_at,
|
|
34
30
|
channel: @channel
|
|
35
|
-
}
|
|
31
|
+
}.merge(@country ? { country: @country } : {})
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
34
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tinker
|
|
4
|
+
module Model
|
|
5
|
+
module Dto
|
|
6
|
+
class CreateSubscriptionPlanRequestDto
|
|
7
|
+
def initialize(name:, amount:, currency:, intervals:, description: '', is_active: true, features_json: nil)
|
|
8
|
+
@name = name
|
|
9
|
+
@amount = amount
|
|
10
|
+
@currency = currency
|
|
11
|
+
@intervals = intervals
|
|
12
|
+
@description = description
|
|
13
|
+
@is_active = is_active
|
|
14
|
+
@features_json = features_json
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_hash
|
|
18
|
+
hash = {
|
|
19
|
+
name: @name,
|
|
20
|
+
description: @description,
|
|
21
|
+
amount: @amount,
|
|
22
|
+
currency: @currency,
|
|
23
|
+
intervals: @intervals,
|
|
24
|
+
is_active: @is_active
|
|
25
|
+
}
|
|
26
|
+
hash[:features_json] = @features_json unless @features_json.nil?
|
|
27
|
+
hash
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tinker
|
|
4
|
+
module Model
|
|
5
|
+
module Dto
|
|
6
|
+
class CreateSubscriptionRequestDto
|
|
7
|
+
def initialize(plan_id:, gateway:, customer:, payment_method_id: nil, billing_period: nil, country: nil)
|
|
8
|
+
@plan_id = plan_id
|
|
9
|
+
@gateway = gateway
|
|
10
|
+
@customer = customer
|
|
11
|
+
@payment_method_id = payment_method_id
|
|
12
|
+
@billing_period = billing_period
|
|
13
|
+
@country = country
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_hash
|
|
17
|
+
payload = {
|
|
18
|
+
plan_id: @plan_id,
|
|
19
|
+
gateway: @gateway,
|
|
20
|
+
customer: @customer.to_hash
|
|
21
|
+
}
|
|
22
|
+
payload[:payment_method_id] = @payment_method_id if @payment_method_id
|
|
23
|
+
payload[:billing_period] = @billing_period if @billing_period
|
|
24
|
+
payload[:country] = @country if @country
|
|
25
|
+
payload
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -5,10 +5,12 @@ module Tinker
|
|
|
5
5
|
module Dto
|
|
6
6
|
class InitiatePaymentRequestDto
|
|
7
7
|
attr_reader :amount, :currency, :gateway, :merchant_reference, :return_url,
|
|
8
|
-
:customer_phone, :customer_email, :transaction_desc, :metadata
|
|
8
|
+
:customer_phone, :customer_email, :transaction_desc, :metadata, :payment_method,
|
|
9
|
+
:customer_name, :country
|
|
9
10
|
|
|
10
11
|
def initialize(amount:, currency:, gateway:, merchant_reference:, return_url:,
|
|
11
|
-
customer_phone: nil, customer_email: nil, transaction_desc: nil, metadata: nil
|
|
12
|
+
customer_phone: nil, customer_email: nil, transaction_desc: nil, metadata: nil,
|
|
13
|
+
payment_method: nil, customer_name: nil, country: nil)
|
|
12
14
|
@amount = amount
|
|
13
15
|
@currency = currency
|
|
14
16
|
@gateway = gateway
|
|
@@ -18,6 +20,9 @@ module Tinker
|
|
|
18
20
|
@customer_email = customer_email
|
|
19
21
|
@transaction_desc = transaction_desc
|
|
20
22
|
@metadata = metadata
|
|
23
|
+
@payment_method = payment_method
|
|
24
|
+
@customer_name = customer_name
|
|
25
|
+
@country = country
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def to_hash
|
|
@@ -33,8 +38,11 @@ module Tinker
|
|
|
33
38
|
|
|
34
39
|
payload[:customerPhone] = @customer_phone if @customer_phone
|
|
35
40
|
payload[:customerEmail] = @customer_email if @customer_email
|
|
41
|
+
payload[:customerName] = @customer_name if @customer_name
|
|
42
|
+
payload[:country] = @country if @country
|
|
36
43
|
payload[:transactionDesc] = @transaction_desc if @transaction_desc
|
|
37
44
|
payload[:metadata] = @metadata if @metadata
|
|
45
|
+
payload[:paymentMethod] = @payment_method if @payment_method
|
|
38
46
|
|
|
39
47
|
payload
|
|
40
48
|
end
|
|
@@ -4,24 +4,23 @@ module Tinker
|
|
|
4
4
|
module Model
|
|
5
5
|
module Dto
|
|
6
6
|
class InitiationDataDto
|
|
7
|
-
attr_reader :payment_reference, :status, :authorization_url
|
|
7
|
+
attr_reader :payment_reference, :status, :authorization_url, :client_secret, :publishable_key
|
|
8
8
|
|
|
9
9
|
def initialize(data)
|
|
10
|
-
@payment_reference = data['payment_reference']
|
|
11
|
-
|
|
12
|
-
@
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Tinker::Enum::PaymentStatus::PENDING
|
|
16
|
-
end
|
|
17
|
-
@authorization_url = data['authorization_url']
|
|
10
|
+
@payment_reference = data['paymentReference'] || data['payment_reference']
|
|
11
|
+
@status = Tinker::Enum::PaymentStatus.parse(data['status'])
|
|
12
|
+
@authorization_url = data['authorizationUrl'] || data['authorization_url']
|
|
13
|
+
@client_secret = data['clientSecret'] || data['client_secret']
|
|
14
|
+
@publishable_key = data['publishableKey'] || data['publishable_key']
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
def to_hash
|
|
21
18
|
{
|
|
22
|
-
|
|
19
|
+
paymentReference: @payment_reference,
|
|
23
20
|
status: @status,
|
|
24
|
-
|
|
21
|
+
authorizationUrl: @authorization_url,
|
|
22
|
+
clientSecret: @client_secret,
|
|
23
|
+
publishableKey: @publishable_key
|
|
25
24
|
}
|
|
26
25
|
end
|
|
27
26
|
end
|