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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/README.md +232 -107
  4. data/lib/tinker/api/base_manager.rb +73 -10
  5. data/lib/tinker/api/payout_manager.rb +31 -0
  6. data/lib/tinker/api/subscription_manager.rb +122 -0
  7. data/lib/tinker/api/transaction_manager.rb +33 -0
  8. data/lib/tinker/auth/authentication_manager.rb +32 -7
  9. data/lib/tinker/config/configuration.rb +21 -3
  10. data/lib/tinker/config/endpoints.rb +10 -5
  11. data/lib/tinker/enum/gateway.rb +1 -0
  12. data/lib/tinker/enum/payment_status.rb +11 -0
  13. data/lib/tinker/exception/api_exception.rb +18 -2
  14. data/lib/tinker/exception/feature_not_supported_exception.rb +28 -0
  15. data/lib/tinker/http/http_client.rb +24 -4
  16. data/lib/tinker/model/api_meta.rb +23 -0
  17. data/lib/tinker/model/cancellation_result.rb +14 -0
  18. data/lib/tinker/model/checkout_cancellation.rb +8 -0
  19. data/lib/tinker/model/dto/callback_data_dto.rb +4 -8
  20. data/lib/tinker/model/dto/cancellation_request.rb +17 -0
  21. data/lib/tinker/model/dto/create_subscription_plan_request_dto.rb +32 -0
  22. data/lib/tinker/model/dto/create_subscription_request_dto.rb +30 -0
  23. data/lib/tinker/model/dto/initiate_payment_request_dto.rb +10 -2
  24. data/lib/tinker/model/dto/initiation_data_dto.rb +10 -11
  25. data/lib/tinker/model/dto/managed_checkout.rb +25 -0
  26. data/lib/tinker/model/dto/payout_request.rb +18 -0
  27. data/lib/tinker/model/dto/query_data_dto.rb +4 -8
  28. data/lib/tinker/model/dto/query_payment_request_dto.rb +1 -1
  29. data/lib/tinker/model/dto/refund_request.rb +17 -0
  30. data/lib/tinker/model/dto/subscription_customer_dto.rb +28 -0
  31. data/lib/tinker/model/lifecycle_resource.rb +28 -0
  32. data/lib/tinker/model/payout.rb +8 -0
  33. data/lib/tinker/model/payout_result.rb +14 -0
  34. data/lib/tinker/model/refund.rb +8 -0
  35. data/lib/tinker/model/refund_result.rb +15 -0
  36. data/lib/tinker/model/transaction.rb +2 -7
  37. data/lib/tinker/payments.rb +21 -4
  38. data/lib/tinker/version.rb +1 -1
  39. data/lib/tinker/webhook/dto/invoice_event_data_dto.rb +5 -4
  40. data/lib/tinker/webhook/dto/onboarding_event_data_dto.rb +19 -0
  41. data/lib/tinker/webhook/dto/payment_event_data_dto.rb +4 -8
  42. data/lib/tinker/webhook/dto/settlement_event_data_dto.rb +8 -5
  43. data/lib/tinker/webhook/dto/subscription_event_data_dto.rb +12 -6
  44. data/lib/tinker/webhook/webhook_event.rb +44 -3
  45. data/lib/tinker/webhook/webhook_handler.rb +42 -0
  46. data/lib/tinker.rb +19 -0
  47. 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(url, headers: headers, body: body)
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
- message = result['message'] || 'Authentication failed'
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 result['token']
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 = result['token']
54
- expires_in = result['expires_in'] || 3600
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
- @base_url = "#{Endpoints::API_BASE_URL}/"
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
- BASE_URL = 'https://payments.tinker.co.ke'
7
- API_BASE_URL = "#{BASE_URL}/api".freeze
8
- AUTH_TOKEN_URL = "#{BASE_URL}/auth/token".freeze
9
- PAYMENT_INITIATE_PATH = '/payment/initiate'
10
- PAYMENT_QUERY_PATH = '/payment/query'
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
@@ -6,6 +6,7 @@ module Tinker
6
6
  MPESA = 'mpesa'
7
7
  PAYSTACK = 'paystack'
8
8
  STRIPE = 'stripe'
9
+ PESAPAL = 'pesapal'
9
10
  end
10
11
  end
11
12
  end
@@ -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
- request = Net::HTTP::Post.new(uri.path)
22
- headers.each { |key, value| request[key] = value }
23
- request.body = body if body
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(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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ class CheckoutCancellation < LifecycleResource
6
+ end
7
+ end
8
+ 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
- status_value = data['status'] || 'pending'
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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tinker
4
+ module Model
5
+ module Dto
6
+ class CancellationRequest
7
+ def initialize(reason:)
8
+ @payload = { reason: reason }
9
+ end
10
+
11
+ def to_hash
12
+ @payload.dup
13
+ end
14
+ end
15
+ end
16
+ end
17
+ 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
- status_value = data['status'] || 'pending'
12
- @status = begin
13
- Tinker::Enum::PaymentStatus.const_get(status_value.upcase)
14
- rescue NameError
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
- payment_reference: @payment_reference,
19
+ paymentReference: @payment_reference,
23
20
  status: @status,
24
- authorization_url: @authorization_url
21
+ authorizationUrl: @authorization_url,
22
+ clientSecret: @client_secret,
23
+ publishableKey: @publishable_key
25
24
  }
26
25
  end
27
26
  end